Dependency Injection, Services and Containers (DIC) – part 4

 

If you have read my previous posts. Maybe the terms Inversion of Control, Dependency injection, DI Containers and Service Locator means anathema, so this article doesn’t suite you.

If you think the whole DI idea is a hoax, no problem. Enjoy the other millions patterns that the OOP world offers.

For people that understand the matter, I would love to hear everything you need to say about it. As I said in my first post: The PHP community makes that language so fun to deal with. I look forward to increase my knowledge and share my own. The general idea of this blog.

Note to the newbies – I won’t give an exact implementation of a problem you’ve faced – I’m not StackOverflow and I don’t intend to be. Yeah I’ll try to help and explain if you have questions about the subject but again, I am not a guru 😉

Dependency Injection Series

DI Container vs Service Locator

So let me continue where I left – where and who will take care of that nasty dependencies. In my second post I’ve said:

Common criticism is that DI hides that complexity and creates a new layer between the parts of the application, so what? It’s better to have a place where you can control these dependencies than let them fly around.

DI Container – A class needs a dependency! Let me give it.

Service Locator – A class needs a dependency! Which one?

This is the main difference.

Look at the example code block below:

class User
{
    public function login()
    {
        // code for a login action
        
        // Log this to a file
        $logger = new Logger();
        $logger->log();
    }
}

So we have a user, that when he logs with his credentials, the User objects instances the Logger class and calls its log() method. If you are following me you already know that is a tight coupled code, and it doesn’t work, and in order to follow the best practices we need an Interface. Lets refactor that and do a DI with a Service Locator and a Container.

NOTE: All the external libraries are not shown, for the sake of simplifying the examples.

Service Locator Example

class ServiceLocator
{
    public $service;
    public static function set(array $instance)
    {
        $this->service = $instance;
    }
    public static function get($key)
    {
        return $this->service[$key];
    }
}

$logger = [
    'Logger' => new Logger(),
];
ServiceLocator::set($logger);

class User
{
    public function login()
    {
        // code for a login action
        // Log this to a file
        $logger = ServiceLocator::get('Logger');
        $logger->log('log.txt');
    }
}

So just to explain the previous code example. We have a ServiceLocator, that in order to pass a dependency we need to register it (set it) first. And that’s the general idea. A Service Locator acts like a container and passes the dependencies to whomever calls them. The User class doesn’t care what interface must the logger extend, or w/e … Just needs an instance of that class and wants to log it. Period.

For some users that is quite alright, and enough. But I don’t like that technique. The only explanation is that I don’t cope with its flow. For me is more natural the next approach:

DI Container Example

use App\Engine\Container;

class User
{
    private $logger;

    public function login(LoggerInterface $logger)
    {
        $this->logger = $loger;
        // code for a login action
        // Log this to a file
        $this->logger->log('log.txt');
    }
}

In this case the user class receives the Logger instance from the outside world. The App\Engine\Container will take care of the initialization of the logger before passing it down to the User->login(); method. This is the essence of the Dependency Injection pattern.

Martin Flower states:

With service locator the application class asks for it explicitly by a message to the locator. With injection there is no explicit request, the service appears in the application class – hence the inversion of control.

This is why some people doesn’t like DI and other prefer it. DI hides the dependencies when using Autowiring. I think it’s good to explain autowiring. What is it, and what it does.

If you haven’t heard about PHP-DI this is the link you must check, and in short as they state: The Dependency Injection Container for Humans. So in this library they’ve made an awesome feature called autowiring. And I’ll try to help with their explanation.

Autowiring is an exotic word that represents something very simple: the ability of the container to automatically create and inject dependencies.

Cool right? Well … It has its pros and cons, but for me this is the way to inject with my framework.

More about autowiring – In order to achieve that the container uses the PHP Reflection to detect what parameters the class (constructor or setter) waits. Super simple? Right. Sometimes it’s a good thing, sometimes not … The next paragraph tries to help you with selecting a Service Locator or DI-Container.

Service Locator vs DI-Container. Deciding which to use

These next paragraphs will try to explain which to use.

TL;DR It does not matter. Yeah a Service Locator isn’t the best practice here, but if you master it – surly there’s a benefit.

The first point is that both implementation provide the fundamental decoupling that’s missing in the native example – in both cases application code is independent of the concrete implementation of the service interface. The important difference between the two patterns is about how that implementation is provided to the application class.

Am I repeating myself? Yeah. With service locator the application asks for it explicitly by messaging the locator, and with the injection – the service appears in the class – hence the inversion of control.

I’ve talked and talked and I’m not sure that I’ve stated somewhere what is the general definition of Inversion of control – so here it is:

