Dependency Injection in Delphi: a simple example

I want to give a fairly simple Delphi example that will expose the dependency injection pattern. No framework, no third-party library will be needed here: just plain Delphi code.

I won’t dig into the different forms of dependency injection. I will explain the idea of the pattern as simple as possible.

Instead of giving you bunch of definitions, I will present you with some code. The need to inject a dependency will come naturally. You’ll see:

type
  IDependency = interface
  ['{618030A2-DB17-4532-81D0-D5AA6F73DC66}']
    procedure GetType;
  end;

  TDependencyA = class(TInterfacedObject, IDependency)
  public
    procedure GetType;
  end;

  TDependencyB = class(TInterfacedObject, IDependency)
  public
    procedure GetType;
  end;

 TConsumer = class
  private
    FDependency: IDependency;
  public
    constructor Create(aDependencyClassName: string);
    procedure GetDependencyType;
  end;

implementation


{ TDependencyA }

procedure TDependencyA.GetType;
begin
  WriteLn('Instance of type TDependencyA');
end;

{ TDependencyB }

procedure TDependencyB.GetType;
begin
  WriteLn('Instance of type TDependencyB');
end;

{ TConsumer }

constructor TConsumer.Create(aDependencyClassName: string);
begin
  if aDependencyClassName = 'TDependencyA'  then
    FDependency:= TDependencyA.Create
  else if aDependencyClassName = 'TDependencyB'  then
    FDependency:= TDependencyB.Create;
end;

procedure TConsumer.GetDependencyType;
begin
  if FDependency <> nil then
    FDependency.GetType;
end;

It is a good and recommended practice in OOP to decrease coupling as much as possible. This means that each component (class) should avoid knowing implementation details of the other components (classes).

In our example, the TConsumer class has a dependency of type IDependency. So far so good, since we are abstracting any implementation specifics by using an interface (contract). The problem becomes obvious when you take a look at the constructor of TConsumer.

TConsumer.Create is instantiating the concrete classes TDependencyA   or TDependencyB depending on the string parameter aDependencyClassName. As you can see, the design is not well decupled here, because the consumer class (TConsumer) is hard-coding implementation details about its dependency.  

The question is: can we decuple this design even more? Yes, the dependency injection pattern will do it for us.

It’s now time to refactor our code a little bit. We’ll start by changing the signature of the constructor of the TConsumer class:

constructor TConsumer.Create(aDependency: IDependency);
begin
  FDependency:= aDependency;
end;

Instead of choosing the concrete dependency to instantiate within the constructor, we are now injecting the dependency trough the aDependency parameter. Now the class TConsumer is completely independent of the dependency concrete class.

Ok, ok, but we still need to create the concrete dependency instance somewhere, right? Yes, we do. For that we will create a new class TDependencyInjector whose only purpose is to return the right dependency instance. This class will use reflection in order to create the right instance of IDependency. It will use just a string parameter that contains the dependency class name.

uses
  RTTI,
  Dependencies;

type
  TDependencyInjector =  class
  public
    class function GetDependency(aDependencyClassName: string): IDependency;
  end;

implementation

{ TDependencyInjector }

class function TDependencyInjector.GetDependency(aDependencyClassName: string): IDependency;
var
  RttiContext: TRttiContext;
  RttiType: TRttiInstanceType;
begin
  RttiType := RttiContext.FindType(aDependencyClassName) as TRttiInstanceType;
  if RttiType <> nil then
    Result:= RttiType.GetMethod('Create').Invoke(RttiType.MetaclassType, []).AsInterface as IDependency;
end;

Finally, let's put all the pieces together. Consider this console application that puts all the pieces in place:

program DependencyInjection;

{$APPTYPE CONSOLE}

{$R *.res}

uses
...

var
  SomeConsumerObj: TConsumer;
  Dependency: IDependency;

begin
  Dependency:= TDependencyInjector.GetDependency('Dependencies.TDependencyA');
  //Dependency:= TDependencyInjector.GetDependency('Dependencies.TDependencyB');
  SomeConsumerObj:= TConsumer.Create(Dependency);

  try
    SomeConsumerObj.GetDependencyType;
  finally
    SomeConsumerObj.Free;
  end;

  Readln;
end.

In the code above we get the Dependency instance at runtime by using the TDependencyInjector class. Then we inject that dependency using the constructor of the class  TConsumer. We have got a more decoupled design by using dependency injection. Don't you agree? ;-)

Get the full source code of this example here (written in Delphi XE 2).

