Dependency inversion principle

svg viewer

DIP: Changes to Objects A and B will not affect the other

“One should depend upon abstractions, [not] concretions.”

 

The dependency inversion principle (DIP) has two parts:

  1. High-level modules should not depend on low-level modules. Instead, both should depend on abstractions (interfaces)
  2. Abstractions should not depend on details. Details (like concrete implementations) should depend on abstractions.

The first part of this principle reverses traditional OOP software design. Without DIP, programmers often construct programs to have high-level (less detail, more abstract) components explicitly connected with low-level (specific) components to complete tasks.

DIP decouples high and low-level components and instead connects both to abstractions. High and low-level components can still benefit from each other, but a change in one should not directly break the other.

The advantage of this part of DIP is that decoupled programs require less work to change. Webs of dependencies across your program mean that a single change can affect many separate parts.

If you minimize dependencies, changes will be more localized and require less work to find all affected components.

The second part can be thought of as “the abstraction is not affected if the details are changed”. The abstraction is the user-facing part of the program.

The details are the specific behind-the-scenes implementations that cause program behavior visible to the user. In a DIP program, we could fully overhaul the behind-the-scenes implementation of how the program achieves its behavior without the user’s knowledge.

This process is known as refactoring.

This means you won’t have to hard-code the interface to work solely with the current details (implementation). This keeps our code loosely coupled and allows us the flexibility to refactor our implementations later.

 

Implementation

We’ll create a general business program with an interface, high-level, low-level, and detailed components.

First, let’s create an interface with the getCustomerName() method. This will face our users.

public interface ICustomerDataAccess
{
    string GetCustomerName(int id);
}

Now, we’ll implement details that will depend on the ICustomerDataAccess interface. Doing so achieves the second part of the DIP principle.

public class CustomerDataAccess: ICustomerDataAccess
{
    public CustomerDataAccess() {
    }

    public string GetCustomerName(int id) {
        return "Dummy Customer Name";        
    }
}

We’ll now create a factory class that implements the abstract interface ICustomerDataAccess and returns it in a usable form. The returned CustomerDataAccess class is our low-level component.

public class DataAccessFactory
{
    public static ICustomerDataAccess GetCustomerDataAccessObj() 
    {
        return new CustomerDataAccess();
    }
}

Finally, we’ll implement a high-level component CustomerBuisnessLogic that also implements the interface ICustomerDataAccess. Notice that our high-level component does not implement our low-level component but merely uses it.

public class CustomerBusinessLogic
{
    ICustomerDataAccess _custDataAccess;

    public CustomerBusinessLogic()
    {
        _custDataAccess = DataAccessFactory.GetCustomerDataAccessObj();
    }

    public string GetCustomerName(int id)
    {
        return _custDataAccess.GetCustomerName(id);
    }
}

Here’s the full program in both code and visual chart:

public interface ICustomerDataAccess
{
    string GetCustomerName(int id);
}

public class CustomerDataAccess: ICustomerDataAccess
{
    public CustomerDataAccess() {
    }

    public string GetCustomerName(int id) {
        return "Dummy Customer Name";        
    }
}

public class DataAccessFactory
{
    public static ICustomerDataAccess GetCustomerDataAccessObj() 
    {
        return new CustomerDataAccess();
    }
}

public class CustomerBusinessLogic
{
    ICustomerDataAccess _custDataAccess;

    public CustomerBusinessLogic()
    {
        _custDataAccess = DataAccessFactory.GetCustomerDataAccessObj();
    }

    public string GetCustomerName(int id)
    {
        return _custDataAccess.GetCustomerName(id);
    }
}

svg viewer

Visual chart of our data access program

Related Tutorial
Follow Us
https://www.facebook.com/Rookie-Nerd-638990322793530 https://twitter.com/RookieNerdTutor https://plus.google.com/b/117136517396468545840 #
Contents +