In software engineering, inversion of control (IoC) is a design principle in which custom-written portions of a computer program receive the flow of control from a generic framework. A software architecture with this design inverts control as compared to traditional procedural programming: in traditional programming, the custom code that expresses the purpose of the program calls into reusable libraries to take care of generic tasks, but with inversion of control, it is the framework that calls into the custom, or task-specific, code.

Inversion of control is used to increase modularity of the program and make it extensible, and has applications in object-oriented programming and other programming paradigms.

So IoC is a common feature of frameworks, because the human mind thinks like that – procedural. The computer is at its fastest when you pass procedural commands and functions. But procedural code is a nightmare to support, and once trained a PHP developer’s mind can relate to objects.

Martin Flower’s opinion

The problem behind IoC comes at a price. It tends to be hard to understand and leads to problems when you are debugging. So M. Flower states:

So on the whole I prefer to avoid it unless I need it.

The key difference is that with a Service Locator every user of a service has a dependency to the locator. And the locator can hide dependencies to other implementations, but you need to see the locator. So the decision between the two depends on how much of the dependency is a problem.

Using DI can (especially constructor-) can help make it easier to see what the component dependencies are. With DI-Container you can just look at the injection mechanism and see them. With the service locator you have to search the source code for calls to the locator.

Get the following example. You have an API, and this API provides something to a number of custom applications. Some kind of a service architecture:

service-locator-example

 

Here the idea is to provide a Service Locator to pass dependencies to your clients. You have a service locator that holds data for a weather API. Your clients needs a dependency from it, and you can mix their dependencies with yours. With the __set and __get magic methods you can get all the needed data.

You cannot do that with a DI Container, because you cannot store your classes into the Container. Hence you cannot complete the modulation.

One of the reasons people prefer dependency injection is that it makes testing easier. And to do testing, you need to easily replace real service implementation with stubs or mocks.

And the primary issue is for people who are writing code that expects to be used in applications outside of the control of the writer. In these cases even a minimal assumption about a Service Locator is a problem.

Fabien Potencier’s opinion

Most of the time, you don’t need a DI Container to benefit from Dependency Injection

But when you manage a lot of different objects with a lot of dependencies, a DI Container can be really helpful.

DI Container is an object that knows how to instantiate and configure objects. And to be able to do its job, it needs to know about the constructor arguments and the relationship between the objects itself.

So Mr. Potencier in his article uses a Service Locator. I could copy-paste it, but generally a Service Locator is not that simple to achieve. But one of the things that are absolute true and I quote him:

Of course, creating and maintaining the container class by hand can become a nightmare pretty fast. But as the requirements are quite minimal for a container to be useful, it is easy to implement one.

This is something that you need to think about when deciding if you have to use Service Locator, DI Container or just to ignore this technique.

The Containers / Locator in the Net

So let me give a list and opinion about which container I find attractive to my needs.

First of all check this article here. The PHP Recommendation Standard are voting for DI Containers interface, so this is a huge step.

Second of all check a fellow’s Bulgarian Developer blog article for another good explanation on the topic, and of course source files. He is a great Front End Engineer and a powerful teacher.

Check Jean Bruenn’s blog especially this article for a custom build DI Container, followed with explanations.

Almost every modern framework has and uses the DI pattern, and many of them uses a Container, a Locator or both.

Laravel is one of the examples. The creator of the FW has implemented in its latest version this technique. For me Laravel is powerful, but not that good if your application has a lot of load. Then with L5 you’ll have a big problem. But I love if for small to middle-sized applications. The flow of Laravel is so natural.

Slim on the other hand uses a variation of Pimple. It’s good and fast, as Slim FW has to be. Link

Jeremy Lindblom is a PHP community leader. On this page you can check it’s implementation of the Container. Frankly I’ve never used it, and don’t have a opinion. But that will change.

PHP DI looks promising, but I’ve faced one major problem with it. If you use the autowiring – and a lot of setter injections, for a larger application the maintenance becomes a horror movie.

Dice is cool. Easy to understand – but when PSR-11 is accepted – will not be used.

Pimple is mostly a Locator, but if you wish to use it – it’s easy and fast. KNP university has a good explanation how does Pimple work – here.

Conclusion

As Richard Miller states

To get the benefits of Dependency Injection you need to keep pushing the creation of dependencies up through the code until it reaches the top. Once this is done a Dependency Injection Container helps to manage the resulting complicated configuration code as well providing performance benefits over directly creating the classes.

Just try to understand one thing. If this technique seems like a new and frightening stuff to you. Just don’t use it. Yeah play with it. Understand it. But like all the other patterns, seems hard at first, but in reality if it makes a nightmare to remember, just ignore it.

Advertisements

Give your opinion

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s