Amazon.com Widgets Getting Giddy with Dependency Injection and Delphi Spring #4 – Dependency Injection Basics

Getting Giddy with Dependency Injection and Delphi Spring #4 – Dependency Injection Basics

By Nick at July 10, 2011 11:48
Filed Under:

So now after the first three parts, you are probably wondering – What the heck is he getting at?  Wasn’t he going to talk about dependency injection?  What the heck is this dependency injection anyway?

Okay, so let’s get down to more practical matters.  Earlier I recommended the Martin Fowler article that (I believe) actually coined the term “Dependency Injection”, but that article is a bit wonky.  I’m going to try to be a bit more practical and “examply” here, and of course, I’ll be showing everything in Delphi.  We’ve talked about the Law of Demeter and why writing testable code is a good idea, so now we’ll get down to it and show how we might actually go about doing what I’ve been talking about.

So, Dependency Injection.  Well, Dependency Injection (I’ll call it ‘DI’ from now on) is really just a way of providing functionality to a given class without creating a specific dependency on that class.  That is, the dependency is “injected” – provided -- for you.  There are a number of  ways to do this from the simple provision of a class to the use a complex framework and  attributes to define dependency instances.  By “specific” I mean that DI will allow you have control over how dependencies are provided.  For instance, you may want to control the injection of  dependency depending on whether you want to run things “for real” in production or what to run them as unit tests.

So by way of getting started, I’m going to define two rules (in addition to “Follow the Law of Demeter” and “Write Testable Code” that is…) that you should follow to make the use of DI easier and simpler. 

Rule #1:  Always code against interfaces

This might seem a bit weird, but I think it is a critical  practice to follow.  Interfaces define a given set of functionality, and that, really, is all you need to provide, right?  A given set of functionality?  A side-rule to this is “Never put a class in the interface section of a unit”.  Now that may seem a bit radical, but if you do this, you are pretty much guaranteed that you’ll follow the Law of Demeter.  A class in the implementation section of a Delphi unit cannot be coupled to outside of that unit.  It cannot be used or abused anywhere. 

Consider the following code: 

unit uNormalMathService;

interface

type
  IMathService = interface
    ['{BFC7867C-6098-4744-9774-35E0A8FE1A1D}']
    function Add(a, b: integer): integer;
    function Multiply(a, b: integer): integer;
  end;

function MathService: IMathService;

implementation

type

  TMathService = class(TInterfacedObject, IMathService)
    function Add(a, b: integer): integer;
    function Multiply(a, b: integer): integer;
  end;

{ TAdditionServiceImplemenation }

function TMathService.Add(a, b: integer): integer;
begin
  Result := a + b;
end;

function TMathService.Multiply(a, b: integer): integer;
begin
  Result := a * b;
end;

function MathService: IMathService;
begin
  Result := TMathService.Create;
end;

end.

This code provides a clearly defined set of functionality, implements that functionality, and makes that functionality available to anyone who wants to use it.  It does all of this without exposing any class at all to anyone outside of the unit.  All you get is an interface and a means of getting an interface via a function call.  (It’s simpler than you’d probably like it all to work, but it’s illustrative of the point….)  You can’t do anything other than use TMathService via the IMathService interface. You can’t abuse and otherwise mess around with TMathService because it is completely hidden from everyone. 

IMathService is easy to test, too.  You can grab an interface, and run tests on it to your hearts content.  And as we know, “easy to test” is a mantra akin to “look both ways before crossing the street” or “Don’t cross the streams”.

And in fact, as we move forward, a really cool part here is that you can provide different implementations of the IMathService interface if need be.  That can provide all kinds of flexibility in the areas of testing and production.  For instance,  if you somehow come up with some better way of adding and multiplying numbers, you can can change to the new way without having to worry about breaking anything at all.  Since it’s easy to test a specific interface implementation, you’ll have a complete suite of unit tests, and can change the implementation with confidence. 

Rule #2:  Keep Constructors Simple

Here’s a good rule of thumb for our second rule:  Don’t create anything in your constructor.  At all.  Ever.  Your constructors should do little else than assign values to field variables.  If your class needs something, it should ask for it.  To start, we’ll do the “asking” via parameters in the constructor.

