Skip to main content

SOLID: Dependency Inversion Principle

About 5 years ago, I wrote a series of short articles on the SOLID principles, shared by email to my colleagues as a weekly, Monday morning read. They were then archived for reference to our internal knowledge base. That's getting a revamp and re-platforming now, and given their age, I'm not sure these pieces will be making the cut. Given it's fairly timeless stuff, seems a shame for them to disappear into the virtual ether, so I'll save them for posterity here.

The SOLID Principles

The SOLID principles are a well established set of tenets intended to guide software design toward a maintainable solution. They are widely applicable and operate at a high level, so are well worth considering for almost any type of application we are looking to build, using any framework or language.

For the full set of articles in the series, see:

D is for Dependency Inversion Principle

The dependency inversion principle (DIP) is perhaps the my colleagues and I were most familiar with at the time of writing these articles, being an explicit pattern we widely use with ASP.Net MVC and also found it front-end frameworks like angularjs (I said these were old...). Strictly speaking though, dependency injection is what we are doing here, which is the method used to follow to the DIP.

As if often the case with these principles, they relate to each other - specifically in this case with the LSP and ISP. Without also following LSP, depending on abstractions would be risky without assurance that concrete types will respect the abstraction's contract. And by following ISP it's easier to ensure this is the case.

Formally, the DIP is expressed as high level modules should not depend upon low level modules. Both should depend upon abstractions.

More colloquially, I've read it as: if there is (potentially) more than one way to skin a cat, then do not behave like there is only one.

When implemently DIP, the class isn't responsible for creating components it depends on. Instead they are injected in via the constructor and the class uses what it has been provided with. So long as the object passed in implements the expected interface, that's all it needs to care about. This encourages loose coupling between components and prevents changes to one leading to rippling changes throughout an application.

Of course as it stands this has potentially just moved the problem. Although the class itself is improved by the removal of the direct coupling to a particular implementation of a dependency, we've required the calling code that instantiates the class to do it. Avoiding this regress of the coupling though is where IoC (inversion of control - another phrase for dependency injection) containers come in.

These are configured at the application root - for an ASP.Net application, usually via global.asax - where the particular concrete types to be used for a given interface are defined. These days normally this is done in code due to the type safety this offers and ability to customise the types used based on configuration options, though direct configuration via XML file is also possible with some containers.