Dependency Inversion Principle (DIP)

Intent

High-level modules should not depend on low-level modules. Both should depend on abstractions.

Abstraction should not depend upon details. Details should depend upon abstractions.

Description

In structured analysis and design, the software that gets created tends to have a dependency in the wrong direction. The business rules depend on the low-level implementation details. This is a cause of problems later as the business rules may change when the low-level implementation has to be changed. Rather it should be the other way round; the low-level implementation should be dependent on the business rules. Thus, the dependency has to be inverted; hence the name Dependency Inversion Principle.

Consider the classic example of a button that is used to switch on and off a lamp.

Lamp.cs

class Lamp
{
public void turnOn()
{
//implemenation
}

public void turnOff()
{
//implementation
}
}

Button.cs

class Button
{
private Lamp lamp = new Lamp();
private Boolean pressed = false;
public void Press()
{
if(pressed)
{
lamp.turnOff();
}
else
{
lamp.turnOn();
}

press = !pressed;
}
}

The Lamp class has the implementation of the workings of a lamp. It is controlled by a Button class that calls its methods turnOn() and turnOff(). This is an example of a structured analysis and design. The logic of control is in Button and the low-level implementation is in the Lamp class. So Button class contains the high-level implementation (rules) and the Lamp class contains the low-level implementation.

It is quite obvious that the dependency direction is from Button class to Lamp class. If the implementation of the lamp were to change, it would also affect the Button class. This means the business rules are affected by the low-level implementation and this would require changing a lot of high-level classes.

Refactored Solution

Ideally, the business rules define how the low-level implementation should be done. So, the low-level modules ideally depend on the business rules and operate on accordance to those.

So, according to the Dependency Inversion Principle, we should invert the dependency of Button class on the Lamp class. This can be done by defining an abstraction that will set the rules for the Lamp class to follow if it has to be controlled by the Button class.

We add a class called as Switchable that contains the protocol that all the objects should follow if they are to be controlled by the Button class. The Button class will now interact with Switchable instead of the Lamp.

Switchable.cs

//can be an interface or an abstract class
interface Switchable
{
void turnOn();
void turnOff();
}

Lamp.cs

class Lamp : Switchable
{
public void turnOn()
{
//implemenation
}

public void turnOff()
{
//implementation
}
}

Television.cs

class Television : Switchable
{
public void turnOn()
{
//implemenation
}

public void turnOff()
{
//implementation
}
}

Button.cs

class Button
{
private Switchable switchable;
private Boolean pressed = false;

public void setSwitchable(Switchable s)
{
switchable = s;
}

public void Press()
{
if(pressed)
{
switchable.turnOff();
}
else
{
switchable.turnOn();
}

press = !pressed;
}
}

Now, it is possible to add more objects that can be switched off through the button without changing the Button class itself. The dependency has been inverted now, as the low-level modules depend on the high level modules. This promotes loose coupling and extensibility. You will see more exciting posts soon. Stay tuned with us.

Advertisements

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