Software Design

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.

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.

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

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.

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.

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.

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

So our test would do something like

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

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.

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

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

Leave a Reply

Your email address will not be published. Required fields are marked *