The Dependency Inversion Principle

Tweet

If I ever had to make an ad-hoc “Solomonic” decision concerning the relevance of each SOLID principle, I would just dare say that the Dependency Inversion Principle (DIP) is the most underrated of all.

While some central concepts in the realm of object-oriented design are generally harder to digest at first, such as separation of concerns and implementation switching, more intuitive and untangled paradigms on the other hand are simpler, like programming to interfaces. Unfortunately, the DIP’s formal definition is surrounded by a double-edged curse/blessing that often makes programmers gloss over it, as in many cases there’s an implicit assumption that the principle is nothing but a fancy expression for the aforementioned “programming to interfaces” commandment:

  1. High-level modules should not depend on low-level modules. Both should depend on abstractions.
  2. Abstractions should not depend upon details. Details should depend upon abstractions.

At first blush, the above statements seem to be self-explanatory. Considering at this point no one would disagree that systems built upon a strong dependency on concrete implementations are an ominous sign of bad design, switching over a few abstractions makes perfect sense. So this would lead us right back to the start, thinking that the DIP’s main concern is all about programming to interfaces.

In reality, decoupling interface from implementation is just a half-baked approach when it comes to fulfilling the principle’s requirements. The missing portion is achieving the real inversion process. Of course, the question that comes up is: inversion of what?

In a traditional sense, systems were always designed to work with high-level components, be it classes or procedural routines, depending upon low-level ones (the details). For instance, a logging module could have a strong dependency on a set of concrete loggers down the chain which actually log information onto the system. It’s no surprise then this scheme would noisily ripple side effects up toward the upper layers whenever the protocol owned by the loggers is modified, even if the protocol has been abstracted away.

The implementation of the DIP though helps to mitigate to some extend these ripples by making the logging module own the protocol instead, therefore inverting the overall dependency’s flow. Upon inversion, the loggers should adhere faithfully to the protocol hence changing accordingly and accommodating themselves to its fluctuations if one ever occurs further down the road.

In a nutshell, this shows that the DIP is, under the hood, slightly more complex than just relying on the heaps of benefits brought by a standard interface-implementation decoupling. Yes it talks about making both high and low-level modules dependent on abstractions, but at the same time the high-level modules must have ownership of those abstractions – a subtle yet relevant detail that just can’t be overlooked so easily.

As you might expect, one way that may help you more easily understand what’s actually under the umbrella of the DIP is through a few hands-on code samples. Therefore, in this article I’ll be setting up some examples so you can learn how to get the most out of this SOLID principle when developing your PHP applications.

Developing a Naive Storage Module (the missing “I” in the DIP)

There are many developers, particularly those with an aversion to the chilly waters of object-oriented PHP, who tend to see the DIP and other SOLID principles as rigid dogma that pushes hard against the language’s inherent pragmatism. I can understand such thinking, as it’s rather difficult to find pragmatic PHP examples in the wild that demonstrate the principle’s real benefits. Not that I want to play myself up as an enlightened programmer (that suit just doesn’t fit well), but it’d be useful to strive for a good cause and demonstrate from a practical standpoint how to implement the DIP in a realistic use case.

To start things off, consider the implementation of a simple file storage module. The module is charged with the task of reading from and writing data to a specified target file. At a very minimalist level, the module in question could be written like this:

<?php
namespace LibraryEncoderStrategy;

class Serializer implements Serializable
{
    protected $unserializeCallback;

    public function __construct($unserializeCallback = false)  {
        $this->unserializeCallback = (boolean) $unserializeCallback;
    }

    public function getUnserializeCallback() {
        return $this->unserializeCallback;
    }
    
    public function serialize($data) {
        if (is_resource($data)) {
            throw new InvalidArgumentException(
                "PHP resources are not serializable.");
        }

        if (($data = serialize($data)) === false) {
            throw new RuntimeException(
                "Unable to serialize the supplied data.");
        }

        return $data;
    }
    
