Home > Design, Principle > Design Principles

Design Principles

In this post, I will be trying to describe my understanding about the design principles which is popularly known as “SOLID” principle.

“SOLID” is abbreviation formed by collecting initials of five basic designing principles namely,

  1. Single Responsibility Principle
  2. Open – Closed Principle
  3. Liscov Substitution Principle
  4. Interface Segregation Principle
  5. Dependency Inversion Principle

Let’s try to understand all these principles in brief with some examples :

Single Responsibility Principle :

This principle states that

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

This principle is simplest however not so simple to apply effectively. Normally while designing the software we are tend to think  in terms of physical grouping and boundaries rather than thinking in terms of responsibility and functionality.

e.g. We have one window in the application which fetches the data from database and displays in the window. Following is the skeleton of the window class :

  public class DisplayWindow
  {
      // establishes connection to database
      public void ConnectDataBase();
      // disconnect connection to database
      public void DisconnectDataBase();
      // gets data from databse
      public void GetData();
      // displays data read from database
      public void DisplayData();
  }

In Above example, all the implementation pertaining to said window has been grouped together in the DisplayWindow class. This  is definitely not following the Single Responsibility Principle. DisplayWindow class in this example is having responsibility of  data retrieval and representation both. There are two problems with this kind of approach :

1. it results in tight coupling between data and representation.

2. All the code has been stuffed in one class only so when changes are made to code it is more prone to regression defects.

If we want to segregate the classes based on the responsibility then it can be done as follow :

image

So we can make two class namely Window class and DataManger class. Here responsibility of both the classes are very clear. Window class is responsible for displaying data and DataManager is responsible for retrieving data from Database and supplying it to presentation layer. This results in decoupling of data and presentation. Also, such change can make DataManager class reusable.

Hence, when we think about the responsibilities it will result in code which is not so tightly coupled and it will increase the reusability.

Open – Closed Principle

This principle states

Software Entities (Classes, Modules, Functions, etc.) should be open for extension but closed for modifications.

This is very important principle and it states that design the code in such a way so that when the requirement changes, it is possible to meet the requirement by extending the module rather than making changes to existing implementation. It seems like kinda impossible right? However attempt can be made to achieve this objective.

Let’s consider one problem where banking application needs to calculate the interests for various types of account. Following code can meet the requirement :

public class BankApp
  {
      public void CalculateInterest(string accountType)
      {
          switch (accountType)
          {
              case "Savings":
                  // routine to calculate savings account interest
                  break;
              case "Current":
                  // routine to calculate current account interest
                  break;
          }
      }
  }

Now bank introduces new account type i.e. “SavingsPlus”. Implementation has to change to calculate the interest for this new account type to accommodate this new requirement. So in above implementation one new case should be added for new account type. This mean altering the existing implementation every time new account type is introduced. Also, when new account type is added, code has to be changed at all places where action needs to be decided based on the account type. The scenario discussed here is very simple but in real world application altering existing code which is working is definitely risky and can cause regression defects.  Also, one requirement triggering changes at multiple places in already running code is also not desirable. Open-Closed principle is exactly meant for such situation.</p> <p>Consider following solution for above problem :</p> <p><a href=”https://csharpsimplified.files.wordpress.com/2010/10/image1.png”><img style=”display:inline;border-width:0;” title=”image” border=”0″ alt=”image” src=”https://csharpsimplified.files.wordpress.com/2010/10/image_thumb1.png&#8221; width=”593″ height=”321″ /></a></p> <p>In above design, Abstraction has been achieved by introducing “Account” base class. Whenever new account type is introduced we can add one more concrete class deriving from “Account” base class and add the interest calculation routine to concrete class. Doing so we don’t have to change the existing implementation at all when new account type is introduced thus making existing code fully closed while code is still extendible.

  public class BankApp
  {
      public void CalculateInterest()
      {
          foreach (Account account in accounts)
          {
              account.CalculateInterest();
          }
      }
  }

Two principles which are key to meet Open-closed principle is Abstraction and Polymorphism. Abstraction helps us in making our code more extensible while polymorphism helps us in making our code close. This is quite apparent from above example.