Another thing to consider is that you should never have conditional code in your constructor.  If you are feeling the need to put an if statement in your constructor, you almost certainly need to break the given class up into two sibling classes with a common descendent.

Consider the following code: 

unit DIBasics;

interface

type

  TPizza = class

  end;

  TPizzaOven = class
    procedure Bake(aPizza: TPizza);
  end;

  TPizzaMaker = class
  private
    FPizza: TPizza;
    FPizzaOven: TPizzaOven;
  public
    constructor Create;
    destructor Destroy; override;
  end;

implementation

{ TPizzaMaker }

constructor TPizzaMaker.Create;
begin
  FPizza := TPizza.Create;
  FPizzaOven := TPizzaOven.Create;
end;

destructor TPizzaMaker.Destroy;
begin
  FPizza.Free;
  FPizzaOven.Free;
  inherited;
end;

{ TPizzaOven }

procedure TPizzaOven.Bake(aPizza: TPizza);
begin
  // bake the pizza
end;

end.


This code has all sorts of problems, no?  Right away, you can see all kinds of coupling.  The TPizzaOven class is directly dependent on TPizza.  The TPizzaMaker class depends directly on TPizza and TPizzaOven.  What if you want to bake a TPepperoniPizza? 

Basically this code defies practically everything that I’ve been telling you not to do. 

Okay, so, let’s apply the most basic form of DI on this code.  I’m not sure if it has a name, but I’ll call it “Dependency Injection via Constructor Parameters”  (as opposed to “Constructor Injection” which is, as we’ll see, something entirely different):

unit DILittleBetter;

interface

type

  TPizza = class

  end;

  TPizzaOven = class
  private
    FPizza: TPizza;
  public
    constructor Create(aPizza: TPizza);
    procedure Bake;
  end;

  TPizzaMaker = class
  private
    FPizzaOven: TPizzaOven;
  public
    constructor Create(aPizzaOven: TPizzaOven);
  end;

implementation

{ TPizzaOven }

procedure TPizzaOven.Bake;
begin
  // bake the pizza
end;

constructor TPizzaOven.Create(aPizza: TPizza);
begin
  FPizza := aPizza;
end;

{ TPizzaMaker }

constructor TPizzaMaker.Create(aPizzaOven: TPizzaOven);
begin
  FPizzaOven := aPizzaOven;
end;


end.

Here, the thing we’ve done to reduce the coupling and the dependency is to have the classes not create their own instances of what they need, but instead, to “ask” for those instances via the constructor.  The TPizzaOven and TPizzaMaker classes get their dependencies provided to them via parameters on their constructors.  The constructors merely assign the passed in references to local variables.  This way, the “link” between the classes is much looser and the flexibility of the whole system is increased while making the classes less coupled together. 

And here’s another reason to use interfaces – the above code doesn’t really cleanly answer the question of the lifetime of the classes passed to the constructors.  Who is in charge of freeing the classes – the caller or the class itself?  The TPizzaMaker and TPizzaOven classes assume that the classes passed to them will be freed elsewhere but there may be occasions when that isn’t the pattern you want to follow .  If you use interfaces for everything, you don’t’ have to worry about the lifetime of your objects – Delphi’s reference counted interfaces will be “garbage collected” for you (for lack of a better term). 

So at its base, DI is the notion that a class should ask for the things it needs and not create those things itself.  There are any number of ways to ask for a given need.  “Dependency Injection via Constructor Parameter” is a very nice way to inject dependencies into a class.  It can get a little messy though, because in order to create a class, you often have to create multiple classes to pass as parameters. 

So, what if you had a system that allowed you to inject dependencies into your classes without even having to really create the classes in the first place?  What if instances of your dependencies could just auto-magically appear out of nowhere without you having to really do anything much at all?

Well, that is what DI frameworks like Spring do, and we’ll take a look at that in our next installment. 

blog comments powered by Disqus

My Book

A Pithy Quote for You

"Some people wonder all their lives if they've made a difference. The Marines don't have that problem."    –  Ronald Reagan

Amazon Gift Cards

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.