The following article aims to explain the five SOLID principles with real world examples. The SOLID principles are five programming principles which is considered to be the foundation of every well designed application. Following the principles will most likely lead to applications which are is easy to extend and maintain. That’s possible since you got small well defined classes with clear contracts.
Single responsibility states that every class should only have one reason to change. A typical example is an user management class. When you for instance create a new user you’ll most likely send an welcome email. That’s two reasons to change: To do something with the account management and to change the emailing procedure. A better way would be to generate some kind of event from the account management class which is subscribed by a UserEmailService that does the actual email handling.
The most effective way to break applications it to create GOD classes. That is classes that keeps track of a lot of information and have several responsibilities. One code change will most likely affect other parts of the class and therefore indirectly all other classes that uses it. That in turn leads to an even bigger maintenance mess since no one dares to do any changes other than adding new functionality to it.
Making sure that a class has a single responsibility makes it per default also easier to see what it does and how you can extend/improve it.
Classes that are hard to unit test are often breaking SRP.
Open/Closed principle says that a class should be open for extension but closed for modification. Which means that you can add new features through inheritance but should not change the existing classes (other than bug fixes).
The reason is that if you modify a class, you’ll likely break the API/Contract of the class which means that the classes that depend on it might fail when you do so. If you instead inherit the class to add new features, the base contract is untouched and it’s unlikely that dependent classes will fail.
Here is a real world parser method (from a SO question which I’ve answered):
// Depending of what comes after the '!' character,
// process the entire "scope" and/or the command in "line".
if(line == "!execute")
elseif(line == "!single_line_directive")
scope = newStringBuilder();
// No processing directive, i.e. add the "line"
// to the current scope.
line = reader.ReadLine();
It works great. But the method have to be changed each time we want to add support for a new directive. It’s therefore not closed for modification.
Lets create an interface which is used for each handler (for'$'and'!'in the example above):
voidProcess(IProcessContext context, stringline);
Notice that we include a context object. This is quite important. If we create a new parser calledSuperCoolParserin the future we can let it create and pass aSuperAwsomeContextto all handlers. New handlers which supports that context can use it while others stick with the basic implementation.
We comply with Liskovs Substitution Principle and doesn’t have to change theIMyHandler.Processsignature (and therefore keeping it closed for modification) when we add new features later on.
IProcessContext context = null; // create your context here.
stringline = reader.ReadLine();
while(line != null)
IMyHandler handler = null;
handler = _defaultHandler;
line = reader.ReadLine();
If you go back and look at the!handling you’ll see a lot ofifstatements. That method likely have to be changed to add support for more features. Hence it do also violate the principle. Let’s refactor again.
Liskovs Substitution Principle states that any method that takes class X as a parameter must be able to work with any subclasses of X.
The principle makes sure that every class follows the contract defined by its parent class. If the classCarhas a method calledBreakit’s vital that all subclasses breaks when theBreakmethod is invoked. Imagine the suprise ifBreak()in aFerrarionly works if the switchChickenModeis activated.
Let’s use the motivator image as inspiration and define the following classes:
publicclassDuck : IDuck
//do something to swim
publicclassElectricDuck : IDuck
And the calling code:
As you can see, there are two examples of ducks. One regular duck and one electric duck.
The electric duck can only swim if it’s turned on.TheMakeDuckSwimmethod will not work if a duck is electric and not turned on.
That breaks LSP since any user of theIDuckinterface expects the duck to swim when callingMakeDuckSwim.
You can of course solve it by doing something like this (in the method that uses the ducks)
But that would break Open/Closed principle and has to be implemented everywhere that the ducks are used (and therefore still generate instable code).
The proper solution would be to automatically turn on the duck in theSwimmethod and by doing so make the electric duck behave exactly as defined by theIDuckinterface.
ISP states that interfaces that have become “fat” (like god classes) should be split into several interfaces. Large interfaces makes it harder to extend smaller parts of the system.
There is nothing that says that there should be a one-to-one mapping between classes and interfaces. It’s in fact much better if you can create several smaller interfaces instead (depends on the class though).
TheMembershipProviderin ASP.NET is a classical example of a violation. MSDN contains a large article (which 4 out of 34 have found useful) which contains a long and detailed instruction on how to properly implement the class.
The provider could have been divided in several parts:
*MembershipProvider– A facade to the below interfaces
*IAccountRepository– Used to fetch/load accounts
*IPasswordValidator– Checks that the password is valid (according to business rules)
*IPasswordStrategy– How to store PW (hash it, use encryption or just store it in plain text)
Now you only have to implement a small part if you need to customize the provider (for instanceIAccountRepositoryif you are using a custom data source). There’s a saying: “Favor composition over inheritance” which the membership providers illustrates well. The original solution used inheritance while mine used composition.
The principle which is easiest to understand. DIP states that you should let the caller create the dependencies instead of letting the class itself create the dependencies. Hence inverting the dependency control (from letting the class control them to letting the caller control them).
Which makes it a lot more fun since we now can do the following:
varmyVolvo = newVolvo(newBigBadV12());
(Nice real world example, huh? ;))
I messed up a bit. Dependency Inversion states that you should depend upon abstractions and that lower layers should not be aware of higher layers. I’ll get back to that. Dependency Injection is when you inject dependencies into the constructors instead of creating them in the class. Inversion Of Control is that the container controls your objects and their lifetime.
A. HIGH LEVEL MODULES SHOULD NOT DEPEND UPON LOW LEVEL MODULES. BOTH SHOULD DEPEND UPON ABSTRACTIONS.
B. ABSTRACTIONS SHOULD NOT DEPEND UPON DETAILS. DETAILS SHOULD DEPEND UPON ABSTRACTIONS
The original principle targets modules while I also like to apply it at class level too. It makes the principle easier to apply (so the text below is how I apply the principle).
Depend on abstractions simply means that you should depend on as generic class/interface as possible. For instanceIUserRepositoryinstead ofDbUserRepositoryorHttpRequestBaseinstead ofHttpRequest. The purpose is that your code should be as flexible as possible. The more abstract the dependencies are, the easier it is to refactor them or create new implementations.
Depending on abstractions also make your code less likely to change if the dependency change.