I was in your shoes couple months ago till I found a very helpful article.
Each principle is nicely explained with real-world situations that each software developer may face in their projects. I am cutting short here and pointing to the reference - S.O.L.I.D. Software Development, One Step at a Time.
As pointed in comments, there is another very good pdf reading - Pablo's SOLID Software
Development.
In addition, there are some good books that describe SOLID principles in more details - Good Book on SOLID Software Development.
Edit and comments of a short summary for each principle:
“S” – Single Responsibility Principle is driven by the needs of the business to allow change. “A single reason to change” helps you understand which logically separate concepts should be grouped together by considering the business concept and context, instead of the technical concept alone.
In another words
, i learned that each class should have a single responsibility.
The responsibility is to just accomplish the assigned task
“O” – I learned Open Closed Principle and started to "prefer composition over inheritance" and as such, preferring classes that have no virtual methods and are possibly sealed, but depend on abstractions for their extension.
“L” – I learned Liskov Substitution Principle with help of Repository pattern for managing data access.
- “I” – I learned about Interface Segregation Principle by learning that clients shouldn't be forced to implement interfaces they don't use (like in Membership Provider in ASP.NET 2.0). So interface should not have “a lot of responsibilities”
- “D” – I learned about Dependency Inversion Principle and started to code that is easy to change. Easier to change means a lower total cost of ownership and higher maintainability.
As a useful resource from CodePlex was mentioned in comments, reference is included to SOLID by example
Aren't we breaking the Single Responsibility Principle with this super-mega-god-interface?
Not exactly, because the interface isn't doing anything. It has no responsibilities.
You are breaking Liskov Substitution Principle and Interface Segregation Principle though.
LSP because you're writing methods that do nothing, just to make it fit the interface (ie. What is true of IDrawable -- that you can move it -- is not true of Wall).
ISP because you'll have situations like a trade method, which should have access to Buy and Sell methods, but will also have access to Draw.
Are both approaches valid against the Liskov's Substitution Principle? I think it's broken in the first case because the post-conditions are broken due to some methods don't do anything. Anyway, in the second approach I'm not sure as well because of the interfaces inheritance tree
It's just as broken in the second case as the first.
Edit: On second thoughts, it isn't. But it is still broken. It doesn't seem automatically true that everything that could have a Move method will also have a Draw method. It might be true in your case but, if you later find yourself wanting to pass an object with a Move method into a method that receives an IMovable, you're going to have to implement Draw on that object, even if you don't need it.
And it gains you nothing over a third solution ...
Maybe, another alternative could be the definition of basic Drawable objects (with 'click' and 'draw' methods). Thus, we may take advantage of some kind of mechanism like the Decorator pattern in order to add behaviors dynamically. But how?
The best alternative is to segregate your interfaces fully. You can still have objects with lots of methods (but Dependency-Inversion Principle and Single Responsibility Principle dictate you must push the logic down into services), but they only have to implement the interfaces they need. Wall shouldn't implement IMovable, if it can't be moved.
public class Wall : IDrawable
public class Furniture : IDrawable, IMovable, ISellable
public class Window : IDrawable, IOpenable
Nothing here dictates that any of those interfaces must derive from another.
public interface IDrawable
{
void Draw(Canvas canvas);
}
public interface IMovable
{
void MoveTo(int x, int y);
}
etc
You should then manage your list of IDrawables, which you can only be sure implement Draw(), and cast to see if other methods are available.
For example,
ITradable tradable = drawable as ITradable;
if (tradable != null)
{
tradable.Trade(buyer, seller);
}
You're going to have to do the above in your option 2 anyway, so why tie everything to IDrawable?
If we use a language without interfaces such as JavaScript or Python, how can we deal with this problem?
OO languages tend to support at least one of three things:
Prototypical languages, such as Javascript, support ... well, prototypes, which allow for a slightly modified version of duck-typing (in prototypes, you generally ask if a method exists before calling it).
Other paradigms generally handle these issues in their own way.
Best Answer
There is no problem with the Single Responsibility Principle. The SRP doesn't say that the class shall do only one thing. It says that it should have only one reason to change. Some will argue that this implies that it should have only one single purpose. So for a shape, setting the dimensions and calculating a couple of shape related properties seems perfectly fine !
There is indeed a problem with the Liskow Subsittution Principle, since setting the height also changes the width. This might break the post-conditions of the setters (depending on how these post-conditions are expressed). And it breaks in any case the history constraint, that says that the properties of the Rectangle should only be changed according to the primitives of the Rectangle interface (i.e. no unexplained change).
Edit: This time, there is no issue with Interface Segregation Principle (thanks to the side effects when changing length or width). Dependency inversion is not relevant here either.
On the Open/CLosed, there's some room for discussion. The private variables should not be used by the extension, since it is an implementation detail of
Rectangle
andSquare
should not know about it. So it's not so open for extension: if the class would change the way it manages the heigth and the width, the extension would no longer work. SoSquare
should use only the public interface. To improve the situation you could consider separating the interface of Rectangle from its implementation. But this could be an overkill in such a simple case.