In real world, no implementation can be 100% closed e.g. if the way interest is calculated is changed then above design is not closed. In order to make change to interest calculation routine we have to change the “CalculateInterest” method of each concrete account classes. However while implementing the calculation routine if some abstraction can be achieved then changes required can be reduced or existing implementation can remain closed when new requirement comes.

Liskov Substitution Principle

This principle state

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

This principle stresses on the fact that when sub classing is done, it should not break the base class implementation and subclass – superclass should remain interchangeable. To understand this let’s take following example :

  public class Rectangle
  {
      private double width;
      private double height;

      public Rectangle(double w, double h)
      {
          width = w;
          height = h;
      }

      public virtual void SetHeight(double h)
      {
          height = h;
      }

      public virtual void SetWidth(double w)
      {
          width = w;
      }

      public virtual double GetHeight()
      {
          return height;
      }

      public virtual double GetWidth()
      {
          return width;
      }
  }

  public class Square : Rectangle
  {
      private double size;

      public Square(double s) : base(s,s)
      {
          size = s;
      }

      public override void SetHeight(double h)
      {
          // for square we have to keep both height and width same
          base.SetHeight(h);
          base.SetWidth(h);
      }

      public override void SetWidth(double w)
      {
          // for square we have to keep both height and width same
          base.SetWidth(w);
          base.SetHeight(w);
      }
  }

In above code sample, Class Square is derived from Rectangle. In Square class when SetHeight is called we have to set both height and width in order to keep the sides same for square.

Now consider following use for the above implementation :

  public class EditorApp
  {
      public void ReduceWidthToHalf(Rectangle r)
      {
          r.SetWidth(r.GetWidth() / 2);
      }

      public static void main(string[] args)
      {
          EditorApp editor = new EditorApp();

          Square sq = new Square(20);

          // calling this will result in reduction of both width and height by half.
          // This is not desirable as per implementation of ReducteWidthToHalf method.
          // When actual object passed is Rectangle only width is reduced to half but
          // when Square object is passed, it reduces both height and width by half.

          // this mean both base class and sub class objects exhibit different behaviors
          // and they are not interchangable without breaking functionalities.
          editor.ReduceWidthToHalf(sq);
      }
  }

In above code sample, as my code comment mentions base class and derived class exhibits different behavior and Square can not be substituted for a Rectangle and vice versa.

So in nutshell, Liskov principle states that never break the base class implementation while subclassing.

Interface Segregation Principle

This principle states

Clients should not be forced to depend upon interfaces that they don’t use.

This principle basically talks about the problem of “Polluted”/”Fat” interfaces. Following example (from objectmentor)should clarify what is Fat interface :

There is a door system with normal door operation like door open, close and getting the door status, etc. This is a normal door operation. Basic IDoor interface below should do the needed for this :

image

image

However, some specialized door system is requested where door should close / alert user about it being open after certain time period. So this additional feature will require to add some timer function to existing door system as shown in right :

Well, Changing the IDoor interface have it’s implications :

1. Adding new method to existing interface will break the existing implementation because all the classes that derive from the IDoor interface will have to implement newly added method “TimerFunction”.

2. Though not all doors need a timer function, still as per new interface design all the objects that derive from IDoors will have to implement TimerFunction which is not desirable.

Also, adding new features in this way will make interface more n more fat and it will lead to tightly coupled code. Also, making interface fat will result in violation of Open-Close principle.

So solution to this problem is to segregate the Interfaces. Ideal way is to design the separate interface for each client.

image

So now we have two interfaces as shown in the figure. So to construct simple door we use IDoor interface while constructing TimedDoor we use both IDoor and ITimeFunction interface. In such design, the abstraction has been achieved in IDoor while add-ons have been segregated to new interface. Also, with this design even if changes in ITimeFunction is required than also changes will remain limited only to TimedDoor objects and SimpleDoor objects will remain unaffected. This way the tight coupling which existed in previously design is reduced. Also, after segregation classes are also complying to single responsibility principle.

Dependency Inversion Principle

This principle state

A. High level modules should not depend on low level module. Both should depend on abstraction. Both should depend on Abstraction.

B. Abstraction should not depend on details. Details should depend on Abstraction.