    public function unserialize($data) {
        if (!is_string($data) || empty($data)) {
            throw new InvalidArgumentException(
                "The data to be decoded must be a non-empty string.");
        }

        if ($this->unserializeCallback) {
            $callback = ini_get("unserialize_callback_func");
            if (!function_exists($callback)) {
                throw new BadFunctionCallException(
                    "The php.ini unserialize callback function is invalid.");
            }
        }

        if (($data = @unserialize($data)) === false) {
            throw new RuntimeException(
                "Unable to unserialize the supplied data."); 
        }
        
        return $data;
    }
}
<?php
namespace LibraryFile;

class FileStorage
{
    const DEFAULT_STORAGE_FILE = "default.dat";

    protected $serializer; 
    protected $file;

    public function __construct(Serializable $serializer, $file = self::DEFAULT_STORAGE_FILE) {
        $this->serializer = $serializer;
        $this->setFile($file); 
    }
    
    public function getSerializer() {
        return $this->serializer;
    }
    
    public function setFile($file) {
        if (!is_file($file) || !is_readable($file)) {
            throw new InvalidArgumentException(
                "The supplied file is not readable or writable.");
        }

        $this->file = $file;
        return $this;
    }
    
    public function getFile() {
        return $this->file;
    }
    
    public function resetFile() {
        $this->file = self::DEFAULT_STORAGE_FILE;
        return $this; 
    }
    
    public function write($data) {
        try {
           return file_put_contents($this->file, 
               $this->serializer->serialize($data));    
            
        }
        catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
    }
    
    public function read()
    {
        try {
            return $this->serializer->unserialize(
                @file_get_contents($this->file));
            
        }
        catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
    }
}

The module is a pretty naive structure, composed of just a few basic components. The first class reads and writes data to the file system, and the second is a simplistic PHP serializer used internally to generate a storable representation of the data.

These sample components neatly do their business in isolation, and could be wired together to work in sync as follows:

<?php
use LibraryLoaderAutoloader,
    LibraryEncoderStrategySerializer,
    LibraryFileFileStorage;    

require_once __DIR__ . "/Library/Loader/Autoloader.php";
$autoloader = new Autoloader;
$autoloader->register();

$fileStorage = new FileStorage(new Serializer); 
$fileStorage->write(new stdClass());
print_r($fileStorage->read());

$fileStorage->write(array("This", "is", "a", "sample", "array"));
print_r($fileStorage->read());

$fileStorage->write("This is a sample string.");
echo $fileStorage->read();

At first glance, the module exhibits fairly decent behavior considering that its functionality allows to save and fetch a nice variety of data from the file system without much hassle. Moreover, the FileStorage class injects in the constructor a Serializable interface, thus depending on the flexibility provided by an abstraction rather than a rigid concrete implementation. With this slew of benefits shining bright on their own, what could be wrong with the module?

As usual, shallow first impressions can be tricky and blurred. Looking a bit closer, not only is it clear FileStorage actually depends on the serializer, but because of this tight dependency, storing and pulling in data from the target file is constrained to using only PHP’s native serializing mechanism. What would happen if the data must be passed along to an external service as XML or JSON? The carefully-crafted module isn’t reusable anymore. Sad but true!

The situation raises a few interesting points. First and foremost, FileStorage still exhibits a strong dependency on the low-level Serializer, even when the protocol that makes them interoperate with each other has been isolated from the implementation. Second, the level of genericity exposed by the protocol in question is very restrictive, limited to merely swapping out one serializer for another. Not only is depending on an abstraction a delusive perception in this case, but the real inversion process encouraged by the DIP is never achieved.

It’s possible to refactor some portions of the file module to make it adhere faithfully to the DIP’s mandates. In doing so, the FileStorage class would acquire the ownership of the protocol utilized to store and pull in file data, thus getting rid of the dependency on a lower-level serializer and affording you the ability to switch between several storage strategies at runtime. In doing so, you’d be actually gaining a lot of flexibility for free. Therefore, let’s continue along and see how to turn the file storage module into a true DIP-compliant structure.

