As always, you can download the Delphi Spring Framework from GoogleCode.
By now you should be getting the idea about dependency injection – how you can use it to decouple your code and provide instances of your objects. Hopefully you see the benefits of coding against interfaces and not implementations. And if things are going really well, you are writing unit tests with ease because your code is easily tested.
However, I bet by now some of you are thinking that this looks really cool and all, but there is just a touch too much “magic” going on – that there is a bit too much going on under the covers.
Well, you are right about that – there is a lot going on under the covers. The Delphi Spring Framework does a lot of work for you, mainly by creating instances of classes using Run-time Type Information (RTTI). It controls the creation, and can even let you manage the lifetime of those objects.
But I also bet that some of you are a bit uncomfortable with the notion of relying on all that magic. While turning over that control will work in many cases, you don’t always want to give up control over the creation of your objects.
Well, you don’t have to. The framework allows you to customize the way that your classes are created if you so desire. Very often, if your classes are well designed – your constructors are simple, you aren’t doing much more than assigning values – you don’t need to do anything special when you construct an instance. But sometimes you do. Sometimes, your class needs to be passed something dynamic – a class whose state can only be known at runtime. For instance, you may have a given customer, and that customer has invoices, and you have a class that you want to pass that customer to, but you also want to completely decouple that class. Your customer is dynamic – you may be running a query and need to perform this operation on every customer in your database. Or maybe the customer is doing something on your website, and so your customer object is specific to that customer. In any event, the customer data is dynamic at runtime, and so the creation of an object can’t be cookie-cutter. It has to be unique each time you need an instance of this object that takes action on the given customer.
The code below comes from the Samples directory of the Delphi Spring Framework. It’s part of the Demo.Spring.DelegatedConstructor project.
The Spring Framework lets you control the creation of your objects through a method on the TRegistration class – the class used to register types against interfaces. As part of registering your class, you can pass an anonymous method of type TActivatorDelegate, which his declared as:
TActivatorDelegate<T: class> = reference to function: T;
The TRegistration class uses the fluent interface, so you can chain together a number of things that you want to attach to any given registration. So, for instance, if you have a project involving a TUser class, and the TUser class is dynamic, then a class (such as TUpgradeUser) which needs a TUser, might be registered as follows:
Result := TUserProcessor.Create(GetCurrentUser);
The TUserProcessor class is registered as implementing the IUserUpgrader interface. The lifetime of the resulting class is set to be “Transient” meaning that the implementing class will live “normally”; that is, it will be destroyed when the interface reference goes out of scope. (Note that AsTransient is the default lifetime. We’ll discuss container lifetime management in a future blog post).
Finally, the actual creation of the TUserProcessor class is “delegated to” an anonymous method – a function in this case, as noted above – that simply returns an instance of the TUserProcessor class. The key here, of course, is that the anonymous method can call the GetCurrentUser routine which will inject the current, dynamic TUser instance into the resulting object. You can do anything you want in this anonymous method, including creating and setting up the resulting object in any manner you choose. You could set properties and even call methods on the resulting, implementing object.
If you examine the code for the project, you’ll note, of course, that it doesn’t actually do anything other than write to the console. The GetCurrentUser call is merely a singleton returning the same instance of TUser. The code is merely illustrative, and so the “background stuff” doesn’t really do anything. In a real application, a call to GetCurrentUser would do just that, return an instance of TUser that represented the current, dynamic state of the user in question. The critical part to note is that you can control the creation of the TUserProcessor to as large a degree as you want.
Finally, when you actually call the ServiceLocator to grab an instance, the Container will call your anonymous method and return an instance constructed exactly like you want it to be constructed.
So in the end, the Spring Container gives you total control – if you want it – over the creation of your objects. If you need to, you can have a little control over the “magic” that takes place and sprinkle your own little fairy dust on the construction process.