To understand this, consider following Salary Calculator application example where perks for the employee is to be calculated :

image

Worker is entitled to have various perks. Details of the allowed perk is available inside the Worker class. So in this implementation “ComputePerks” method will iterate through all the perks and go through the if loops to compute the final perk for each employee. But perks entitlement and perks may keep changing i.e. after a while no TransportationPerk is given or new perk say FoodAllownce is added then in this case we will have to change the if loops inside the method to accommodate new structure and this exercise will  repeat every time when perks change. So drawback of this design is :

1. High level module i.e. SalaryCalculator is dependent on the low level classes i.e. various perk calculator classes.

2. Tight coupling exist between the high and low level module.

3. Also, every time low level module changes, High level module has to change which also violates the “Open-Closed” principle.

So to fix this situation we have to follow what this principle is saying i.e. both High and low level module should depend on Abstraction. We should introduce Abstraction layer which can isolate high and low level modules and both can depend on this Abstraction layer. So consider the following new design :

image

In this design, IPerks interface has been added and now all perks will implement this interface and worker class will use the IPerks. Also, SalaryCalculator class will use the IPerks collection inside the worker class and will calculate the perks by iterating through the list as mentioned in the comment in above diagram. Using this approach,

1. High level SalaryCalculator class is not directly depending upon the Perk classes.

2. Both High and low level classes now depend on the abstraction of IPerks interfaces.

3. Changes in the perk structures i.e. addition or removal of perks will not affect the SalaryCalculator top level class. Every time when perk structure changes only the list inside the worker class will change. So this will also follow the “Open-Close” principle.

So finally, I am done with describing the SOLID principle in brief as per my understanding.  All the principles are very extensive in nature and deserves separate post for each but here it is my humble effort to put them in shortest possible way.  However, people are always welcome to provide their feedbacks/suggestions or pointing out any errors in constructive way.

References :

1. wiki

2. WWW.ObjectMentor.com

3. some other web contents

Advertisements
  1. Santosh
    October 4, 2010 at 6:20 pm

    The descriptions and the examples are simple and easy to understand. I thoroughly enjoyed in reading this topic and got an clear understanding about the 5 design principles.Great Effort…

  2. Surya
    December 8, 2010 at 8:41 pm

    Article is very well written. It is to the point and crisp. Keep up the good work.

  3. Pannikutty Ramsamy
    February 9, 2011 at 4:19 am

    Excellent article. Thanks!

  4. Chase
    April 14, 2011 at 4:51 am

    Hi. Great article! My only comment is that I believe img4.png is incorrect beneath your Interface Segregation Principle topic. Your IDoor interface contains the TimerFunction, which is being addressed separately by your ITimeFunction interface, so it shouldn’t be there.

    Short and succinct. I’m bookmarking this page 🙂

  5. jan
    April 21, 2011 at 2:52 am

    thank you for this post, very helpful.

  6. kapil
    July 18, 2011 at 8:40 pm

    Thank for this post,very helpful to understand each principles.Good Work.

  7. January 23, 2012 at 8:59 pm

    was really helpful, enjoyed reading it…thank you

  8. February 23, 2012 at 2:08 pm

    Thank you. This was a nice article to refresh the class design principles.

  9. Anuj Kumar
    February 23, 2012 at 5:43 pm

    Very good examples

  10. Neeraj Rawat
    March 16, 2012 at 2:26 pm

    Excellent Article…. Thanks a lot… Please post more on other topics as well…..

  11. venkatesh
    May 15, 2012 at 5:13 pm

    Great article, Simple and neat explanation. Thank a lot

  12. Senthil Raja
    May 20, 2012 at 3:29 am

    Easily understandable article.. Thanks

  13. Prince Thakkar
    December 4, 2012 at 3:47 pm

    great article

  14. SaTS
    January 7, 2013 at 4:40 pm

    Good article with SOLID explanation that good enough to trigger the spark. Thanks!

  15. Vishnu
    February 7, 2014 at 2:25 pm

    Simple and crispy and the example are very good to understand the each topic .Enjoyed reading it.

  1. January 31, 2012 at 6:05 am

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: