Dependency Inversion Principle

Start implementing DIP in your code, it will drive you through other SOLID principles as well. Take advantage of DIP to write loosely coupled software modules/classes. This makes a software modules mockable and testable .

Dependency inversion principle has two parts.

  1. High level modules should not depend on low level modules directly, both should depend on abstractions.
  2. Abstractions should not depend on details, details should depend on abstractions.

In order to understand this, let’s revise our example on Notifier we implemented while implementing Single Responsibility Principle with a bit of modification.

    public class CampaignManager
    {
        EmailNotifier notifier = new EmailNotifier();
        public void LaunchCampaign(Campaign campaign)
        {
            try
            {
                // Code to launch the campaign
                notifier.NotifySubscribers();
            }
            catch (Exception exception)
            {
                // Log messages
            }
        }
    }

    class EmailNotifier
    {
        public void NotifySubscribers()
        {
            // Notify subscribers through email
        }
    }

Here CampaignManager depends directly on EmailNotifier. This is poor design as both modules are tightly coupled, changing one would need changes in the other.
If we need to send notifications through SMS rather email messages we will need to change almost everything. This is what DIP part 1 says, modules should not depend directly on other modules but they should depend on abstractions.

What are abstractions? Abstractions are concepts rather actual functionality/details. In Object oriented we have interfaces and abstract classes to serve the purpose while concrete classes are details/modules which provide actual functionality.

Part 2 of the principle says, details should depend on abstractions. Meaning we our classes should depend on interfaces for defining their contracts/functionality. So to implement DIP I would create an interface which should define what functions an EmailNotifier has to perform.

    interface INotifier
    {
        void NotifySubscribers();
    } 

And then our EmailNotifier implements INotifier. This means we are now following part 2 of DIP.

    class EmailNotifier : INotifier
    {
        public void NotifySubscribers()
        {
            // Notify subscribers through email
        }
    }

Let’s implement Part 1 completely in our code. High level modules should depend on abstractions rather details or modules. Right now CampaignManager directly depends on EmailNotifier. How to make it depend on INotifier.

    class CampaignManager
    {
        private INotifier notifier;

        public void LaunchCampaign(Campaign campaign)
        {
            try
            {
                // Code to launch the campaign
                notifier.NotifySubscribers();
            }
            catch (Exception exception)
            {
                // Log messages
            }
        }
    }

We have amended our code, to make CampaignManager depend on INotifier but the question is how to instantiate notifier object reference?
Well we have three options here.
1. Pass the object in a constructor
2. Create a property
3. Create a method
For this article, I am using option 1 i.e. constructor. Dependency Injection Pattern is used for this purpose. I will write on DI Patter in future. This pattern can be used to implement DIP.

    class CampaignManager
    {
        private INotifier notifier;

        public CampaignManager(INotifier notifier)
        {
            this.notifier = notifier;
        }

        public void LaunchCampaign(Campaign campaign)
        {
            try
            {
                // Code to launch the campaign
                notifier.NotifySubscribers();
            }
            catch (Exception exception)
            {
                // Log messages
            }
        }
    }

One might wonder, where should we pass the object of EmailNotifier to CampaignManager, well the caller should do it. Suppose we have a Main function which instantiates CampaignManager.

    public void Main()
    {
        var manager = new CampaignManager(new EmailNotifier());
    }

Dependency inversion makes our software modules, testable and mockable. DIP is at the heart of Test driven designs (TDD).
Mockability:
Mockability is the ability to provide fake implementations of abstractions to allow us writing unit tests. While writing unit tests for LaunchCampaign, we will not call the actual EmailNotifier. With DIP we have the option to swap EmailNotifier with FakeNotifier

    class FakeNotifier : INotifier
    {
        public void NotifySubscribers()
        {
            // Do nothing
        }
    }

So our test would do something like


var manager = new CampaignManager(new FakeNotifier());

We have different frameworks like Moq/typemock to help us mock the types on the fly rather creating our own fake classes. I will cover this in detail in TDD article.
Testability:
Mockability and testability go hand in hand. Having the option to mock low level modules which is Notifier here, makes a software highly testable.

Factories:

This also gives us the ability to have factories in our code, which will provide us the objects of actual classes seamlessly depending on certain configurations. We talked about having SMSNotifier as well. Let’s create one

    class SMSNotifier : INotifier
    {
        public void NotifySubscribers()
        {
            // Notify subscribers through SMS
        }
    }

Now let’s create a factory which should be responsible for providing objects of classes. This factory can read some setting from the config let say NotifierType, if NotifierType is 1 we should use EmailNotifier else SMSNotifier.

    class Factory
    {
        public static INotifier GetNotifier()
        {
            // Read NotifierType from Config
            // create and return object of EmailNotifier if NotifierType is 1 else SMSNotifier
        }
    }

In our Main method now we use factory to provide the actual implementation

    public void Main()
    {
        var manager = new CampaignManager(Factory.GetNotifier());
    }

DIP is my personal favorite principle. It dares to write code which will follow almost all the SOLID principles.

Leave a Reply