Amazon.com Widgets Getting Giddy with Dependency Injection and Delphi Spring #7 – Controlling Construction

Getting Giddy with Dependency Injection and Delphi Spring #7 – Controlling Construction

By Nick at October 08, 2011 21:47
Filed Under: Software Development, Delphi

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:

  GlobalContainer.RegisterType<TUserProcessor>.Implements<IUserUpgrader>.AsTransient.DelegateTo(
    function: TUserProcessor
    begin
      Result := TUserProcessor.Create(GetCurrentUser);
    end
  );

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.

Comments (5) -

10/9/2011 1:45:50 AM #

Baoquan Zuo (Paul)

Thanks, Nick.

Furthermore, you can use the GlobalContainer.Resolve method(s) to query an instance of a service in the delegated anonymous method which will be executed at run-time. e.g.

GlobalContainer.RegisterType<TUserProcessor>.Implements<IUserUpgrader>.AsTransient.DelegateTo(
  function: TUserProcessor
  begin
    Result := TUserProcessor.Create(GetCurrentUser);
    Result.Logger := GlobalContainer.Resolve<ILogger>;  // <- Dynamic
  end
);

Baoquan Zuo (Paul) United States |

10/11/2011 5:44:21 PM #

Dean Hill

Was just about to ask this. Thanks.

Dean Hill South Africa |

10/9/2011 11:17:59 AM #

nick

Excellent point, Paul!  Thanks --

nick United States |

10/12/2011 9:33:41 PM #

Fran&#231;ois

Unrelated to this specific post, but I just noticed it now...
On the bottom right of the page, there is a recap of the viewer's comments and in my case it's getting my name "funny":
  Hi Fran%c3%a7ois
  Welcome back!

2011 and cedilla still causing problems in blog software Wink

François United States |

10/18/2011 10:47:06 AM #

Dean Hill

Probably as good a place as any to raise the question as it seems more related than the unit testing blog.

How do you typically chose which object is responsible for deciding which objects get created?

The reason for me raising this is your recent blog about unit testing. I typically work as follows. using the unit testing example. If I have a credit card manager and a credit card validator, I will let the configuration for the instance of the manager decide on which validator to create (so I could have 2 different managers with different validators). The trend seems to be to inject the validator instance through the constructor or another method on the class. So the object using the credit card manager decides which validator to use and not the credit card manager itself. While I can see the benefits of doing this from a TMock style approach, my gut feeling is that this approach is wrong. The outside object should only know that it can call validate on the credit card manager, not which object is responsible for that validation. Am I missing the boat on this?

Dean Hill South Africa |

Comments are closed

A Little About Me

Hey, I'm Nick.  I'm interested in Software Development, Leadership, and Basketball.  I'm a big fan of Delphi, but love all cool programming languages.

A Pithy Quote for You

"Never interrupt your enemy when he is making a mistake."    –  Napoleon Bonaparte

Little Buttons and Stuff

Nick Hodges

Create Your Badge
View Nick Hodges's profile on LinkedIn
profile for Nick Hodges on Stack Exchange, a network of free, community-driven Q&A sites
Powered by DiscountASP.net
Join Dropbox

General Disclaimer

The views I express here are entirely my own and not necessarily those of any other rational person or organization.  However, I strongly recommend that you agree with pretty much everything I say because, well, I'm right.  Most of the time. Except when I'm not, in which case, you shouldn't agree with me.

Book Stuff

Earn Free Stuff

Search & Win