Inverting Protocol Ownership and Decoupling Interface from Implementation (getting the most out of the DIP)

While there aren’t a plethora of options per se, nevertheless there are a few approaches that can be employed for effectively inverting the ownership of the protocol between the FileStorage class and its low-level collaborator while still keeping the protocol abstract. There’s one in particular, though, which turns out to be pretty intuitive because it relies on the natural encapsulation provided right out of the box by PHP’s namespaces.

To translate this somewhat intangible concept into concrete code, the first change that should be made to the module is to define a more relaxed protocol for saving and retrieving file data, that way it is easy to manipulate it through formats other than just PHP serialization.

A slim, segregated interface like the one shown below does the trick with elegance and simplicity:

<?php
namespace LibraryFile;

interface EncoderInterface
{
    public function encode($data);
    public function decode($data);
}

The existence of the EncoderInterface doesn’t seem to have a profound impact in the file module’s overall design, but it does a lot more than what it promises at face value. The first improvement is the definition of a highly-generic protocol for encoding and decoding data. The second, which is equally important as the first one, is that now the protocol’s ownership belongs to the FileStorage class since the interface lives and breaths in the class’ namespace. Simply put, we’ve managed to make still undefined low-level encoders/decoders depend on the high-level FileStorage just by writing a properly namespaced interface. In a nutshell, this is the actual inversion process that the DIP promotes behind its academic veil.

Naturally, the inversion would be a clumsy halfway attempt if the FileStorage class wasn’t amended to inject an implementer of the previous interface, so here’s the refactored version:

<?php
namespace LibraryFile;

class FileStorage
{
    const DEFAULT_STORAGE_FILE = "default.dat";
    
    protected $encoder; 
    protected $file;
    
    public function __construct(EncoderInterface $encoder, $file = self::DEFAULT_STORAGE_FILE) {
        $this->encoder = $encoder;
        $this->setFile($file); 
    }
    
    public function getEncoder() {
        return $this->encoder;
    }
    
    public function setFile($file) {
        if (!is_file($file) || !is_readable($file)) {
            throw new InvalidArgumentException(
                "The supplied file is not readable or writable.");
        }
        
        $this->file = $file;
        return $this;
    }
    
    public function getFile() {
        return $this->file;
    }
    
    public function resetFile() {
        $this->file = self::DEFAULT_STORAGE_FILE;
        return $this; 
    }
    
    public function write($data) {
        try {
           return file_put_contents($this->file,
               $this->encoder->encode($data));    
            
        }
        catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
    }
    
    public function read() {
        try {
            return $this->encoder->decode(
                @file_get_contents($this->file));
            
        }
        catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
    }
}

With FileStorage now declaring explicitly in the constructor the ownership of the encoding/decoding protocol, the only thing left to do is to create a set of concrete low-level encoders/decoders, thus allowing you handle file data in multiple formats.

The first of these components is just a refactored implementation of the PHP serializer written before:

<?php
namespace LibraryEncoderStrategy;

class Serializer implements LibraryFileEncoderInterface
{
    protected $unserializeCallback;
    
    public function __construct($unserializeCallback = false) {
        $this->unserializeCallback = (boolean) $unserializeCallback;
    }
    
    public function getUnserializeCallback() {
        return $this->unserializeCallback;
    }
    
    public function encode($data) {
        if (is_resource($data)) {
            throw new InvalidArgumentException(
                "PHP resources are not serializable.");
        }
        
        if (($data = serialize($data)) === false) {
            throw new RuntimeException(
                "Unable to serialize the supplied data.");
        }
        
        return $data;
    }
    