12 comments:

  1. I honestly can't see the difference between the two versions. You swapped a table based string lookup of the class for an RTTI based string lookup of the class.

    ReplyDelete
  2. The difference is that you won’t find references to TDepencencyA, TDepencencyB or any other dependency class hardcoded within the TConsumer class. The less hardcoded dependencies you have, the better flexibility you’ll get.

    Consider the first version: what happens if I need to introduce a new dependency class: TDepencencyC? In that case, the program won’t work. In order to make it work, you will have to modify the constructor of the TConsumer class. This is not flexible enough.

    In the second version, you don’t need to change the code of the TConsumer class. Even more, you don’t need to change the code of the RTTI based string look-up. You just need to take care about the new component been introduced (injected), that is TDepencencyC.

    Please, let me know if it is clear now. Thanks a lot for your comment.

    ReplyDelete
  3. Two remarks:
    1) Using RTTI for dependency injection is a bit fragile as errors will only popup at runtime. You can fix it easily by introducing a generic dictionary and registering the classes/interfaces in it. You'll get compile-time checks, eliminate those "as", the resulting code will be simpler to debug, and as a bonus, execute faster.
    2) Pass those strings and interfaces as "const" parameters.

    ReplyDelete
  4. Finally, I got the ideas of this post :)

    It's indeed simple but illustrates two points:
    1. A consumer class should just tells us what "external" services are needed insteading of constructing concrete implementations.
    It's obvious to put these dependencies in the constructor just like you did:
    constructor TConsumer.Create(aDependency: IDependency);

    IMO, At this level, it doesn't matter with Dependency Injection but only Design Principles. Developers will benefit from this practice. (Flexible, Testable, etc.)

    To be continued...

    ReplyDelete
  5. 2. Now it's time to show the power of the Dependency Injection container. The container is able to mange releationships and lifetime of all services and components so that it will simplify our work.

    The following handcraft code shows how to use the container in spring4d:
    (TDependencyA -> TComponentA, TDependencyB -> TComponentB)

    // Registration
    GlobalContainer.RegisterComponent.Implements('a').AsSingleton;
    GlobalContainer.RegisterComponent.Implements('b').AsSingleton;
    GlobalContainer.RegisterComponent.InjectConstructor(['a']);

    // Build the container
    GlobalContainer.Build;

    // Using the container
    consumer := GlobalContainer.Resolve;
    consumer.xxx;
    consumer.Free;

    ReplyDelete
  6. Have you had a look at the Delphi Spring Framework? DI in a framework.

    ReplyDelete
  7. Hi folks, I thank you for your comments. I agree with them for the most part.

    I just want to stress that the purpose of my post is to give the idea (and just the idea) of what dependency injection is. When talking about dependency injection there is “always” references to frameworks (like Spring), to lightweight containers (like PicoContainer) and so on. I intended to show that dependency injection is just another design pattern, not tighten in anyway to frameworks or other high level programming attachments.

    I haven’t taken a look at the Delphi Spring Framework yet. How mature is that framework these days?

    ReplyDelete
  8. I still don't get your example and I think it is rather misleading. You have used a string based lookup in one version, and RTTI in the other. However, that contrast is not the point. You could have done it with lookup in both, or RTTI in both and made your point equally. The introduction of RTTI merely hides the idea.

    ReplyDelete
  9. Hi David, first of all, I thank you for your comments.

    As you just said the point was never the STRING or RTTI lookup. Where did I say that? The point was to avoid hard-coding dependencies; thus getting a more decoupled design.

    I used RTTI because that way there’s no need to extend the look-up method every time a new dependency is added. When using RTTI I can add TDependencyC, TDependencyD, ... TDependencyZ and the method TDependencyInjector.GetDependency()remains the same.

    If on the contrary, you use the string based look-up you will need to add lines to the body of the method TDependencyInjector.GetDependency()every time a new type of dependency is added.

    By using RTTI the code is flexible and doesn’t break for every new dependency that’s added.

    I used RTTI to gain in flexibility, but it was never the "point". I didn’t stress in anyway that RTTI is the way, or the only way. I just used RTTI for the reason exposed and that’s all.

    Anyway, if my example is misleading for you, it might be misleading for others. All that you think is worth saying, please, say it. It will help me (us) to gain in clarity.

    Once again, thanks.

    ReplyDelete
  10. These are the key points:

    Point 1:
    --------
    Transform
    constructor TConsumer.Create(aDependencyClassName: string);
    into
    constructor TConsumer.Create(aDependency: IDependency);

    This makes the TConsumer class independent from any concrete dependencies: it allows injecting the dependency.

    Point 2:
    --------
    Introduce a new class TDependencyInjector that instantiates the right dependency by means of the GetDependency method. The key thing is not the internal implementation of GetDependency: I used RTTI (this is not the point); but you can use what’s flexible enough for your purposes.

    ReplyDelete
  11. If you haven't seen Nick Hodges video on Spring that he did for CodeRage6 then go watch it now!

    ReplyDelete
  12. Simple yet nice explanation, I like it :)

    ReplyDelete