Dependency Inversion Principle


As a software developer, you get used to lots of acronyms being banded about, but perhaps one of the most important is SOLID.In this series of posts, I will endeavour to explain the ideas behind the terminology and why it should be central to everything you do as a software developer.

This is the final article

Say no to hard-coded dependencies

iPhone In recent years Apple started a trend of phones with concealed built-in batteries that couldn’t be replaced by users and other phone manufactures have since followed.

They will tell you that they did this because they wanted their casing to be made of one single piece of aluminium, that it looked beautiful and saved weight but in my opinion it was a cynical ploy to force you into a dependency on Apple.

If there was a problem with your battery, you have to take the phone back to Apple for repair. You can’t buy a cheaper after-market alternative battery without voiding your warranty. Battery performance degrades over time so in doing this, they’d effectively created a need for you to buy a new phone every couple of years.

What Apple did was the exact opposite of the Dependency inversion principle, they effectively hardcoded the battery class so that you could only use the single available implementation of a battery.

If Apple didn’t hard code this dependency, you could find a battery that conformed to Apple’s IBattery specification i.e. the correct physical dimensions, contact positions and voltage then you could swap out one battery for another.

But what does this mean for my code?

Don’t be like Apple, use Dependency Injection such as Constructor Dependency Injection to introduce things that your code is dependent on. That way, you can replace the real dependency with a fake/mock version in order to test your code in isolation and if at some point in the future you need to change that dependency, you can easily without risk of breaking other parts of your code.

For example, suppose you have a website that allows users to register. As part of registering a user, you might want to send them an email. Without dependency injection the code could look like this:

public class UserService
{
  private UserRepository userRepository;
  private EmailService emailService;
  //Constructor
  public UserService()
  {
     userRepository = new UserRepository();
     emailService = new EmailService();
  }
  public User Register(User user)
  {
     //Save the user to the database
     user = userRepository.Save(user);
     //Send a welcome email
     emailService.SendWelcomeEmail(user);
     return user;
  }
} 

The code above isn’t without its merits, at least we have a separation of concerns with a separate class to handle the responsibility of Saving a new user and another class for Sending an email. This means that we will be able to reuse this code elsewhere.

But how do we test this? You’d need a persistence store setup for the user repository to save the user and you’d need a smtp server set up to send the email.

Generally speaking, seeing the new keyword in your code for classes that provide functionality is a code smell. It violates the Single Responsibility principle because your class becomes responsible for managing the lifetime of its dependency as well as doing what it’s meant to be doing. We would normally use an IoC (Inversion of Control) container to manage object life cycles and dependency resolution.

It may be that we would want to test this whole process together, that would be an integration test but we also want to be able to say that our Register method is sending an email without the test failing because the persistence store is unavailable. If we build up a large test suite and every test fails because of a dependency that we’re not trying to test, it could be very confusing.

Now lets apply the dependency inversion principle

public class UserService
{
  private IUserRepository _userRepository;
  private IEmailService _emailService;
  //Constructor
  public UserService(IUserRepository userRepository, IEmailService emailService)
  {
     _userRepository = userRepository;
     _emailService = emailService;
  }
  public User Register(User user)
  {
     //Save the user to the database
     user = userRepository.Save(user);
     //Send a welcome email
     emailService.SendWelcomeEmail(user);
     return user;
  }
} 

What you’ll notice is we haven’t actually changed very much. In fact our Register method hasn’t changed, but this time we’re injecting the dependencies via the constructor.

We could now write a fake version of the UserRepository like so

public class FakeUserRepository : IUserRepository
{
    private readonly IList _users;

    public FakeUserRepository()
    {
         _users = new List();
    }
    public User Save(User user)
    {
         _users.Add(user);
         return user;
    }
}

The fake version of the code can do whatever we want it to as long as it implements the Save(User user) method. In this case it’s storing the user in an in memory collection. Now we can test our UserService by injecting the fake repository and we know the test won’t fail because our repository can’t connect to a database.

We’re also following the Open Closed Principle because if we wanted to, we could change the behaviour of our UserService by injecting a different type of repository, maybe replacing a database persistence store with a document datastore without having to change a line of code in the UserService.

In real life, writing fake classes to replace real ones all the time would soon get very tedious so we can use mocking frameworks instead that can be setup to return any object we would want our dependencies to return but I’ll write about the joys of Mocking separately in my Test Driven Development post.