Managing Class Dependencies: An Introduction to Dependency Injection, Service Locators, and Factories, Part 1

This entry is part 1 of 2 in the series Managing Class Dependencies: An Introduction to Dependency Injection, Service Locators, and Factories

Managing Class Dependencies: An Introduction to Dependency Injection, Service Locators, and Factories

Let’s face it: for good or bad, OOP has been actively drilling deep holes in the soil of PHP in the last few years, hence its ubiquitous presence now is anything but breaking news. Furthermore, while this steady incremental “objectification” in the language’s terrain is generally considered a beneficial shift towards new and more promising horizons, for those still rooted in the procedural paradigm, the process has been far from innocuous.

There’s a few solid arguments that sustain this mindset: first and foremost the nature of OOP is awkward and inherently complex, plagued with nuance that can take literally years to tame. Also, defining the APIs that OO components use for interacting with one another can be quite a burden at times, especially when it comes to designing systems whose backbone rests on facilities provided by large and tangled object graphs.

From a programmer’s perspective, the process of designing easily consumable APIs while still keeping a decent level of decoupling between the involved classes is always a tricky trade-off, as the more expressive the dependencies are, the harder the work is that needs to be done behind the scenes for wiring up the collaborators in their proper sequence. This fact itself is a dilemma that has plagued the minds of developers from long ago, which is certainly harder to digest as time goes by. With applications becoming increasingly bloated, housing inside their boundaries a growing number of classes, the construction of clean, declarative APIs isn’t just a loose practice anymore that can eventually be pushed into the language’s mainstream from time to time. It’s simply a must.

This raises a few interesting questions: how should a class be provided with its dependencies without cluttering its API, or even worse, without coupling it to the dependencies themselves? Just by appealing to the benefits of Dependency Injection? Through the reigns of an injected Service Locator?

For obvious reasons, there’s no a one size fits all solution to the problem, even when all of the aforementioned approaches have something in common. Yes, to some extent they all make use of some form of Inversion of Control, which not only allows you to decouple the dependencies from the class but also keeps a neat level of insulation between the dependences’ interfaces and the implementation, making mocking/testing a breeze.

Evidently, the topic is a world away from being banal, and as such it deserves a close, in-depth analysis. In this two-part series I’ll be doing a quick roundup of some of the most common methodologies that can be employed for managing class dependencies in modern application development, ranging from using service locators and injectable factories, to sinking your teeth into plain vanilla Dependency Injection, and even implementing a naive Dependency Injection Container.

A (Hopefully) Vanishing Plague – “new” Operators in Constructors

The mantra is somewhat old, sure, but its claim still rings loud and clear: “placing ‘new’ operators in constructors is just plain evil.” We all know that now, and even take for granted that we’ll never commit such a cardinal sin. But this was literally the default method used for years for a class to look up its dependencies before Dependency Injection reached the PHP world. For the sake of completeness, let’s recreate this clunky, old-fashioned scenario and remind ourselves why we should get away from such a harmful plague.

Say that we need to build a simple file storage module which must internally consume a serializer in order to read and write data to a specific file. The implementation of the serializer would look something like to this:

<?php
namespace LibraryEncoder;

class Serializer implements Serializable
{
    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 unserialized must be a non-empty string.");
        }
        if (($data = @unserialize($data)) === false) {
            throw new RuntimeException("Unable to unserialize the supplied data."); 
        }
        return $data;
    }
}

The Serializer class is just a lightweight implementation of the native Serializable interface and exposes the typical serialize/unserialize duet to the outside world. With this contrived strategy class doing its thing as expected, the aforementioned file storage module could be sloppily coded as follows:

<?php
namespace LibraryFile;
use LibraryEncoderSerializer;

class FileStorage
{
    const DEFAULT_STORAGE_FILE = "data.dat";
    protected $serializer;
    protected $file;
    
    public function __construct($file = self::DEFAULT_STORAGE_FILE) {
        $this->setFile($file);
        $this->serializer = new Serializer();
    }
    
    public function setFile($file) {
        if (!is_file($file)) {
            throw new InvalidArgumentException(
                "The file $file does not exist.");
        }
        if (!is_readable($file) || !is_writable($file)) {
            if (!chmod($file, 0644)) {
                throw new InvalidArgumentException(
                    "The file $file is not readable or writable."); 
            }
        } 
        $this->file = $file;
        return $this;
    }
    
    public function read() {
        try {
            return $this->serializer->unserialize(
                @file_get_contents($this->file));
            
        }
        catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
    }
    
    public function write($data) {
        try {
            return file_put_contents($this->file,
                $this->serializer->serialize($data));
        }
        catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
    }
}

The behavior of FileStorage boils down to just saving and pulling in data from a predefined target file. But the seemingly benign nature of the class is nothing but an illusion, as it blatantly instantiates the serializer in its constructor!

