I am familiar with the term: Separation of Concerns. It's pretty much advocating modularity in your code.
However, when I was reading about AOP, it specifically says that it allows you separate cross-cutting concerns.
So my question is, if AOP separates cross-cutting concerns, what kind of concerns does OOP separate?
OOP separates concerns that in a real-world scenario would be coupled each other with direct associations.
For example, a company has many employees, and employees have a salary.
AOP is about concerns that aren't direct associations but more like sentinels that go beyond object to object relationships.
A typical sample scenario for AOP is logging. Logging is still represented by an object called Logger but actually the logging action is like an observer that filters out the normal flow and extracts information intercepting the flow behind the scenes.
While logging can be implemented without AOP, actually you end up dirtying your code with something that has nothing to do with the purpose of a given action (for example: register an user has nothing to do with logging).
Therefore, you should understand that AOP enforces and improves a good separation of concerns, even taking some requirements out of your eyes, keeping the code simpler.
The short answer: OOP separates abstraction levels. Well-designed OOP code will use a consistent set of abstractions throughout a module.
Long answer: OOP is built on four 'pillars':
Data abstraction
Encapsulation
Inheritance
Polymorphism
These allow a programmer to easily build layers of abstraction, each level using the lower abstractions to provide higher abstractions.
For example, you can build a TCP/IP stack from frames, messages, routing and sessions (Abstractions). When using a session, you do not need to know about how frames are resent, and collision detection (encapsulation). Also, you can send a message without knowing if you are using IP4 or IP6 (polymorphism). And all levels can use the same CRC checking through e.g. inheritance. So these 4 pillars together allow a way of programming where very clean abstraction levels can be created. But all too often, OOP software becomes a mess where the abstraction levels are not cleanly separated.
AOP is complementary to OOP. OOP is focussed on abstraction levels and structures, AOP is focussed on behavior related to 'concerns'. They are orthogonal ways of looking at code.
Related
I've read the other posts discussing abstraction and encapsulation, but I'm not confident I understand them; or maybe I understand them but feel unsatisfied with the clarity of their content. Here are my understandings of abstraction and encapsulation. In what regards are they accurate/inaccurate/complete/incomplete?
"Abstractions are data types created by programmers to extend a language when primitive data types are insufficient. Like primitive data types, abstractions have specifications which list the inputs they require and the outputs they return, but the specifications do not overwhelm programmers with the methods, functions, and variables used to operate on the inputs. A class is an example of an abstraction. An API is another example of an abstraction."
"Encapsulation is the state of having abstract data types — i.e. classes — isolated from each other so their methods, functions, and variables do not conflict with each other, and so programmers can easily reuse an existing class in other programs without being concerned that doing so would interfere with the rest of the program (presuming the programmer correctly provides the required inputs and correctly handles the data that get returns)."
I prefer Robert C. Martin's definition in APPP:
Abstraction is the elimination of the irrelevant and the amplification of the essential.
I'd say your understanding is correct ... so much, so, that I hesitate to comment more specifically.
However, if I were to comment, I might say that "Data types can be used to implement abstractions ...", rather than "Abstractions are data types ...", since abstractions can exist outside of software (it hurt me to say that :-).
But that's just nitpicking. I think you understand. I hope I do, after 36 years of coding ... mostly in languages that support reasonable levels of abstraction (PL/1, Pascal, C, C++, Java).
There are a lot of nice intelligent people in industry, though, who have no concept of abstraction in software, and consider it pretentiously high brow.
Personally, I think that good clear misnomer-free abstraction is a key technical ingredient of solid software engineering.
I've never come across that definition of encapsulation before. That definition sounds more like what namespaces are for. I've always read about encapsulation being purely about the ability to restrict access to certain components of your code, such as access modifiers in OOP languages. However, there seems to be a two definitions of encapsulation on wikipedia, which is news to me:
Encapsulation is the packing of data and functions into a single
component. The features of encapsulation are supported using classes
in most object-oriented programming languages, although other
alternatives also exist. It allows selective hiding of properties and
methods in an object by building an impenetrable wall to protect the
code from accidental corruption.
In programming languages, encapsulation is used to refer to one of two
related but distinct notions, and sometimes to the combination
thereof:
A language mechanism for restricting access to some of the object's
components.
A language construct that facilitates the bundling
of data with the methods (or other functions) operating on that
data.
Some programming language researchers and academics use
the first meaning alone or in combination with the second as a
distinguishing feature of object-oriented programming, while other
programming languages which provide lexical closures view
encapsulation as a feature of the language orthogonal to object
orientation.
The second definition is motivated by the fact that in many OOP
languages hiding of components is not automatic or can be overridden;
thus, information hiding is defined as a separate notion by those who
prefer the second definition.
source
So, I guess I've always defined encapsulation in terms of point #1, but it looks like some people define it as the ability to bundle methods and data together, and term #1 "information hiding".
Recently, i go back to read some parts of the "UML Reference Manual" book, second edition (obviously by: Booch, Rumbaugh, Jacobson).
(see: http://www.amazon.com/Unified-Modeling-Language-Reference-Manual/dp/020130998X)
Meanwhile, i have found these "strange" words in the first chapiter "UML overview" at "Complexity of UML" section:
There is far too much use of generalization at the expense of essential distinctions. The myth that inheritance is always good has been a curse of object orientation from earliest days.
I can't see how this sentence can be fully in line with Object Oriented Paradigm which states that inheritance is a fundamental principle.
Any idea/help please?
You seem to believe the two points are mutually exclusive. They are not. Inheritance is a fundamental and powerful principle of object-oriented programming, and it is overused.
It is overused typically by inexperienced developers who are so captivated with the idea of inheritance that they are more focused on the inheritance tree than solving the problem. They try to factor out as much code as possible to some parent base class so they can just reuse it throughout the tree, and as a result they have a brittle design.
One of the greatest evils of software engineering is tight coupling between classes. That's the sort of thing that causes you to have to work through the weekend after the customer asks for a simple change. Why? Because making a change in one class has an effect on another class, and fixing that class has an effect on another, and so on.
Well, there is no tighter coupling than inheritance.
When you factor too much out to the "top level," every derived class is coupled to it. And as you find more and more code you want to factor out to various levels, you eventually have these deep trees, and every change made at the top cascades throughout the tree. As a result, you start to have methods that return null or are empty. They're unnecessary for the class, but the inheritance contract demands they be there. This violates the Liskov Substitution Principle.
So use inheritance of course. But do it smartly. Favor delegation to inheritance if you have any doubt. And when you do use inheritance, make sure you aren't factoring commonalities to the top level (of the whole tree or a subtree) just to reuse common code, but rather do so because there is a commonality of behavior from top to bottom.
If your tree is more than two or three levels deep (and I think three is really pushing it), you are almost certainly setting yourself up for trouble.
Everything is good in moderation. Remember that the quote is not saying do not use it, or avoid, etc. Rather it is saying it is an overused principal when other OO abstractions or principals work better. Inheritance is powerful but it's coupling is tight.
Wisely or rather randomly the author of the UML book is saying pointing out this current truism that inheritance is often over-used and over-referenced. What about all the other principals and abstractions. I find that developers typically only hit the OO highlights (inheritance being one) and use that abstraction to excess.
For me in UML it is a good reminder that UML is OO generally, but it is not limited to Java or .Net OO features. Many languages only offer of the abstractions available across all languages. UML attempts to help you model and express many of them.
Remember the author only said 'too much use', not bad or incorrect. Also remember that maybe you are an expert developer who does not apply inheritance incorrectly.
The creator of the Clojure language claims that "open, and large, set of functions operate upon an open, and small, set of extensible abstractions is the key to algorithmic reuse and library interoperability". Obviously it contradicts the typical OOP approach where you create a lot of abstractions (classes) and a relatively small set of functions operating on them. Please suggest a book, a chapter in a book, an article, or your personal experience that elaborate on the topics:
motivating examples of problems that appear in OOP and how using "many functions upon few abstractions" would address them
how to effectively do MFUFA* design
how to refactor OOP code towards MFUFA
how OOP languages' syntax gets in the way of MFUFA
*MFUFA: "many functions upon few abstractions"
There are two main notions of "abstraction" in programming:
parameterisation ("polymorphism", genericity).
encapsulation (data hiding),
[Edit: These two are duals. The first is client-side abstraction, the second implementer-side abstraction (and in case you care about these things: in terms of formal logic or type theory, they correspond to universal and existential quantification, respectively).]
In OO, the class is the kitchen sink feature for achieving both kinds of abstraction.
Ad (1), for almost every "pattern" you need to define a custom class (or several). In functional programming on the other hand, you often have more lightweight and direct methods to achieve the same goals, in particular, functions and tuples. It is often pointed out that most of the "design patterns" from the GoF are redundant in FP, for example.
Ad (2), encapsulation is needed a little bit less often if you don't have mutable state lingering around everywhere that you need to keep in check. You still build ADTs in FP, but they tend to be simpler and more generic, and hence you need fewer of them.
When you write program in object-oriented style, you make emphasis on expressing domain area in terms of data types. And at first glance this looks like a good idea - if we work with users, why not to have a class User? And if users sell and buy cars, why not to have class Car? This way we can easily maintain data and control flow - it just reflects order of events in the real world. While this is quite convenient for domain objects, for many internal objects (i.e. objects that do not reflect anything from real world, but occur only in program logic) it is not so good. Maybe the best example is a number of collection types in Java. In Java (and many other OOP languages) there are both arrays, Lists. In JDBC there's ResultSet which is also kind of collection, but doesn't implement Collection interface. For input you will often use InputStream that provides interface for sequential access to the data - just like linked list! However it doesn't implement any kind of collection interface as well. Thus, if your code works with database and uses ResultSet it will be harder to refactor it for text files and InputStream.
MFUFA principle teaches us to pay less attention to type definition and more to common abstractions. For this reason Clojure introduces single abstraction for all mentioned types - sequence. Any iterable is automatically coerced to sequence, streams are just lazy lists and result set may be transformed to one of previous types easily.
Another example is using PersistentMap interface for structs and records. With such common interfaces it becomes very easy to create resusable subroutines and do not spend lots of time to refactoring.
To summarize and answer your questions:
One simple example of an issue that appears in OOP frequently: reading data from many different sources (e.g. DB, file, network, etc.) and processing it in the same way.
To make good MFUFA design try to make abstractions as common as possible and avoid ad-hoc implementations. E.g. avoid types a-la UserList - List<User> is good enough in most cases.
Follow suggestions from point 2. In addition, try to add as much interfaces to your data types (classes) as it possible. For example, if you really need to have UserList (e.g. when it should have a lot of additional functionality), add both List and Iterable interfaces to its definition.
OOP (at least in Java and C#) is not very well suited for this principle, because they try to encapsulate the whole object's behavior during initial design, so it becomes hard add more functions to them. In most cases you can extend class in question and put methods you need into new object, but 1) if somebody else implements their own derived class, it will not be compatible with yours; 2) sometimes classes are final or all fields are made private, so derived classes don't have access to them (e.g. to add new functions to class String one should implement additional classStringUtils). Nevertheless, rules I described above make it much easier to use MFUFA in OOP-code. And best example here is Clojure itself, which is gracefully implemented in OO-style but still follows MFUFA principle.
UPD. I remember another description of difference between object oriented and functional styles, that maybe summarizes better all I said above: designing program in OO style is thinking in terms of data types (nouns), while designing in functional style is thinking in terms of operations (verbs). You may forget that some nouns are similar (e.g. forget about inheritance), but you should always remember that many verbs in practice do the same thing (e.g. have same or similar interfaces).
A much earlier version of the quote:
"The simple structure and natural applicability of lists are reflected in functions that are amazingly nonidiosyncratic. In Pascal the plethora of declarable data structures induces a specialization within functions that inhibits and penalizes casual cooperation. It is better to have 100 functions operate on one data structure than to have 10 functions operate on 10 data structures."
...comes from the foreword to the famous SICP book. I believe this book has a lot of applicable material on this topic.
I think you're not getting that there's a difference between libraries and programmes.
OO libraries which work well usually generate a small number of abstractions, which programmes use to build the abstractions for their domain. Larger OO libraries (and programmes) use inheritance to create different versions of methods and introduce new methods.
So, yes, the same principle applies to OO libraries.
What are Cohesion and Decoupling? I found information about coupling but not about decoupling.
That article from Aaron is very good for understanding, also I'd recommend that you read manning publications Spring in Action book, they give very good examples on how the spring solves that problem it will definitely improve your understanding of this.
EDIT :
I came accross this in this great book called Growing object oriented software guided by tests :
Coupling :
Elements are coupled if a change in
one forces a change in the other. For
example, if two classes inherit from a
common parent, then a change in one
class might require a change in the
other. Think of a combo audio system:
It’s tightly coupled because if we
want to change from analog to digital
radio, we must rebuild the whole
system. If we assemble a system from
separates, it would have low coupling
and we could just swap out the
receiver. “Loosely” coupled features
(i.e., those with low coupling) are
easier to maintain.
Cohesion:
An element’s cohesion is a measure
of whether its responsibilities form a
meaningful unit. For example, a class
that parses both dates and URLs is not
coherent, because they’re unrelated
concepts. Think of a machine that
washes both clothes and dishes—it’s
unlikely to do both well.2 At the
other extreme, a class that parses
only the punctuation in a URL is
unlikely to be coherent, because it
doesn’t represent a whole concept. To
get anything done, the programmer will
have to find other parsers for
protocol, host, resource, and so on.
Features with “high” coherence are
easier to maintain.
Cohesion - related to the principle that a class/method should be responsible for one thing only i.e there are no stray methods that don't belong in the encapsulation; a method only does one thing. High/Low cohesion is the degree to which this holds.
Coupling - how interdependent different parts of the system are. e.g how and where there are dependencies. If two classes make calls to methods of each other then they are tightly coupled, as changing one would mean having to change the other. Decoupling is the process of making something that was tightly coupled less so, or not at all.
Flexible systems have High Cohesion and Loose Coupling.
For coupling, this Wikipedia article should answer all your questions. This article deals with cohesion.
"Decoupling" is just another name for "little/low coupling".
So these terms answer these questions:
How much does each part of your project depend on another part?
If you wanted to use just a part of your project (like to solve a specific problem) how much do you need to know about all the rest of the project?
Is every part of your project focused on a single solution to a specific problem or do solutions "leak" to other parts?
Here are my thoughts on cohesion. Imagine there is a module. Inside that module, we have some tasks. When those tasks are highly related to each other, we say it has high cohesion. When those tasks are not related, we say it has low cohesion. My best attempt to explain decoupling is that decoupling is the act of removing coupling.
Low Coupling helps us get to high cohesion! Remember that we want our module to have related tasks and one single responsibility. But what is coupling? Coupling is the degree of dependency on other modules to achieve our single responsibility for that module. So by low coupling, we are saying that we are not very dependent on external modules hence we have high cohesion.
However, if we have many dependencies to external modules, we would have high coupling and low cohesion. Get it?
Other more decorated thinkers and groups say:
Cohesion is the degree to which the tasks performed by a single module are functionally related." IEEE, 1983 "Cohesion is the "glue" that holds a module together. It can be thought of as the type of association among the component elements of a module. Generally, one wants the highest level of cohesion possible." Bergland, 1981
A software component is said to exhibit a high degree of cohesion if the elements in that unit exhibit a high degree of functional relatedness. This means that each element in the program unit should be essential for that unit to achieve its purpose. Sommerville, 1989
decoupling allows the separation of object interaction from classes and inheritance into distinct layers of abstraction used to polymorphic-ally decouple the encapsulation which is the practice of using re-usable code to prevent discrete code modules from interacting with each other.
Recently I heard that there are 9 rules for OOP(Java). I know only four as Abstraction, Polymorphism, Inheritance and Encapsulation. Are there any more rules for OOP?
Seems like what you're looking for are the Principles of Object-Oriented Design.
Summarized from Agile Software Development Principles, Patterns, and Practices. These principles are the hard-won product of decades of experience in software engineering. They are not the product of a single mind, but they represent the integration and writings of a large number of software developers and researchers. Although they are presented here as principles of object-oriented design, they are really special cases of long-standing principles of software engineering.
SRP The Single Responsibility Principle A class should have only one reason to change.
OCP The Open-Closed Principle Software entities (classes, packages, methods, etc.) should be open for extension, but closed for modification.
LSP The Liskov Substition Principle Subtypes must be substitutable for their base types.
DIP The Dependency Inversion Principle Abstractions should not depend upon details. Details should depend upons abstractions.
ISP The Interface Segregation Principle
Clients shold not be forced to depend upon methods that they do not use. Interfaces belong to clients, not to hierarchies.
REP The Release-Reuse Equivalency Principle
The granule of reuse is the granule of release.
CCP The Common Closure Principle
The classes in a package should be closed together against the same kinds of changes. A change that affects a closed package affects all the classes in that package and no other packages.
CRP The Common Reuse Principle
The classes in a package are reused together. If you reuse one of the classes in a package, you reuse them all.
ADP The Acylcic Dependencies Principle
Allow no cycles in the dependency graph.
SDP The Stable Dependencies Principle
Depend in the direction of stability.
SAP The Stable Abstractions Principle
A package should be as abstract as it is stable.
Not sure about any rules. All these mentioned things are more like OO paradigms to me. There are few advices we follow like,
Separation of Concern
Single Responsibility per Class
Prefer Composition over Inheritance
Programming to Interface
Plus all mentioned by Billybob, already
These OO principles are straight from Head First Design Patterns:
Encapsulate what Varies
Program to an Interface, rather than an Implementation
Favour Composition over Inheritance
A Class should have only one reason to Change (Single Responsibility Principle)
Sub-Types must be substitutable for their Base (Liskov Substitition Principle)
Classes shoule be Open for extension, but Closed for Modification (Open-Closed Principle)
These are concepts, not rules. There are no rules really, just decisions to make, some designs are better than others, some much better than others :-)
There are plenty of guidelines though :-) Some are language specific (C++ is riddled with them) others are OO specific. Too many to list though :-)
Off the top of my head, important ones are:
Loose coupling, high cohesion
Write testable classes, which you test
Use inheritence sparingly and only where it makes sense (prefer composition)
Try stick to the open/close principle.
(most important) KISS
Plenty to expand upon and add :-)
EDIT: I should add, the rules which you listed are not unique to OO
According to the Pragmatic Programmers - the rules are:
Keep it DRY (Don't Repeat Yourself)
Keep it SHY (Ensure that your classes have high cohesion and low coupling)
and tell the other GUY (Separation of concerns)
http://media.pragprog.com/articles/may_04_oo1.pdf
There are no "Rules" to OOP.
There are 4 language properties that make a language object-oriented or not (these are the things you listed in your question).
The rest of the material out there are guidelines. The best/most helpful guidelines I've read are GRASP
Many of the suggestions are not readily understandable by laymen (non-CS majors). I thought GRASP was pragmatic and approachable.
I think GRASP is nice because it suggests the most critical part of OO in its name - Assignment of Responsibility (to objects not programmers).
The two most critical GRASP concepts from which everything else derives are coupling and cohesion. These two concepts/principals drive all other patterns and approaches.
BTW - did I just interview you? You transcribed the question incorrectly...