Dependency Inversion Principle(DIP)

Dependency Inversion Principle(DIP)

What Is Dependency Inversion Principle(DIP)?

In object-oriented design, the dependency inversion principle(DIP) refers to a specific form of decoupling software modules.
Dependency Inversion Principle or DIP has two key points:
1.Abstractions should not depend upon details;
2.Details should depend upon abstractions.
The principle could be rephrased as use the same level of abstraction at a given level. Interfaces should depend on other interfaces. Don’t add concrete classes to method signatures of an interface. However, use interfaces in your class methods.

Simple Example Of Dependency Inversion Principle

Consider the case of the Button object and the Door object.
The Button object senses the external environment. On receiving the Poll message, the Button object determines whether a user has “pressed” it. It doesn’t matter what the sensing mechanism is.
It could be a button icon on a GUI, a physical button being pressed by a human finger, or even a motion detector in a home security system. The Button object detects that a user has either activated or deactivated it.
The Door object affects the external environment. On receiving a TurnOn message, the Door object opens the door. On receiving a Close message, it closes that door.
How can we design a system such that the Button object controls the Door object?  The Button object receives Poll messages, determines whether the button has been pressed, and then simply sends the Open or Close message to the Door.

Consider the C# code implied by this model (above). Note that the Button class depends directly on the Door class. This dependency implies that Button will be affected by changes to Door. Moreover, it will not be possible to reuse Button to control a Motor object. In this model, Button objects control Door objects and only Door objects.


 public class Button  
  {  
   private Door door;  
   public void Poll()  
   {  
    if (/*some condition*/)  
     door.Open();  
   }  
  }  
The above solution violates DIP.The abstractions have not been separated from the details.Without such a separation, the high-level policy automatically depends on the low-level modules, and the abstractions automatically depend on the details.

Finding the Underlying Abstraction

What is the high-level policy? It is the abstraction that underlies the application, the truths that do not vary when the details are changed. It is the system inside the systemit is the metaphor. In the
Button/Door example, the underlying abstraction is to detect an open/close gesture from a user and relay that gesture to a target object.
What mechanism is used to detect the user gesture? Irrelevant! What is the target object? Irrelevant!
These are details that do not impact the abstraction.
The model in above can be improved by inverting the dependency upon the Lamp object. In model below, we see that the Button now holds an association to something called a ButtonServer,which provides the interfaces that Button can use to turn something on or off. Door implements the ButtonServer interface.
Thus, Door is now doing the depending rather than being depended on.
The design above allows a Button to control any device that is willing to implement the ButtonServer interface. This gives us a great deal of flexibility. It also means that Button objects will be able to control objects that have not yet been invented.
However, this solution also puts a constraint on any object that needs to be controlled by a Button.
Such an object must implement the ButtonServer interface. This is unfortunate, because these objects may also want to be controlled by a Switch object or some kind of object other than a Button.
By inverting the direction of the dependency and making the Door do the depending instead of being depended on, we have made Door depend on a different detail: Button. Or have we?
Door certainly depends on ButtonServer, but ButtonServer does not depend on Button. Any kind of object that knows how to manipulate the ButtonServer interface will be able to control a Door .
Thus,the dependency is in name only. And we can fix that by changing the name of ButtonServer to something a bit more generic, such as SwitchableDevice. We can also ensure that Button and SwitchableDevice are kept in separate libraries, so that the use of SwitchableDevice does not imply the use of Button.
In this case, nobody owns the interface. We have the interesting situation whereby the interface can be used by lots of different clients, and implemented by lots of different servers. Thus, the interface needs to stand alone without belonging to either group. In C#, we would put it in a separate namespace and library.
Further Reading

1.Agile Software Development, Principles, Patterns, and Practices By Robert C.Martin

2. Head First Design Patterns by Freeman, Eric; Freeman, Elisabeth; Kathy, Sierra; Bert, Bates

3.Object Solutions: Managing the Object-Oriented Project by Grady Booch,

Interface Segregation Principle(ISP)

ISP Image

Interface Segregation Principle

The Interface Segregation Principle(ISP) deals with the disadvantages of “fat” interfaces.

