46

Over time I could understand two parts of SOLID – the “S” and “O”.

“O” – I learned Open Closed Principle with the help of Inheritance and Strategy Pattern.

“S” – I learned Single Responsibility principle while learning ORM (the persistence logic is taken away from domain objects).

In a similar way, what are the best regions/tasks to learn other parts of SOLID (the “L”, “I” and “D”)?

References

  1. msdn - Dangers of Violating SOLID Principles in C#
  2. channel9 - Applying S.O.L.I.D. Principles in .NET/C#
  3. OOPS Principles (SOLID Principles)
5

3 Answers 3

57

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

enter image description here

6
  • 3
    I found the following collection of articles very useful: lostechies.com/wp-content/uploads/2011/03/…
    – Scroog1
    Jul 6, 2012 at 14:48
  • I read that whole article and I am not sold on patterns or on SOLID. The example is too simplistic, but when it gets complex that complexity is artificial. I am yet to encounter real world SOLID OOP without better alternatives.
    – Job
    Jul 7, 2012 at 3:46
  • 3
    since the lostechies articles were mentioned here there is also this solidexamples.codeplex.com (based on lostechies)
    – dark fader
    Jul 8, 2012 at 5:34
  • 2
    I was one of the contributors of Pablos eBook. I'm glad that people still find it useful. :) Jul 10, 2012 at 16:33
  • 1
    +1000000 if I could for your summary of the Open-Closed principle - everybody gets this wrong and thinks it's about inheritance
    – Alex
    Jul 31, 2014 at 16:48
13

(I)nterface Segregation and (D)ependency Inversion can be learned via unit testing and mocking. If classes create their own dependencies, you can't create good unit tests. If they depend on a too-broad interface (or no interface at all), it isn't very obvious what needs to be mocked to make your unit tests.

1
  • 2
    +1 this is very much true. You don't even have to adhere to the (imo) sometimes too strict 'a unit test should only test one thing' rule: if you can't come up with a decent test suite for a class in a couple of minutes, it violates I and D and probably the rest of the alfabet as well
    – stijn
    Jul 6, 2012 at 19:00
8

Liskov Substitution Principle basically does not let you to overuse implementation inheritance: you should never use inheritance just for code reuse (there is composition for this)! By adhering to LSP, you can be pretty sure that there actually exists a "is-a relationship" between your superclass and your subclass.

What it says is that your subclasses must implement all of the methods of the subclass in a similar way to the implementation of the methods in the subclass. You should never override a method with implementing NOP or return null when the supertype throws exception; stated in Design by Contract terms, you should respect the contract of the method from the superclass when overriding a method. A way to defend against breaking this principle is by never overriding an implemented method; instead extract an interface and implement that interface in both classes.

Interface Segregation Principle, Single Responsibility Principle and High Coehsion Principle from GRASP are somehow related; they refer to the fact that an entity should be responsible for only one thing so that there is only one reason for change so that change is done very easily.

It actually says that if a class implements an interface then it must implement and use all those interface's methods. If there are methods that ar not needed in that particular class, then the interface is not good and must be splitted into two interfaces one that has only the methods needed by the original class. It can be regarded from a POV, that relates to the previous principle by the fact that it does not let you create large interfaces so that their implementation could break LSP.

You can see Dependency Inversion in the Factory Pattern; here both the high-level component (the client) and the low-level component (individual instance to be created) depend upon the abstraction (the interface). A way to apply it in a layered architecture: you should not define an interface to a layer in the layer that is implemented but in the module that is called. For example the API to the data source layer should not be written in the data source layer but in the buisness logic layer, where it is needed to be called. In this way, the data source layer inherits/depends on the behavior defined in the buisness logic (thus the inversion) and not vice-versa (as would be in a normal way). This provides flexibility in the design, letting the business logic work without any code change, with another entirely different data source.

1
  • 1
    Great explanation on Liskov. :) Mar 13, 2013 at 18:12

Not the answer you're looking for? Browse other questions tagged or ask your own question.