    public function decode($data) {
        if (!is_string($data) || empty($data)) {
            throw new InvalidArgumentException(
                "The data to be decoded must be a non-empty string.");
        }
        
        if ($this->unserializeCallback) {
            $callback = ini_get("unserialize_callback_func");
            if (!function_exists($callback)) {
                throw new BadFunctionCallException(
                    "The php.ini unserialize callback function is invalid.");
            }
        }
        
        if (($data = @unserialize($data)) === false) {
            throw new RuntimeException("Unable to unserialize the supplied data."); 
        }
        
        return $data;
    }
}

It would certainly be redundant to dissect the logic behind Serializer. Even though, it’s worth to pointing out that it not only depends now upon a more permissive encoding/decoding abstraction, but the abstraction’s ownership is explicitly exposed at the namespace level.

Likewise, we could go one step further and set out to write a few more encoders so that the benefits brought by the DIP are highlighted. With that said, here’s how another additional low-level component could be written:

<?php
namespace LibraryEncoderStrategy;

class JsonEncoder implements LibraryFileEncoderInterface
{
    public function encode($data) {
        if (is_resource($data)) {
            throw new InvalidArgumentException(
                "PHP resources cannot be JSON-encoded.");
        }
        
        if (($data = json_encode($data)) === false) {
            throw new RuntimeException(
                "Unable to JSON-encode the supplied data.");
        }
        
        return $data;
    }
    
    public function decode($data) {
        if (!is_string($data) || empty($data)) {
            throw new InvalidArgumentException(
                "The data to be decoded must be a non-empty string.");
        }
        
        if (($data = json_decode($data)) === false) {
            throw new RuntimeException(
                "Unable to JSON-decode the supplied data.");
        }
        
        return $data;
    }
}

As expected, the underlying logic that’s behind extra encoders generally resembles that of the first PHP serializer, except for any obvious refinements and variants. Additionally, the components conform to the requirements imposed by the DIP, therefore adhering to the encoding/decoding protocol defined within the FileStorage namespace.

With both high-level and low-level components in the file module depending upon an abstraction, and encoders exposing a clear dependency on the file storage class, we could safely claim that the module behaves as a true DIP-compliant structure.

Moreover, the following example shows how to bring the components together:

<?php
use LibraryLoaderAutoloader,
    LibraryEncoderStrategyJsonEncoder,
    LibraryEncoderStrategySerializer,
    LibraryFileFileStorage;    

require_once __DIR__ . "/Library/Loader/Autoloader.php";
$autoloader = new Autoloader;
$autoloader->register();

$fileStorage = new FileStorage(new JsonEncoder); 
$fileStorage->write(new stdClass());
print_r($fileStorage->read());

$fileStorage = new FileStorage(new Serializer);
$fileStorage->write(array("This", "is", "a", "sample", "array"));
print_r($fileStorage->read());

Aside from some naive subtleties that the module exposes to client code, it’s useful for making the point and to demonstrate in a pretty instructive manner why the DIP’s predicates are actually more extensive than the old “programming to an interface” paradigm. It describes and explicitly prescribes an inversion of dependencies, and as such, it should be fulfilled through different mechanisms. PHP’s namespaces are a great way to accomplish this without excessive burden, even though traditional approaches like defining well-structured, expressive application layouts can yield the same results.

Closing Remarks

In general, opinions based on subjective expertise tend to be rather biased, and certainly the ones I expressed at the beginning of this article aren’t any exceptions. There is, however, a slight tendency to overlook the Dependency Inversion Principle in favor of its more convoluted SOLID counterparts, as it’s quite easy to misunderstand it as a synonym for dependency on abstractions. Furthermore, some programmers tend to react intuitively and think of the term “Inversion” as an abbreviated expression for Inversion of Control, which, although related to each other, is ultimately a wrong conception.

Now that you know what’s really under the hood of the DIP, make sure to exploit all of benefits that it brings to the table, something that will surely make your applications a lot less vulnerable to Fragility and Rigidity issues that might eventually arise as they grow over time.

