Principles of Class Design : SOLID

There are 5 basic principles of class design commonly known as SOLID.

S – Single Responsibility Principle
O – Open Closed Design Principle
L – Liskov Substitution Principle
I – Interface Segregation principle
D – Dependency Injection or Inversion principle

Single Responsibility Principle

There should never be more than one reason for a class to change.

This principle is similar to designing classes which are highly cohesive. One responsibility doesn’t mean that the class has only ONE method. A responsibility can be implemented by means of different methods in the class.

Imagine designing classes with more than one responsibility/implementing more than one functionality. There’s no one restriction to do this, but there will be too much dependency over due time. When it comes to changing, the class is so big, that we are not sure which method affects what and we end up testing everything.

Open Closed Design Principle

Software entities (Classes, modules, functions) should be open for extension, closed for modification.

Suppose you are writing a module to approve personal loans and before doing that you want to validate the personal information, code wise we can depict the situation as:

OCPloadhandlerWrongOCPpersonalWrong

So far so good. As you all know the requirements are never the same and now its required to approve vehicle loans, consumer goods loans and what not. So one approach to solve this requirement is to:

OCPvehicleWrongOCPloadhandlerWrong2

We have edited the existing class to accommodate the new requirements in the process we ended up changing the name of the existing method and also adding new methods for different types of loan approval. This clearly violates the OCP. Lets try to implement the requirement in a different way:

OCPvalidatorInterface

Now using the above validators we can write a LoanApprovalHandler to use the Validator interface, which is implemented by various individual validators like VehicleLoadValidator, PersonalLoanValidator. Now we can make write our handler as follows:

OCPloadhandlerCorrrect

So to accommodate any type of loan validators we would just have create a subclass of Validator and then pass it to the approveLoan method. That way the class is CLOSED for modification but OPEN for extension.

Liskov Substitution Principle

Methods that use references to base classes must be able to use objects of derived classes without knowing it.

In simple terms, if a methods input parameters takes a Parent reference or list, it should not fail if a Child object is passed.

It would confirm to the perfect class hierarchies which in turns confirm to ease of code maintenance. Preventing subtyping related bugs.

eg. Suppose you have written Vehicle class, which is extended by HondaCityPetrol and HondaCityDiesel class. The child class have overrides methods accordingly.

 

LSPvechileWrongLSPhondapetrolWrongLSPhondadieselwrong

LSPvehicletestWrong

It’s the clear violation of LSP. The code will break in getVehicleDetails method. Diesel car will throw an exception. What could be the consequences of such a class hierarchy :

  1. Logically, both derived classes can extend Vehicle class, but if there is a functionality lying in a base class which a derived class should not support then definitely it’s not the correct class hierarchy and, in such a case it does make a sense to redefine the hierarchy.

  2.  derived class does not contain the overloaded method which it does not support…. your program may break unexpectedly or you get undesirable results (preventing subtyping related bugs)

Correct way (One of the): Have a generic method in parent class that child classes always supports. Also, child classes can override if they want to change something in it.

LSPvehicleCorrect        LSPvehicletestCorrect

Now getVehicleDescription will not fail. Alternatively you may choose to remove the methods which may not be supported by child class.

Interface Segregation Principle

Clients should not be forced to depend on methods that they do not use.

This principle deals with the non-cohesive interface- interfaces that contain methods that are not used by all the clients of the interface. This leads to interface pollution and such interface is called as a fat interface. The clients are forced to depend on the method they are never going to use and might lead to future runtime error. For example : 

ISPanimalInterfaceWrong

ISPtigerWrong

Here we have an Interface Animals with basic methods. Now Tiger implements these methods but is force to throw an UnsupportedOperationException for method fly. similarly for any Fish class which will have to throw exceptions for fly and walk.

Interface should be design carefully. One of the way to solve above problem.

ISPinterfaceCorrect

Now Tiger Class which will implement LandAnimals will not have to implement fly method.

 

Dependency Injection Principle

               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.

A class should not configure its dependencies but should be configured from outside or Inject Dependencies from outside.

For example, suppose we have a Manager Class (Manager) which does some business logic. This class requires a Database Class (DAO) for fetching/storing data. Here Manger is dependent on DAO. According to Dependencies Injection principle, the DAO is pushed in Manager from the outside, i.e Manager should not instantiate DAO from inside of its own. Instead, it should get the DAO object via Constructor or a method (some sort of setter).

This way it decouples your classes. Ofcourse there will be some other class which will first create a DAO object and then set or pass it to Manager, but the point here is your Manger decouples itself from DAO. Its more readable and reusable strategy. It’s also eases unit testing.

The best implementation of this principle is the SPRING framework.

Lets have another example.  Suppose we have MusicPlayer Class which play a different genre of music.

DIProckClassWrong

DIPpopClassWrong

DIPmusicplayerClassWrong

Your application class might look like this

DIPclientWrong

To add more genre you need to modify your MusicPlayer class again and again.

DIP way :

DIPsongInterface

Your Rock and Pop class both implement this interface.

Your MusicPlayer class will look like this:

DIPmusicplayerClassCorrect

For calling:

DIPclientCorrect

This way you have decouples the dependencies of MusicPlayer class on the individual genre classes. Note that we can impliment it using inheritance and abstraction also.

 

It not compulsory to follow all rules while building a class, yet its important to keep these things in mind. Also, following one rule may automatically satisfy others also.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: