What if you want to employ the Dependency Injection pattern in your application, but don’t want to use a third party framework for it?

By way of exploring this question, I’ll tell you an anecdote. I am currently developing a new FOSS project. I won’t tell you what it is. At this early stage, it’s a secret. Given that the project uses a lot of service oriented interfaces, I thought using Dependency Injection throughout would reduce over-all complexity. I didn’t want to use the Delphi Spring Framework or something similar – I won’t go into the reasons. I ended up rolling my own DI code.

Most of rolling your own DI is pretty much straight forward. Use an Abstract Factory for your service container and Attributes & RTTI to identify injection targets.
Then we come to the bug-bear: How to inject an instance into the interface data members of a object being constructed by the DI service?

The problem defined.

What we have:

What we need:

Sean’s Solution:

One might expect that there would be some simple RTTI function to set the dependency member, like so..

procedure Inject( var Rtti: TRttiContext;
                  Product: TObject; DepMember: TRttiField;
                  const ServiceGUID: TGUID;
	          const Injection: IInterface;
		  {Injection is hard-cast from the unknown service interface type.});
begin
DepMember.SetInterfaceValue( Product, ServiceGUID, Injection)
end;

However, I don’t believe there is. Here is what I came up with:

procedure Inject( var Rtti: TRttiContext;
                  Product: TObject; DepMember: TRttiField;
                  const ServiceGUID: TGUID;
	          const Injection: IInterface;
		  {Injection is hard-cast from the unknown service interface type.});
var
  InjectionAsRTTIValue: TValue;
begin
Assert( DepMember.FieldType.TypeKind = tkInterface);
Assert( DepMember.FieldType is TRttiInterfaceType);
Assert( IsEqualGUID( ServiceGUID, TRttiInterfaceType( DepMember.FieldType).GUID));
Assert( Supports( Injection, ServiceGUID));
TValue.Make( @Injection, DepMember.FieldType.Handle, InjectionAsRTTIValue);
DepMember.SetValue( Product, InjectionAsRTTIValue)
end;

3 Responses

  1. First off, great post – good food for thought.

    Just a quick question though, why do you have a TRttiContext parameter on the Inject procedure?

    Would be great to see a worked example of how you use the Inject method.

  2. About the TRttiContext parameter. You need to keep at least one of these records alive, even if you dont directly use it, otherwise all your other Rtti objects cease to be valid. Refer to Barry Kelly’s blog entry (http://bit.ly/tUqueJ) for details.

    I’ll post a worked example shortly – a simple bare-bones DI framework.

  3. > I didn’t want to use the Delphi Spring Framework or something similar – I won’t go into the reasons.

    Pity Thart could be the most insightful and practical part of the article 🙂