The Interface Segregation Principle(ISP) states that no client code object should be forced to depend on methods it does not use. Basically, each code object should only implement what it needs, and not be required to implement anything else.

The ISP was first used and formulated by Robert C. Martin while consulting for Xerox.

I will tackle today’s principle using an example from MSDN

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
 
namespace SOLIDPrinciplesDemo 
{ 
    // Only common generic methods exists for all derived classes. 
    interface IDataProvider 
    { 
        int OpenConnection(); 
        int CloseConnection(); 
    } 
    // Implement methods specific to the respective derived classe 
    interface ISqlDataProvider : IDataProvider 
    { 
        int ExecuteSqlCommand(); 
    } 
    // Implement methods specific to the respective derived classe 
    interface IOracleDataProvider : IDataProvider 
    { 
        int ExecuteOracleCommand(); 
    } 
    // Client 1 
    // Should not force SqlDataProvider client to implement ExecuteOracleCommand, as it wont required that method to be implemented. 
    // So that we will derive ISqlDataProvider not IOracleDataProvider 
    class SqlDataClient : ISqlDataProvider 
    { 
        public int OpenConnection() 
        { 
            Console.WriteLine("\nSql Connection opened successfully"); 
            return 1; 
        } 
        public int CloseConnection() 
        { 
            Console.WriteLine("Sql Connection closed successfully"); 
            return 1; 
        } 
 
        // Implemeting ISqlDataProvider, we are not forcing the client to implement IOracleDataProvider 
        public int ExecuteSqlCommand() 
        { 
            Console.WriteLine("Sql Server specific Command Executed successfully"); 
            return 1; 
        } 
    } 
    // Client 2 
    // Should not force OracleDataProvider client to implement ExecuteSqlCommand, as it wont required that method to be implemented. 
    // So that we will derive IOracleDataProvider not ISqlDataProvider 
    class OracleDataClient : IOracleDataProvider 
    { 
        public int OpenConnection() 
        { 
            Console.WriteLine("\nOracle Connection opened successfully"); 
            return 1; 
        } 
        public int CloseConnection() 
        { 
            Console.WriteLine("Oracle Connection closed successfully"); 
            return 1; 
        } 
        // Implemeting IOracleDataProvider, we are not forcing the client to implement ISqlDataProvider 
        public int ExecuteOracleCommand() 
        { 
            Console.WriteLine("Oracle specific Command Executed successfully"); 
            return 1; 
        } 
    } 
    class InterfaceSegregationPrincipleDemo 
    { 
        public static void ISPDemo() 
        { 
            Console.WriteLine("\n\nInterface Inversion Principle Demo "); 
 
            // Each client will implement their respective methods no base class forces the other client to implement the methods which dont required. 
            // From the above implementation, we are not forcing Sql client to implemnt orcale logic or Oracle client to implement sql logic. 
 
            ISqlDataProvider SqlDataProviderObject = new SqlDataClient(); 
            SqlDataProviderObject.OpenConnection(); 
            SqlDataProviderObject.ExecuteSqlCommand(); 
            SqlDataProviderObject.CloseConnection(); 
 
            IOracleDataProvider OracleDataProviderObject = new OracleDataClient(); 
            OracleDataProviderObject.OpenConnection(); 
            OracleDataProviderObject.ExecuteOracleCommand(); 
            OracleDataProviderObject.CloseConnection(); 
        } 
    } 
}

Advantages of Using ISP:

  1. Better Understandability
  2. Better Maintainability
  3. High Cohesion
  4. Low Coupling

Limitations

ISP like any other principle should be used intelligently when necessary otherwise it will result in a code containing lots of interfaces containing one method. So the decision should be taken intelligently.

Additional Resources

1.Agile Software Development, Principles, Patterns, and Practices-Robert C.Martin

2.SOLID Principles of Object Oriented Design– Steve Smith(Pluralsight)

3.Principles Of OOD

That’s it folks for this week,hope you learnt something in today’s topic,Interface Segregation Principle(ISP), the “I” in the S.O.L.I.D acronym,i will be concluding our series in the next post covering the Dependency Inversion Principle.