Zend Framework 2.0: Dependency Injection (Part 2)
In Part 1 of this miniseries, I expounded (it’s better than exploding) about Dependency Injection (DI) and Dependency Injection Containers (DICs). To summarise, DI is an obvious and ubiquitous design pattern used daily by most programmers to allow objects accept their dependencies from an external agent (e.g. a unit test which needs to inject mock objects). In an application, the ideal external agent is some container that can assemble objects on demand and create the necessary object graph from scratch outside of the application’s control flow. It is this object assembly function that can be fulfilled by a DIC.
For Part 2, we’re going to dig more into what a DIC is and isn’t. I’ve already noted one very simple DIC called Pimple which will continue as one of my reference points since it best illustrates just how simple a DIC can be. In Part 3, we’ll (finally) turn our attention to some actual source code. Baby steps. Parts 1 and 2 should get you thinking so that ZF 2.0’s DIC is a lot easier to understand and critique. We don’t want anyone panicking just by throwing them into the deep end ;).
Make sure to read Part 1 if you haven’t already! 😛
Things Which Are Not A Dependency Injection Container (DIC)
Now, in explaining a DIC it’s worth noting there are related solutions which you should find very familiar.
You could use lots of Factories (classes or methods on a class in which the logic necessary to create an object is packaged for reuse). If you followed Part 1, you’d soon realise that the Pimple DIC, as simple as it is, looks very much like a collection of Factory Methods (as Closures). Zend_Application also appears to use Factory Methods or Classes to generate its resources. Along this line of thinking, a DIC is a container of executable Factories. These tend to be the simplest kind of DICs since they rely on source code instructions which can be combined in sequence to assemble a final dependent object and its injected dependencies.
The differences however all come back to the concept of an “external agent”. Factories are traditionally executed within and by an application object whereas a DIC operates from outside an application. Since this is a simple inversion of control, DICs following this mechanism can be written over a coffee break since it’s just a matter of aggregating and mixing Factories – or you can just standardise on Pimple ;).
Another possible solution is to use a Service Locator. This is often interpreted as an object which can create and retrieve objects. The Service Locator is injected into dependent objects as needed so they can lookup their own dependencies. For example, in our earlier Leprechaun class example from Part 1, we could have created a Service Locator capable of creating Pot classes, injected it into Leprechaun, and allowed the Leprechaun class to lookup whatever Pot it needs.
This too looks very similar to how a DIC appears to operate. It’s also similar to a Factory Class except it can construct many kinds of objects instead of just one particular type. A Pimple container, for example, can be passed into other objects which in turn can ask it to retrieve necessary objects or dependencies. The same holds true of Zend_Application’s resulting bootstrap object. So, in yet another line of thinking, a DIC is always a potential Service Locator – the difference is that we generally don’t inject DICs into dependent objects but allow the DIC create the dependent object from the outside (i.e. it’s an external agent).
In summary, a Dependency Injection Container is not simply a standalone invention – it’s a combination of a few well known patterns we use in PHP that, when combined, create something greater than its individual (and obvious) parts when acting solely as an external agent. That helps explain why most DICs you look at feel a bit too complex. You might see the simple task it performs but not quite grasp why it needs to be so complex until you realise it’s a combination of patterns. We’re used to seeing the constituent patterns isolated and scattered across our application – not brought together in one single entity.
The primary differentiating factor from the simpler patterns, especially Service Locators, lies in one simple concept: Is our DIC truly an external agent? All other solutions tend to require the container to be an internal agent, i.e. a dependency of other objects. Service Locators are injected into dependent objects, and Factories are called from dependent objects.
External Agents See The Bigger Picture
The ideal DIC is an independent external agent. I use the term “external agent” a lot because it’s a good description that’s easy to grasp. The idea is that the DIC creates all other objects, and their dependencies, and will inject the correct dependencies into the right dependent objects. In other words, it’s a master manipulator orbiting our application but not actually embedded in it. In a framework, it would be used to create almost everything without the rest of the framework even being aware of its existence. Nearly all other possible solutions can’t operate in this fashion. A Service Locator must be embedded into other classes and Factories are all called from within other classes too, i.e. they are internal agents…not external.
Now, programmers have two fundamental questions when it comes to basic OOP:
1. Where do I create objects?
2. How do I transport objects across application layers?
DICs answer the first question. You can create objects using a DIC which is independent of the application. It’s our external agent. They also answer the second question. In applying Dependency Injection, your DIC knows how to inject dependencies into the objects needing them, even if those objects come from different layers of the application. This should render the need for Service Locators, Factories, and the always popular Registry pattern almost defunct.
This is what makes the concept of Dependency Injection and DICs useful in frameworks. If you’ve ever used Zend Framework before the arrival of Zend_Application you’ll be familiar with the two questions from above. Creating and transporting objects was an unanswered question at the time, with users running in all directions using Registries, Service Locators, In-Controller instantiation and bootstrap instantiation (mixed with non-static Registries and the handy FrontController parameter transport) – and that’s just the generic groups. In reality, people developed dozens of varying implementations on these themes. This lack of consistency was an irksome problem. By implementing good DICs, both Symfony 2 and Zend Framework 2.0 have settled on one consistent direction.
However, using a DIC in its designed role requires something of a leap of faith. PHP programmers use Service Locators, Factories and Registries all the time. We’re comfortable with those patterns and many of us will always crave their simple natures even when we understand why Dependency Injection is a better solution. This craving can end up corrupting the idea of DI by implementing a common antipattern: turning the DIC external agent into an internal agent.
DICs As Dependencies Are Evil: They Are Not Service Locators
For example, bearing in mind my earlier creation/transportation question duo. Let’s say we create a NewsletterController in a Zend Framework 1.x application using a DIC like Pimple. Our Controller requires an instance of Zend_Mail for its emailAction method. How do you get the Zend_Mail object into the Controller? Well, using Dependency Injection the answer is very obvious – you define either a setter, a constructor parameter or a public property on the Controller class. Then you can program Pimple to a) create the Zend_Mail instance, b) create the Controller and c) inject the Zend_Mail instance into the Controller (e.g. using a setter method). Creation and transportation are neatly solved. The best part is that none of the participants are aware of the DIC, the acid test being that anything a DIC can do, you could have done it by hand without a DIC (hint: a DIC can replace a lot of what ZF users would normally call bootstrapping).
Both Zend Framework 2.0 and Symfony 2 optionally allow another possibility. When we create the NewsController, we can inject the DIC into the Controller itself. This would allow the Controller to lookup resources from the DIC instead of the DIC injecting them from outside the Controller, i.e. our external agent just became an internal agent.
The switch may appear very convenient and comforting. Instead of all this running around with a DIC magically creating Controllers, you could have a typical Dispatch/Execute cycle and add the code for object creation/retrieval into your Controller actions. This has benefits – object creation is clearly visible in all your controllers. However, your intuition is slightly off base.
Firstly, this isn’t Dependency Injection. Our dependent objects have now internalised dependency creation. Since it’s not DI, a DIC is obviously a misplaced tool. So instead of a DIC, you are actually mutating it into a Service Locator. The side effects of eliminating DI are to make it harder to understand dependencies (not easier as you might suspect!). A Service Locator just needs a name to lookup – there are no setters or constructor parameters with typehinting or useful Docblocks to refer to. So your intuition was wrong – it might make your life easier, but everyone else who lacks your familiarity with the source code you’re writing will spend a lot of time dissecting your DIC to figure out what the concrete dependencies really are. Yes, it’s the age old justification for many practices these days – ensuring the long term costs of change are minimised.
Secondly, it creates objects which are useless without the specific DIC interface it depends on. Given an application tends to use only one DIC, importing classes from other sources which need a completely different DIC in order to work is a PITA. Classes which don’t need to be framework specific MUST NEVER be framework specific (i.e. frameworks all use different DICs and depending on one excludes using your classes with another). If classes are DIC specific – you have done something horribly wrong in practicing OOP (and by extension DI).
This is very similar to my past arguments as to why my idea of Zend Framework Modules does not include Libraries/Components. Apples and oranges. Putting a generic library into any form of framework specific packaging or tying it to a framework specific import/DIC mechanism is just plain wrong if it restricts reuse outside of your preferred framework. It gets a lot worse should such dependencies expand from your Controllers (where they could be restricted in practice) to your service/model layer which shouldn’t be aware of the framework at all!
Thirdly, DICs are really bad Service Locators. Since your average DIC likely knows almost everything about objects in the application across multiple application layers (by design since it’s an external agent with that specific purpose), any object into which a DIC is injected might now have access to any other object the DIC knows about. This is crazy. If every object can access every other object, it will result in the same thing as having every function able to access every other function (which we endured before PHP got a good OOP model): Spaghetti Code.
Then again, the Flying Spaghetti Monster might be offended by good OOP…
The potential for enabling Spaghetti Coding, DIC/framework specific dependencies, and elimination of Dependency Injection practices are all serious issues – which is why injecting DICs into any other object outside a controlling bootstrap mechanism used to initiate the DIC and get your MVC framework prepped is referred to as an anti-pattern by some. It’s a Bad Thing.
Why Do Frameworks Enable Bad Practice?
Why is water wet? Frameworks operate to a specific set of needs: a compromise between the ideals of the developers and the needs of the users. The goal of a framework is not to hold your hand 100% of the time but to offer an opinionated (all of them are to some degree!) framework which developers hope is capable of meeting a broad set of user needs so that you can get your application off the ground on the cheap and focus on developing your application’s model. Since many PHP programmers will very quickly want the DIC-Injection anti-pattern, frameworks will inevitably offer it. It may not be considered good practice, but it’s one that nearly all PHP programmers have used to some degree. The trick is knowing whether the cost is worth it and, if not, how to opt-out of (or avoid opting-in to!) the anti-pattern.
If it makes you feel any better, framework developers spend an inordinate amount of time debating similar topics. We’re not out to hang you but compromise and education are often a better solution to being overly restrictive. In reading this topic, I hope this little slice of education will inform you on future decisions around how to recognise, implement and use DICs. Even if you wrote it over your coffee break.
In any case, in Part 3 we’ll meet Zend\Di. Code at last :P.
|Print article||This entry was posted by padraic on October 12, 2011 at 2:20 pm, and is filed under PHP General, Zend Framework. Follow any responses to this post through RSS 2.0. You can leave a response or trackback from your own site.|