This post is a brief summary of Microsoft TechEd, North America 2014 session Applying S.O.L.I.D. Principles in .NET/C# by Chris Klug.

Other sources of inspiration:

SOLID principles are build on top of each other. So, carefully understanding one would help realizing the need for the next one. But keep in mind that over engineering and premature optimization are still the root cause of all evil. So, draw the line between when you think it makes sense to continue following these principles to the letter and when it does not. Remember that simple Hello World application does not really need to be huge wired with interfaces and abstract class to the very last bit possible - it is just a Hello World.

Single Responsibility Principle

“A class should have only one reason to change”. There can be only one requirement that, when changed, will cause a class to change. We want to make our classes so small, that it’s difficult to make a mistake.

Open/Closed Principle

“Software entities should be open for extension, but closed for modification”. Once a class is done, it is done! Once a requirement changes, a new class is created instead of an old one, but old one is never changed (getting back to SRP).

The most obvious reason for developer to spend lots of time maintaining unit-tests after implementing new requirements is due to the violation of OCP.

Bertrand Meyer is the author of OCP in such a way that suggests inheriting the base class and overriding it’s implementations. There is also another way - by using interfaces and its’ implementations. The former approach might be more flexible, but it all depends on a particular situation.

The biggest benefit - code does not get changed.

Liskov’s Substitution Principle

“Let q(x) be a property provable about objects x of type T. Then q(y) should be provable for objects y of type S where S is a subtype of T” (Barbara Liskov’s keynote entitled “Data abstraction and hierarchy”).

  • A subclass should behave in such a way that it will not cause problems when used instead of the superclass
  • No new exception types are allowed to be thrown, unless they are subclasses of previously used ones
  • Preconditions cannot be strengthened in a subclass
  • Postconditions cannot be weakened in a subclass
  • The history constraint (e.g. it is not allowed to make an immutable class mutable)

Interface Segregation Principle

“Clients should not be forced to depend upon interfaces that they don’t use”. Breaking down interfaces in smaller pieces make them easier to implement, and offers more control over who sees what.

Dependency Inversion Principle

  • “High-level modules should not depend on low-level modules. Both should depend on abstractions.”
  • “Abstractions should not depend on details. Details should depend on abstractions.”

By making sure classes don’t depend on specific implementations, it becomes easy to change things around. And there is a huge different between Dependency Inversion and Dependency Injection!