I am interested to know which GoF design pattern translates directly to a specific SOLID principle? Like for instance I think (in my opinion) the strategy pattern translates to the Dependency-Inversion Principle.
Unfortunately, I could not find any literature that examines their relationships. It would be nice to have to learn both more effectively with each others perspectives.
SOLID principles are applied in most of GoF's design patterns.
S: Single responsability principle: the classes that are part of the
DP, have only one responsability
O: Open/Closed principle: it is easy
to extend with new functionality, for example the strategy pattern
allows you to implement an additional algorithm without having to
change the other classes that contain other algorithms
Liskov Substitution Principle: also applies to strategy for instance
GOF are patterns i.e. proven design solutions to recurring problems. SOLID are principles and are not tied to any specific problem domain hence true in any scenario
Related
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.
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 DIP states:
High-level modules should not depend on low-level modules. Both should depend on abstractions.
Abstractions should not depend upon details. Details should depend upon abstractions.
And the OCP states:
Software entities (classes, modules, functions, etc.) should be open
for extension, but closed for modification.
I think if we satisfy the DIP, it will cover the OCP too, So, why we separate these two principles?
Uncle Bob Martin, who popularized the Open-Closed Principle (OCP) and Dependency Inversion Principles (DIP) as two of the SOLID principles, states himself that DIP arises from an application of OCP and the Liskov Substitution Principle:
In this column, we discuss the structural implications of the OCP and
the LSP. The structure that results from rigorous use of these
principles can be generalized into a principle all by itself. I call
it “The Dependency Inversion Principle” (DIP).
Robert C. Martin, Engineering Notebook, C++ Report, 1996.
So you're right in stating that every instance of DIP will be an instance of OCP, but OCP is much more general. Here's a use-case of OCP but not DIP I ran into recently. Many web frameworks have a notion of signals, where upon one action, a signal is fired. The object sending the signal is completely unaware of the listeners who are registered with the signal. Every time you want to add more listeners to the signal, you can do so without modifying the sender.
This is clearly exemplifying OCP ("closed to modification, open for extension"), but not DIP, as the sender is not depending on anything, so there's no sense in talking about whether it depends on something more abstract or less so.
More generally you can say the Observer Pattern (one of the GoF patterns) describes how to comply with OCP but not DIP. It'd be interesting to go through the GoF book and see which ones have to do with OCP and how many of those are not DIP-related.
I think adhering to the DIP makes it easier to comply with the OCP. However, one does not guarantee the other.
For example, I can create a class that has a method that takes a parameter of base. If base is an abstract class then I'm adhering to the DIP as I have inverted the dependency to the caller. However, if the code in that method does something like:
if (base is derived)
(derived)base.DoSomethingSpecificToDerived;
elsif (base is evenMoreDerived)
(evenMoreDerived)base.DoSomethingSpecificToEvenMoreDerived;
Then it's not OCP compliant as I have to modify it every time I add a new derivative.
It's very contrived example, but you get my point.
The DIP tells you how to organize the dependencies. It doesn't tell you when you are done with a particular interface.
Roughly speaking, the message of OCP is to have complete but minimalistic interfaces. In other words, it tells you when you are done with an interface but it doesn't tell you how to achieve this.
In some sense, DIP and OCP are orthogonal.
So, why we separate these two principles?
As for design patterns and named principles, almost all of them have in common that:
Find what varies and encapsulate (hide) it.
Prefer aggregation over inheritance.
Design to interfaces.
Even if the named patterns and principles partially overlap in some sense, they tell you something more specific (in a more specific situation) than the above three general principles.
Good answer by #CS. To summarize,
The DIP is an extension of the OCP, so
When we satisfy the DIP, we generally satisfy the OCP as well.
The reverse is not true, and we can conceive of OCP-compliant, DIP violations. Here is one more (Java) example.
public abstract class MyClass {
DependencyOne d1;
DependencyTwo d2;
MyClass() {
d1 = new DependencyOne();
d2 = new DependencyTwo();
}
}
The OCP is satisfied because we can extend the class. The DIP is violated because we directly instantiate dependencies.
Now the challenge is, can we think of a DIP-compliant, OCP violation. The best example I can come up with is an annotation. In Java we use the #Deprecated annotation to mark code which is open for modification, thereby violating the OCP. At the same time, this code may be perfectly DIP compliant in terms of its abstractions and dependencies. Certain libraries use an #Beta annotation to similar effect.
I cannot imagine an example that is DIP-compliant and yet closed to extension, beyond the nullary example of a class which has no dependencies, which is not very interesting. I would say the DIP implies openness to extension. However, there may be edge cases where the DIP does not imply closedness to modification.
The OCP makes a dependent class easy to consume. The OCP enables asynchronous consumption of an interface by decoupling old implementations from newer versions. It allows the things that depend upon it to continue to depend on it even in the face of change for other purposes. That way a class never has to care who's calling it.
The DIP does a couple of things. It makes depending on external classes easy. Dependency Injection enables the substitutions of dependencies by encouraging the separation of creation duties from consumption. Instead of creating the external dependency that is to be consumed, the pattern states that it should be provided externally. Ultimately, this encourages code that is idempotent (code that does not change external state). Idempotent code is good because it can be verified that it does only what is immediately visible. It doesn't have external side effects. It's very testable, understandable, and readable.
How to ensure maintainability in a class? Can it simply be done by creating class using design patterns or is there something else involved? Also, what are the characteristics of a good method?
You won't do badly by following the SOLID and DRY principles.
SOLID is:
SRP Single responsibility principle
the notion that an object should have only a single responsibility.
OCP
Open/closed principle
the notion that “software entities … should be open for extension, but closed for modification”.
LSP
Liskov substitution principle
the notion that “objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program”. See also design by contract.
ISP
Interface segregation principle
the notion that “many client specific interfaces are better than one general purpose interface.”[5]
DIP
Dependency inversion principle
the notion that one should “Depend upon Abstractions. Do not depend upon concretions.”[5]
Dependency injection is one method of following this principle.
And DRY stands for Don't Repeat Yourself, meaning you should strive to remove any duplication in your code.
Put in a lot of effort to make sure you have a good interface. Once you have that, you can completely rewrite the class, if you want, without affecting any other code in the project. If your class is so big that you can't easily rewrite it, then that is an issue too.
Although Oded's answer is good for ensuring the maintainability of a program or library, this question is about class maintainability and for that, there are only two requirements... a good interface, and strong cohesion.
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...