This form of “dictatorial” dependency lookup not only introduces a strong coupling between the class and its dependency, but it condemns testability to a quick death. The following code shows how the artifacts ripple through to client code when using this approach:

<?php
$fileStorage = new FileStorage();
$fileStorage->write("This is a sample string.");
echo $fileStorage->read();

Feel free to run the code and it’ll work like a charm, that’s for sure. But we know the coupling is there just beneath the surface, not to mention the fact that there’s not a single clue about if the FileStorage has an internal dependency on another component, or how this one was acquired. Definitively, this is pretty much like an antipattern which goes against the grain when it comes to defining clean, expressive class APIs.

There’re a few additional approaches that are worth looking into that can improve the way the two previous classes interoperate with each other. For instance, we could be a bit bolder and inject a factory straight into the internals of FileStorage and let it create a serializer object when necessary, thus making the class’ dependency a little more explicit.

Deferring the Creation of Dependencies through an Injected Factory

Factories, in any of their forms, are nifty elements that allow us to nicely distill object construction from application logic. While such ability is quite remarkable in itself, it can be taken a bit further when mixed with the goodness of Dependency Injection.

If you’re the curious sort like I am, you’ll be wondering how to exploit the benefits provided by a factory for enhancing how the earlier FileStorage class looks up its collaborator, getting rid of the infamous “new” operator in its constructor. At a very basic level, the lookup process could be reformulated through the following factory class:

<?php
namespace LibraryDependencyInjection;

interface SerializerFactoryInterface
{
    public function getSerializer();
}
<?php
namespace LibraryDependencyInjection;
use LibraryEncoderSerializer;

class SerializerFactory implements SerializerFactoryInterface
{   
    public function getSerializer() [
        static $serializer;
        if ($serializer === null) {
            $serializer = new Serializer;
        }
        return $serializer;
    }
}

Having at hand a dynamic factory charged with the task of creating the serializer on demand, now the FileStorage class can be refactored to accept a factory implementer:

<?php
namespace LibraryFile;
use LibraryDependencyInjectionSerializerFactoryInterface;

class FileStorage
{
    const DEFAULT_STORAGE_FILE = "data.dat";
    protected $factory;
    protected $file;
    
    public function __construct(SerializerFactoryInterface $factory, $file = self::DEFAULT_STORAGE_FILE) {
        $this->setFile($file);
        $this->factory = $factory;
    }
    
    public function setFile($file) {
        if (!is_file($file)) {
            throw new InvalidArgumentException(
                "The file $file does not exist.");
        }
        if (!is_readable($file) || !is_writable($file)) {
            if (!chmod($file, 0644)) {
                throw new InvalidArgumentException(
                    "The file $file is not readable or writable."); 
            }
        } 
        $this->file = $file;
        return $this;
    }
    
    public function read() {
        try {
            return $this->factory->getSerializer()->unserialize(
                @file_get_contents($this->file));
        }
        catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
    }
    
    public function write($data) {
        try {
            return file_put_contents($this->file,
                $this->factory->getSerializer()->serialize($data));
        }
        catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
    }
}

The functionality of FileStorage remains pretty much the same, but the tight coupling with the serializer has been broken by injecting an implementer of the SerializerFactoryInterface. This simple twist turns the class into a flexible and testable creature, which exposes a more expressive API, as is now easier to see from the outer world what dependencies it needs. The code below shows the result of these enhancements from the perspective of the client code:

<?php
$fileStorage = new FileStorage(new SerializerFactory());
$fileStorage->write("This is a sample string.");
echo $fileStorage->read();

Let’s not fall into blind, pointless optimism, though. It’s fair to say the refactored implementation of the storage module looks more appealing; this approach makes it trivial to switch out different factory implementations at runtime. But in my opinion, at least in this case, it’s a masked violation of the Law of Demeter since the injected factory acts like an unnecessary mediator to the module’s real collaborator.

This doesn’t mean that injected factories are a bad thing at all. But in most cases they should be used only for creating dependencies on demand, especially when the dependencies’ life cycles are shorter than that of the client class using them.

Closing Remarks

Quite often unfairly overlooked in favor of more “seductive” topics, managing class dependencies is unquestionably a central point of object-oriented design and whose underlying logic certainly goes much deeper than dropping a few “new” operators in factories, or worse, in stingy constructors that hide those dependencies from the outside world so they can’t be properly decoupled at will or tested in isolation.

There’s no need to feel that all is lost, however, There exists a few additional approaches that can be utilized for handling in an efficient manner the way that classes are provided with their collaborators, including the use of the Service Locator pattern, raw Dependency Injection, and even appealing to the benefits of a Dependency Injection container. All these ones will be covered in detail in the follow up, so stay tuned!

