Monday, January 4. 2010
About a year ago I wrote about How to get a Singleton right. While nothing has changed my opinion about the Singleton Design Pattern - contrary, I'm even more convinced that it is bad, bad and bad - I still have homework to do. In the comments to this article I was asked about my opinion on the Registry Design Pattern, and I promised it would be worth an own blog entry. Well, I did not anticipate it would take more than a year to write it, but you know, work, and no time, and yada yada yada... so here it is.
When thinking about a Registry one has to consider two points: overall architecture of the application, and implementation of the Registry itself. It's not that much what you can say about the implementation of a Registry. For PHP, it should just follow one rule to make it testable and to ease testing of classes using the registry: do not make it a Singleton. (You might have guessed that already.) PHP offers the possibility to implement the Registry as a pure static class, where data within the Registry can be stored within a static class variable, and setters as well as getters can also be static methods. There is no value in making it a Singleton, it just more stuff to type where the result is the same: global state. So if you implement it as pure static, the Registry in itself is neither bad or good.
Speaking of global state, it should be common sense by now that global state is a bad idea, at least if this global does not mean the root of the application itself, which leads us to the overall architecture of the application. How is this related to the Registry Design Pattern? The Registry is intended to allow access to configuration data, objects etc. which you don't want to pass around in your application but require them in different parts (or layers) of your applications. If you have such a need from my point of view this means the application is not fully based on the Dependency Injection principle, it does not separate object creation and business logic as much as it should. If the application is completely based on Dependency Injection, there is no need for a Registry any more.
Did I just say that there is no need for a Registry any more? Well, two exceptions. First, unfortunately in PHP there might be cases where you can not influence the creation of an object instance, and if you want to pass data or other objects to such an instance, you have to take cumbersome actions to pass those. Creation of user land stream wrapper instances is such an example, as those instances are created by PHP itself and there is no possibility to intercept this. Here a Registry might be of help, but it stays what it is: a workaround for a flaw in PHP.
The other exception is the case of using a Dependency Injection framework. You do not need a Registry here - your DI framework already has something like this. It just not called Registry, but it is it's mechanism where you bind data or objects for example in the case of Stubbles or Google Guice, and in Symfony it is called Service Container. (Please note that this is just a quick thought I had in the last days, I might be wrong on this.)
To conclude, the Registry itself is neither useful or harmful. The more important question is how strong you apply the Dependency Injection principle in your application.
Display comments as (Linear | Threaded)
For the vast magjority of applications Dependency Injection is over the top for many, many people.
I look at it in the same way we look at premature optimisation - it's a bad thing, so why utilise it from the beginning?
I would suggest (whilst Singleton is bad and avoid it) people should just learn to pass thier dependencies around as normal.
If the application has been thought out and designed with thought (separation of concerns - proper layers) then it shouldn't be a problem to hook in one or another Dependency container.
There are many other things you can do at a design level that you won't need Dependency Injection anyway...
As for testing that isn't here nor there in regards that Dependency Injection makes little difference.
Until you actually need it - a proven need that is - then it's [Dependency Injection] just another layer of complexity.
#1 Les on 2010-01-04 05:58
I think you have to differentiate here. Using a DI framework adds another layer of abstraction, and depending on the choosen framework more or less complexity.
However, using DI as a design principle in general does not add more complexity. Quite contrary, it makes code more concise, explicit and easier to understand. Separating construction of objects into their own layer belongs to a well thought out design, because changing it later would require to refactor your code in a way that you would be forced to change your test cases as well (if you hopefully have any), something you do not want to do under all circumstances.
#1.1 Frank Kleine on 2010-01-04 09:44
I have to disagree. From my own experience it is not that complicated to learn the DI patterns and to use them in real world projects. All of our developers who had no experience in DI quickly got used to the DI principles. I think it`s mostly a matter how good and intuitive the DI framework is that gets used.
I find that calling anything evil is a good sign that people are misunderstanding something, if only slightly.
For instance, does a singleton equal global state that is changeable from everywhere? Well, that really depends upon what the singleton does. If it's a global variable in disguise, then obviously. Do things have to be that way? Only to people that insist singletons must be evil by design.
Same with the registry pattern, which, when implemented as pure static, is just as global as a singleton, btw. You can use it and you can abuse it. It might be very easy to abuse it, unfortunately, which is likely why people are yelling at it, much like they're yelling at the singleton.
Now, instead of registries or singletons, DI is offered as the silver bullet to solve all problems. Quickly though, people discover that they either have to pass dependencies deep into hierarchies, through classes that have no need for them and thus should not depend on them, or you need to create a DI container that can be created anywhere. The latter obviously appeals to people, as it seems cleaner, but the next problem presents itself: how do you handle resource pooling? Well, let the DI container take care of it. And hey presto, you're getting close to registries and singletons: you've created a class that needs to be available from anywhere and which keeps track of a state, globally. Only it's called a DI container, so it's not evil - because we know that evil means Singleton and Registry.
Mind you, I'm happy about DI and I do think it solves some problems better than the Singleton or Registry do. What that means is mainly: use the right design for your problem. DI is not a silver bullet, it won't solve all your problems. Abuse it, and you'll end up regretting it.
Actually, I do think you're mistaken. A DI container - in my book - is *not* globally available, and it only instantiates top level classes, such as a controller. The controller then acts as an aggregate root, "telling" the DI container it needs a Repository, which in turn needs a DatabaseConnectionPool, which in turn needs a string that represents the location of a config file with connection details, all by the means of simple type-hinting.
The only objects within my application that know anything about a Dependency Injection container, are factories: the objects that make other objects. Those factories can easily be changed into registry based factories, if you'd like that.
Stating that a DI container is on the same level as a Registry is incorrect; a Registry is known throughout an application, a DI container should not be. It doesn't have any kind of state either.
I do agree, however, with the sentiment that DI containers complicate the overall learning process of the application. Although it's quick, easy to work with and it makes for decent code (by inflicting the need for Inversion of Control), the first time you encounter it in the wild without knowing what it is, you _will_ be confused
I wouldn't want to instantiate all possible objects that my top level classes could possibly use, on the off chance that they would need them. That means code lower down in the hierarchy still needs to know about the DI container. You can obviously pass it around, which is better than statically calling it, but you still run the risk of having classes know about it when they don't need to.
Apart from that, registries don't necessarily have problematic state. People somehow assume that a registry is can be changed from anywhere in the code, but that's a groundless assumption: a registry can easily function as a write-once object, where overwriting values are not possible.
Whether or not such a registry is then a good solution to problems is a different question, but it should evaluated based on what it is instead of being shunned because someone called it evil at some point.
> my top level classes could possibly use
Have you ever actually used a dependency injection container? Your claims so far have been false and completely contradictory to my experiences with DI containers. They don't create all objects your top level classes could possibly use: they instantiate the ones that *one* single aggregate root has in it's signature, and if that needs objects, it will create those.
Those objects have to be instantiated anyway, no matter what strategy you use. Using a DI framework however, will actually instantiate *less* objects than the registry approach would.
> You can obviously pass it around, which is better than statically calling it, but you still
> run the risk of having classes know about it when they don't need to.
Use a DI framework before making your own groundless assumptions. You don't pass around a DI container, as you would then end up with a Service Locator instead.
> Whether or not such a registry is then a good solution to problems is a different question, but it should
> evaluated based on what it is instead of being shunned because someone called it evil at some point.
Fully agreed. The only downside of a registry I can see, is the dependency that each class will get upon the registry. That's also the fundamental difference between a Registry and a DI container.
"Those objects have to be instantiated anyway, no matter what strategy you use. Using a DI framework however, will actually instantiate *less* objects than the registry approach would."
What extra objects will the Registry create?
"You don't pass around a DI container, as you would then end up with a Service Locator instead."
A DI container takes care of the problem of dependency. If you've got a hierarchy 4 levels deep but you don't know when instantiating the 1st level that you'll need the 4th level, how are you going to handle the dependency of the deep level?
I might be completely wrong about the proper use of a DI container, but as far as I'm concerned, it's not limited to top-level objects. That would be limiting a design for no apparent reason.
> A DI container takes care of the
> problem of dependency. If you've got a
> hierarchy 4 levels deep but you don't
> know when instantiating the 1st level
> that you'll need the 4th level, how are
> you going to handle the dependency of
> the deep level?
The first level says "I need some object of the second level", so the DI container tries to create this 2nd level instance. However, the 2nd level says "I need something from the 3rd level", so the DI container tries to create this 3rd level instance. (Same with 4th level, as deep as you wish.) Now it has the 3rd level instance which it can use to create the 2nd level instance which in turn can be used to create the 1st level instance. A proper DI container tries to instantiate the whole object graph, it steps down to the lowest level which has no dependencies and back to the class you want to instantiate after it created all of its dependencies (and the dependencies of the dependencies and so on and so forth).
#22.214.171.124.1.1 Frank Kleine on 2010-01-06 00:54
You're (obviously) right.
I've explained it to one of my coworkers as follows: "a Dependency Injection container is a Factory, which creates objects and recursively fulfills all dependencies which are denoted through type-hints, all starting from one aggregate root (or starting point)."
I like to think that description is both accurate as easy to follow, but the inner workings and "automagic" involved in DI containers can make the use of such code difficult to follow, especially if you haven't encountered one before.
Even if your registry is write-once you cannot really be sure what objects are in there. If you use DI and rely on defined interfaces you can at least be sure to get what you need.
The important difference between a Registry and an DI container is about how dependencies are provided to the requesting class. With a registry the requesting class asks for the dependency explicitly. With DI there is no explicit request, the dependency appears automatically in the requesting class.
Many thanks for sentence: "it should be common sense by now that global state is a bad idea, at least if this global does not mean the root of the application itself".
Personally, I do not understand that fear of global, a few root global statics are manageable (e.g. Registry, Log etc.)
#3 Mastodont on 2010-01-04 14:39