Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Delphi

Dependency Injection in Delphi: A Simple Example

5.00/5 (2 votes)
23 Dec 2012CPOL2 min read 29.7K  
I want to give a fairly simple Delphi example that will expose the dependency injection pattern.

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 simply 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:

Delphi
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 decoupled here, because the consumer class (TConsumer) is hard-coding implementation details about its dependency.

The question is: can we decouple 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:

Delphi
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 through 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.

Delphi
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:

Delphi
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).

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)