In my last post, I spent a good deal of space ranting against the Singleton pattern, and at the end, I promised an alternative. The one I had in mind was Dependency Injection (if you're not familiar with the term, I highly suggest you read the linked Wikipedia article before proceeding).
There are a large number of benefits to Dependency Injection (DI), but I'm going to focus on a few which fall under the categories of improved Cohesion, and improved Coupling.
Improved Cohesion
The strongest cohesive benefit of DI is that it greatly encourages thinking of objects as self-sufficient, stand-alone, software components. Each one must explicitly declare what settings it can accept (i.e. its configurable attributes), what other objects it interacts with (i.e. its dependencies), and what services it provides (i.e. its public methods). Since objects must be configured from the outside, they must provide all the necessary attributes and methods to do so. This requires the author to consider exactly what the function of the object is, and to consider what its public interface should be. The thinking process involved in DI leads to stronger cohesion in the individual objects.
Another major cohesive benefit is that objects don't need to include code to configure themselves. A substantial amount of code in non-DI objects tends to be for looking up configuration values (key-value pairs from files, structured data from a database, object references from a JNDI store, etc). The parsing and handling of this information is generally not the purpose of the class; it is merely an incidental price to make the object sufficiently flexible. By injecting such values from the outside, the class becomes much more focused on its real function, and less on the incidentals of configuration.
Improved Coupling
The crucial and most significant benefit of DI is that objects truly only know about one another's interfaces. In non-DI code, each object may be written in terms of an interface, but somewhere it needed to create or look up an instance using some hard-coded value. This could be as simple as calling a constructor, or as complex as reading a key from a file and looking up the actual object from JNDI. In either case, the object is tied to the actual implementation of that class. In a DI class, there is no connection to the actual implementation class in any way, thereby reducing coupling back to only the shared interface.
The second benefit is simply an implication of the first: that the number and arrangement of objects becomes tremendously more flexible. Since no object ties itself to any other instance, any number of objects can be created and configured to use any combination of dependencies. Perhaps multiple instances will all share the same reference to a dependency; perhaps they will each have their own instance. It may be that one instance of a dependency has been given a version which caches responses while another does not. The fact that every object is given its dependencies (instead of creating them) provides radically looser coupling. As such, the possibilities for arranging classes is dramatically increased.
I've only scratched the surface of the value of Dependency Injection as a design pattern. If you have worked with it before, you undoubtedly already understand the tremendous benefits it provides. If not, I strongly recommend you check out a Dependency Injection framework for your next project. Once you wrap your head around it, you'll never want to go back.
Subscribe to:
Post Comments (Atom)
I had not previously read about "Dependency Injection" so I thank you for that.
ReplyDeleteBut, to seems to me that you are talking about the particulars but skipping the principle. It seems to me that your "Singleton" versus "Dependency Injection" rant is really a question of decoupling of statics. In class design, the general rule is that you do not create public virtual functions in class inheritance in order to seperate the class functionality from the derived class behavior.
Having just read about "Dependency Injection" I have not played with it, but I would guess that it primarily would use a FlyWeight design pattern or an object factory as both of these are general implemented with statics under the covers, but allow for object interfacing, and would be usable with an injector.
There are many situations where a global must exist somewhere. From this post and the previous one, it seems that you are pushing the point that these globals should be private to a class instance.
Whether that is your point or not, I think it is a good one.
Thanks for the feedback!
ReplyDeleteYou're right that most implementations of a DI system use a static Singleton somewhere in the system. However there's an important difference between that and using a lot of various Singletons and/or static factory methods. In a DI system, there is (usually) a *single* Singleton which is hidden from your code, and every class *you* write merely has setters/constructors which accept instances of the object's various dependencies. That way, your code doesn't get entangled with any static factories / variables at all.
This is different from the FlyWeight pattern in that you can inject *any* object... not merely a pool of small, immutable objects. It is much more similar to an object factory, except that it can be configured to return an arbitrary graph of objects instead of only a single one.