Image via SvetlanaR / Shutterstock

Managing Class Dependencies: An Introduction to Dependency Injection, Service Locators, and Factories

Win an Annual Membership to Learnable,

SitePoint's Learning Platform

  • http://jeunito.me Jose

    Great article! Keep ‘em coming especially the ones on design. Your articles make me think all the time. I liked how you injected a factory into the class and gave it the ability to change its dependency implementation at runtime. Never thought of that one before. However at least for the dependency, I think you are not doing any injecting at all. You injected a factory yes but the way the actual dependency is managed is not anymore more through dependency injection but rather through a factory inside the dependent class. It’s the class now that’s injecting to itself rather than an outside injector injecting dependencies into the class. What do you think?

    • Alex Gervasio

      Hi Jose,
      Glad the post has been overall informative. I like your question on the injectable factory, cause it’s really a good one. In fact, the approach does stick to the formalities of dependency injection, only that the factory is in itself a dependency. Though not precisely prolific, the use of injectable factories is particularly appealing is those cases where the dependencies have shorter lifecycles than the class that houses them, thus deferring their creation until the very last minute. For obvious reasons, the downside of this rest on the fact that you’re using a mediator to get the actual dependency, an issue also exposed by a service locator, be it static or dynamic. So, is this a somewhat furtive form of breakage of the Law of Demeter? In a sense, it is. But, it’s a sort of tradeoff you have to learn to live with, specially if you want to exploit its benefits.
      Thanks for the feedback.

  • http://jeunito.me Jose

    I worked in the Java world before heading over to PHP and back there, dependency injection is old news. It’s slow to gain traction in PHP though and the fact that it just gets attention nowadays is a bit sad if you ask me.

    • Alex Gervasio

      Better getting late to the party than never :)

  • Kise S.

    Hi thanks for the article,
    can you please write an article about how to make a plugin/hook system with classes so that it doesn’t need to be changed manually? using standard way

    right now i use hacks to implement my system where i modify the code to add injection point so that other hooks can register and listen to event and add code, but i think its kinda pointless if i have to modify the code it self to add injection points, im still thinking about my next project so im all ears for new ideas about how to extend codes without having to modifying them, that will make auto-update system easier, where the user just click and wola the whole core system updated without breaking their websites

    • Alex Gervasio

      Hey,
      Thanks for the comments. To be frank, writing such article isn’t something I have in mind right now, but just based on the batch of issues you’re describing above, I guess you need to put in practice the old venerable “Programming to Interfaces” principle, which rests on the foundations of Polymorphism. Regardless of the nature of the system you’re implementing, be nice and make your classes depend upon abstractions (interfaces, abstract classes or both), instead of on concrete implementations. In doing so, you can easily create different implementers of the interfaces in question, without having to amend a single pinch your existing code, thus making it easily pluggable and extendable (AKA open for extension, closed for modification). Just make sure to stick from top to bottom to that approach and things will flow like a charm, trust me.

  • Dean

    Thanks for the article. Apparently, I’ve been doing the old school way. I’ll definatly rectify that in my future projects. Did you mean to type hint SerializerFactory and not SerializerFactoryInterface on line 11 of your file storage class, or am I missing something?
    Thanks,
    Dean

    • Alex Gervasio

      Hi Dean,
      Thanks for the insights. In fact, the file storage class types hint the factory interface. That way, it’s not coupled to any implementation in particular.

  • Daniel Lowrey

    I only made it to the first example before I had to comment. I’m not sure about what comes after it, but …

    Creating a `Serializer` implementation that only exists to serialize external data is a bastardization of OOP. The point of `Serializable` is just what it sounds like: to allow easy serialization of an existing object. A class that implements `Serializable` needs to be serialized; a `Serializer` does not.

    • Alex Gervasio

      Daniel,
      That’s only your opinion, which though respectable, to my view is wrong. The point of Serializable, just like any other interface that might exist out there, either native or defined in userland is to provide a contract. Period. From that point onward, whatever you want to do with it, or what implementers you wish to build up on top of it is entirely up to you. Even when the practice of using Serializable to create serializable classes is certainly ubiquitous in PHP, it doesn’t mean if I ever use it with other implementers I’m just being a sinner with OOP. Thanks for the feedback.

      • Daniel Lowrey

        I don’t have a problem with your logic regarding interfaces, but `Serializable` exists to make objects that need to be serialized work with calls to `serialize()` and `unserialize`. Your Serializer class doesn’t itself need to be serialized or unserialized; it’s not serializable, so it shouldn’t implement the `Serializable` interface. Doing so only uses part of the standard library in a way that’s misleading.

        If you aren’t serializing/unserializable Serializer instances then the Serializer class shouldn’t implement `Serializable`:

        “`
        $obj = new Serializer;
        $serialized = serialize($obj);
        $newSerializer = unserialize($serialized);
        “`

        • Alex Gervasio

          Daniel, I get your point. While not showcased in this first installment, I’m just using the contract provided by “Serializable” to assure any implementation injected into FileStorage exposes the “serialize/unserialize” methods. And yep, in perspective this might be potentially misleading, considering the purpose of Serializable. A userland interface should fit better. Thanks for the opinions.

    • Gordon

      I agree with Daniel. One important aspect of OOP is deciding what is something, and that has something (is_a versus has_a). A lot of programmers, especially PHP programmers subclass inappropriately (Just look at how many Zend projects have all their models inherit from Zend_Db_* if you don’t believe me!), but you’ve got to remember the semantics of your objects. Every object has relationships with other objects (has a), but equally they also have characteristics that make them what they are (is a). If an object needs to be serialised, then that means that it is a serialisable object, and it should implement the appropriate interfaces. Then you can pass your object into anything that expects to be able to serialise the provided object, making for a very flexible design.

      I’m also not sure the factory should be enforcing the single instance constraint, it feels a little too close to Singleton for comfort for me.

      I think the solution to that one is also something that would address the LoD concerns. Have the factory’s getSerializer() method do nothing more than new Serializer, and have a $serializer private property in your consumer class and a getSerializer () method that does the lazy instantiation through the factory. Now you can replace the $factory -> getSerializer() calls with $this -> getSerializer() calls instead. As you’re now accessing a property of the object itself instead of a property of the factory, the LoD is satisfied (except in one place, the getter method that uses the factory to instantiate the serializer).

      • Alex Gervasio

        I agree with the semantic-related concepts, sure. In this case, however, we’re far away from debating the “is-a/has-a” dilemma, which by the way is never present in the examples. The point is that if you’re using an implementer of Serializable, client code can rest assured it’ll be consuming an object that conforms to the “serialize/unserialize” contract. It’s really that easy. Of course, if you want to appeal to a user-defined interface that describes more semantically the nature of the contract, rather than a native one, nobody will blame you for that :)
        Regarding the factory’s implementation, yep, your approach looks slightly better in the sense it doesn’t walk on the dangerous Singleton edge, even though it delegates the control of the instantiation of the serializer straight to FileStorage. Pretty ugly, as now the class has too many responsibilities to my taste, not to mention it violates the LoD as well.
        Thanks for the opinions. Agreed or not, they’re valuable, trust me.

  • http://www.andrejfarkas.com Andy

    Hmm, I don’t get the use of factory here. Why do you make the FileSotrage depend on factory and not on Serializer itself? I mean you can as well pass an instance of Serializer to FileStorage constructor.
    I think the factory is better used in cases where the object itself returns an instance of some other class. The object would then ask an injected factory to build such a new instance for it and return that one.
    Just can’t see the benefits of using factory in your particular example.

    • Alex Gervasio

      As pointed out in the post, the role of the factory is to defer the instantiation of the dependency. No more, no less. Naturally, at first blush this seems to be rather pointless with the FileStorage class, but in some specific use cases, this does make sense. So, don’t get ahead of yourself, as the follow up also shows a File Storage implementation, where the serializer is directly injected into the class, without no mediators in between. Thanks.

  • Jesper

    Hi Alex!
    I found the article enjoyable to read and am looking forward to see further parts of the series.
    A question I have is, in the constructor of the new FileStorage class, would it break the OOP pattern or be considered bad practice to do like this:

    public function __construct(…){

    $this->factory = $factory;
    $this->serializer = $this->factory-> getSerializer();
    }
    Thanks for an interesting article! :)

    • Alex Gervasio

      Hey Jesper,
      Glad to know that. In regards to your question, well, assigning the serializer and the factory to class members is simply pointless. Either you should inject the former or the latter, but never both. Thanks for the comments.

  • Boabramah Ernest

    The who has sin and who has not sin is making me confuse. I am now starting to grasp the whole concept about dependency injection and am begining to get it from tutorials like this. I hope you get the time to continue the series. I will like you to also address how we can test the classes. Thanks alot and will be coming back

    • Alex Gervasio

      Hey Ernest,
      Nice to know the article helped you start treading the DI path on the right foot. The second part does cover a few additional concepts, like plain DI, service locators and DICs, but for the sake of brevity, it stays away from showing how to test the sample classes in isolation. Nevertheless, if you’re already get along with PHPUnit, or with any other unit test framework available out there supporting stubs/mocks, this shouldn’t be an issue at all. When hooked it up to Programming to Interfaces (hence Polymorphism) DI allows to decouple classes from their dependencies and those dependencies from the concrete implementations.
      Thanks for stopping by and dropping your comments.