Image via kentoh/ Shutterstock

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Alex

    Alejandro,
    This was a very interesting article, although I’m afraid that most of it went over my head. I have a solid grasp on OOP, although most of my programming uses only concrete and abstract classes. I don’t find a benefit of using interfaces in most of my applications.
    Can you recommend a good book or series to help me bridge the gap from where I am to understanding the complex patterns that you are describing?
    Thanks in advance.

  • Alex Gervasio

    Hi Alex,
    Glad to know the article has been overall informative. While this may sound pretty much like coming out of the mouth of a blind worshiper of cargo cult programming (which I’m not, by the way), the truth is that interfaces are powerful beasts when it comes to sticking to Design By Contract (DBC). PHP’s quirky object model certainly won’t let you explicitly define pre/post conditions beyond simple type hinting, because of its weakly-typed nature, but at least it’ll let you define pretty clean contracts via interfaces. Thinking in terms of interfaces, either in the form of interface constructs or abstract classes packing abstract methods, before getting the hands dirty writing implementations can make programs a lot more flexible and loosely-coupled. Just to pitch a quick and dirty analogy, in TDD you first write a test, then the class that passes it. In DBC, you write the interface or contract, then the implementation.
    On the flip side, the SOLID principles are well-proven, language agnostic guidelines that will help to write more solid code. Most of them fit better with OOP, but procedural programming also may benefit from them too. If you’re interesting in peeking deeper at them, make sure to check Robert “uncle Bob” Martin’s articles http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod. It’s not a coffee break read, but definitively worth looking, trust me. Thanks!

    • Alex

      Thank you very much for the reply and for taking the time to write the explanation. I will check out Uncle Bob’s article. I really appreciate articles like this that challenge my architectural style and help me find ways to improve my craft.
      Thanks again!

      • Alex Gervasio

        Thanks. Enjoy the SOLID reading :)

  • Goran

    Thank you for a very clear explanation of DIP. Still, i have a question regarding it. Resulting set of classes include encoders tightened to LibraryFileEncoderInterface. Thats ok if encoder facility is built just for this (LibraryFile) reason. What if we need to use same encoder facility in other places too? What if there are many those places? We need to implement all those places in all of the encoders?
    What I’m trying to say is – encoder facility is too generic to depend of LibraryFileEncoderInterface. Maybe we need additional layer inbetween?

    • Alex Gervasio

      Hey Goran,
      Nice to know the post has been insightful. With reference to your question, keep in mind that the DIP or any other SOLID partner are just generic guidelines aimed at boosting your code quality. The DIP is particularly tricky, given the fact that decoupling interface from implementation is something can be achieved in a snap, while deciding where to put the abstraction might be a lot harder. Should both live in different namespaces, thus reversing the protocol’s ownership, or should both be put together side by side? Actually, there’s no a straight answer to this, as making the right choice certainly will vary from case to case.
      In generic low-level components, which are infrastructure from top to bottom, most likely the latter will be the rule of thumb to apply, just because there might exist so many clients consuming the component in question, that defining a separate interface for each of them is rather excessive and impractical (though I’ve worked myself in projects that use successfully this approach). In multi-tiered designs, where you need to define higher-level layers, such a domain model, a batch of data mappers or repositories, the former is definitively more appealing, as each lower-level layer uses an interface defined in the upper one. This is a pretty ubiquitous pattern, known as …yep, Separated Interface http://martinfowler.com/eaaCatalog/separatedInterface.html, and may help create nicely decoupled modules that can be easily reused in different contexts.
      By the way, thanks for posting that question. I think it’ll be juicy for other developers digging deeper in the DIP’s internals.

  • http://webdev.plentyinfo.com Alexander

    Hello, Alex.

    Thanks for sharing your knowledge. Could you please help me to get it for 100% right. I am confused with the following statement: “Looking a bit closer, not only is it clear FileStorage actually depends on the serializer, but because of this tight dependency, storing and pulling in data from the target file is constrained to using only PHP’s native serializing mechanism. What would happen if the data must be passed along to an external service as XML or JSON?”
    Why our class is so tightly depended on PHP’s native serializing mechanism ? We could take the first implementation of the ‘Serializer’ class, replace “$data = serialize($data)) === false” with “$data = json_encode($data)) === false”, replace “$data = @unserialize($data)) === false” with “$data = @json_decode($data)) === false” and name this class as ‘JsonSerializer’. So, there is no problem with “What would happen if the data must be passed along to an external service as XML or JSON?”

    • Alex Gervasio

      Hi Alexander
      Well, it’s nice to see how you answered your own question (a good one, by the way). If you have to get your hands dirty and refactor the implementation of FileStorage every single time you need to pass around a new encoder to it, the fact itself shows in a nutshell why the class exposes a tight coupling with a serializer. Certainly, you can pollute the class with a lot of conditionals, which could check what type of encoder has been injected and make sure to call the appropriate encoding/decoding functions in turn. Sad but true, this is nothing but a clunky solution that not violates from top to bottom the Open/Closed Principle, but it throws away the benefits that Polymorphism provides right out the box.
      The idea is to design components whose dependencies are always declared in terms of abstractions, never toward concrete implementations. The DIP not only does promote actively decoupling interfaces from implementations, but it goes a little bit further and prescribes that the interfaces should be owned by higher-level components. In the real world, it might be a little bit harder to be that dogmatic and always separate the interfaces from the implementations, as how and when this should be done depends on each particular use case. I hope that helps to clarify your doubts on the DIP. Thanks for the comments.

      • http://webdev.plentyinfo.com Alexander

        It seems that my previous comment was too short to convey my idea absolutely clear. Sorry for that and thx for the answer.
        As far as I understand, you decided that I offer to pass to the ‘FileStorage’ class constructor
        classes derived from different interfaces (‘Serializable’ and ‘EncoderInterface’).
        But what I meant is to refuse from ‘EncoderInterface’ at all and use only ‘Serializable’ instead.
        The main idea that ‘Serializable’ is not coupled to the ‘serialize’ function and we can easily use it instead of ‘EncoderInterface’.
        Am I right that the only (but not unimportant) goal to declare the ‘EncoderInterface’ interface is to append it to the ‘LibraryFile’ namespace and commit
        the encoding/decoding protocol within namespace of the higher level module ?

        Maybe source code example will illustrate my words better: http://goo.gl/YCvNK

        Thank you for your time and attention.

        • Alex Gervasio

          Alright, Alexander I now see clearly your point. In fact, you can have FileStorage relying on any implementer of Serializable and that’s all well and fine. It’s just a matter of personal taste (consider it pretty much as a picky facet of me) though, but I’d be rather reluctant to pick up this approach on behalf of the “mental sanity” of the next guy peeking at my code.
          Since I always try to be nice with the principle of least astonishment, I don’t like to define, or reuse in this case in particular, an interface whose contract can be potentially misleading. The serialize/unserialize duet should always… well, serialize/unserialize data, with less or more bells and whistles sure, according to the functionality of a specific implementer. JSON and XML encoders are, to my view, slightly different creatures.
          In the last instance, everything boils down to using a userland protocol, owned by FileStorage, or a native one, which lives and breaths in the global namespace. It’s up to you to choose the path that best fills your bill.
          Also, thanks for posting the extra code snippets; they should be instructive for anyone wanting to dig deeper into the DIP’s inner workings.

  • Galen

    “The second, which is equally important as the first one, is that now the protocol’s ownership belongs to the FileStorage class since the interface lives and breaths in the class’ namespace.”

    I’m not sure what you mean by this. You could easily put the EncoderInterface in It’s own namespace, throw a use statement in the FileStorage class and nothing would change…correct?