Archive

Posts Tagged ‘Dependency Inversion Problem’

Design Principles

October 1, 2010 16 comments

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