Article Four in the series had a couple of rules to follow, one of which was “Keep Constructors Simple”. In the last article we saw how you can use the Spring Container to hold interfaces with specific implementations so that you don’t have to create anything, but instead ask the container for an instance of an interface.
Well, in this article, I’m going to go a step beyond even that, and show you how the Delphi Spring Framework can make it so that you don’t even need to have a constructor by automatically injecting implementation instances when they are needed without you having to do a thing.
A Side Note
In the previous article, I had you create a unit called uServiceLocator.pas that provided a central location for registering and resolving interface implementations. Since then, the Spring codebase has changed to provide a similar functionality. The Spring.Services unit contains a singleton function ServiceLocator that is used for getting services (and indeed the call has changed from Resolve to GetService). The Spring.Container unit contains a similar function GlobalContainer that is used to register the interface/implementation combinations. All items registered into GlobalContainer are available for retrieval via ServiceLocator.
A Little Review
One of the common things that happen in a constructor is the creation of other needed things. In the past few articles, we’ve talked about the need to reduce dependencies by not creating things you need but instead asking for them in your constructor. The reason that you don’t want to do much at all in a constructor – why you want to pass stuff in to it instead of actually creating things – is to limit what happens when you do create objects. Consider this code:
FOrderValidator := TOrderValidator.Create;
FOrderEntry := TOrderEntry.Create;
This code looks pretty normal – you are creating some instances of classes you need to process an order. But there are a couple of problems here that need to be avoided:
- You are stuck with TOrderValidator. That’s the only class that TOrderProcessor can use. You can’t choose to use a different class or implementation. That’s it. Same for TOrderEntry.
- Not only are you stuck, but you are coupled to it. If TOrderValidator changes in ways that you don’t like, you might never know. Because you are using the unit that it is declared in, you might get careless and start calling other stuff in that unit. Pretty soon and before you know it, your code is hopelessly joined to those other classes in ways that may make it difficult or impossible to disentangle. And being tangled up with another class is bad.
- There is no way of knowing what a TOrderValidator does, creates, or otherwise allocates. It could be in and of itself creating thirteen other classes, allocating huge blocks of memory, accessing a database or even firing up a nuclear weapon. Who knows? The downstream effects of merely creating TOrderProcessor class can be pretty much something out of your control And it could change as other parts of the code is worked on by other team members.
- Along those same lines, you don’t know what will be left around after you use these classes. They might write records to a production database. TOrderValidator might go off and incur a charge of $0.50 at your credit card validating company. (Imagine your boss getting that bill after you run your unit tests with this code a couple of thousand times….) In the end, you are tightly coupled to those classes. That is bad.
So we could use the Spring Container to have it so that we don’t even have to call Create on the helper classes:
Order := TOrder.Create;
OrderValidator := ServiceLocator.GetService<IOrderValidator>;
OrderEntry := ServiceLocator.GetService<IOrderEntry>;
OrderProcessor := TOrderProcessor.Create(OrderValidator, OrderEntry);
if OrderProcessor.ProcessOrder(Order) then
WriteLn('Order successfully processed....');
Note that we are still calling Create on the TOrderProcessor itself, but we’ll show how to tell the Container exactly how to create our class in the next article.
So in this way, we are able to completely decouple the classes from each other, and we are able to ask the container for implementations. Thus, if we wanted to, say, implement mock objects for these interfaces, we could easily do this by simply choosing to register mock classes against those interfaces instead of “real” classes.
Never Call Create
But if we have to have to be really careful about not doing too much stuff in a constructor, how about just not having a constructor at all? How about if we had a way of initializing everything that needs initializing while merely relying on the default constructor from TObject? Impossible you say? To that I say – bah! Let’s do it!
I should note that often a class’s constructor will require other assignments to integers, strings, and other non-class types. That will, of course, require a constructor, but the point here is to illustrate that you can decouple your classes so much that you don’t even have to create them.
Getting Rid of the Constructor with Field Injection
Field injection is the solution for getting rid of constructors altogether. Field Injection is the ability to inject the implementation of a dependent class directly to a field reference without actually calling Create manually. The Delphi Spring Framework provides two ways for doing field injection.
Consider the following class declaration:
TOrderProcessor = class(TInterfacedObject, IOrderProcessor)
function ProcessOrder(aOrder: TOrder): Boolean;
Note first that the class has no constructor. It still contains references to the two internal interfaces that we saw above, but there is no formal constructor. The second thing to note, of course, is the [Injection] attribute. This is the key here. This attribute tells the Spring Framework – “When you first encounter this identifier, go to the Container and grab the implementation for that interface and assign it. “ Basically, it tells Spring to do everything for you, including creating an instance of the implementing class for the interface.
Thus, for our TOrderProcessor class, we can have no constructor, but freely use the interfaces declared as private fields:
function TOrderProcessor.ProcessOrder(aOrder: TOrder): Boolean;
Result := False;
OrderIsValid := FOrderValidator.ValidateOrder(aOrder);
if OrderIsValid then
Result := FOrderEntry.EnterOrderIntoDatabase(aOrder);
WriteLn('Order has been processed....');
The above code will run just fine, despite the fact that at no time does the code itself ever explicitly create a class for the interfaces.
But wait, you say – what about FOrderEntry? It doesn’t have the [Injection] attribute. You are right. Instead, we do field injection directly as part of the interface/class registration process. In the initialization section of the declaring unit, the registration looks like this:
At the very end, you’ll notice that there is a call to InjectField(‘FOrderEntry"’); which tells Spring the same thing that the [Injection] attribute does. In fact, the [Injection] attribute actually invokes the very same code to ensure that the reference is correctly assigned. (You can see the whole unit here in BitBucket).
So there, we did it: We created a useful class that uses two other classes and we didn’t create anything and we didn’t couple anything to anybody. Pretty nice, huh?
Okay, so now we are able to write classes that is so decoupled from each other that they don’t even have to create instances for use! We used Field Injection to automatically get references to objects that implement our interfaces without doing anything at all. These classes are so decoupled that you don’t even have to include the units that implement the classes in any uses clause other than your application’s *.DPR file. And as we said at the start, “Decoupling is good and coupling is bad”. (If I didn’t say that, I should have, right?)