Remember, you can download the Delphi Spring Framework on Google Code.
Introduction
Okay, so we are finally at the point where we can actually start using a framework to do some of the things that we want to do. So far we’ve seen how the Law of Demeter is important to good code design. We’ve looked at how important it is to write testable code by reducing the dependencies between objects, and we’ve seen how it’s good to code against interfaces and to keep our constructors simple. I’m finally going to make good on my promise to show you how to write useful code in a unit with nothing in the interface section.
I hope I’m doing a good job describing why and how you want to use a Dependency Injection framework. I hope by the end of this, you can see how to use the Delphi Spring Framework. However, I’m doing a very imperfect job of describing the motivations and reasons behind why you want to do dependency injection. However, all is not lost – I strongly recommend that you watch this outstanding episode of DotNetRocks TV, where a really smart guy (with a delightful Canadian accent – he sounds just like my brother-in-law) named James Kovacs explains the whole underpinnings of Dependency Injection and why it’s good. He does the presentation in C#, but don’t let that put you Delphi guys off -- the ideas are all there.
Problems With “DI via Constructor Parameters”
In the last episode, I talked about two rules that you should follow:
- Always code against interfaces
- Keep your constructors simple
We looked at how exposing only an interface will create clean, uncoupled, and testable code, and how keeping constructors simple will decrease one classes dependency on another. We saw an example of what I called “Dependency Injection via Constructor Parameters” (DICP) where you don’t create internal classes yourself, but let them get passed in via parameters on a constructor.
DICP allows you to create the services (classes) you need outside of the consuming class itself, but it still requires that you create specific classes you want in specific places in your code. Despite having a certain level of inversion of control, you are still coupled to the specific class. Consider the following code, based on our Pizza Oven code from the last installment:
var
MyPizzaOven: TPizzaOven;
MyPizza: TPizza;
begin
MyPizza := TPizza.Create;
MyPizzaOven := TPizzaOven.Create(MyPizza);
end;
This code is better than creating the TPizza class inside the constructor of TPizzaOven, but we still have the problem of the TPizza class and the TPizzaOven class being coupled together via the uses clause – TPizzaOven needs to know about the TPizza class and how it is declared.
Even if we re-declare TPizza as an interface – IPizza – we still have to know how to create an instance of TPizza for the IPizza interface, and we aren’t all that much more decoupled than we were before – TPizzaOven still has to know about the specific declaration of TPizza. In fact, the code above really wouldn’t change at all if the MyPizza variable were an IPizza instead of a TPizza,right?
But what if there were a way to get an interface implementation without having to know anything about the specific implementation of that interface? Now that would really decouple things and truly separate the interface from the functionality. If only there were a framework written in Delphi that would let you do this. If only……!
The Spring Container
Okay, I was joshing – there is such a thing: The Delphi Spring Container does exactly that. A Dependency Injection container is a class that can hold references to interfaces and classes that implement them. You can register specific implementations of interfaces in the container. If you need an implementation of an interface, you ask the container, and it provides a reference to that implementation via the given interface. When you need an implemented interface, you reference only the container, and thus there is no direct connection between the class needing the implementation and the implementation itself. Pretty sweet, huh?
So how does this work? The first thing you’ll likely want to do is to create a singleton implementation of the Spring Container. I’ve created the following unit called uServiceLocator, which wraps up the Spring Container in a Singleton:
unit uServiceLocator;
interface
uses
Spring.DI
;
function ServiceLocator: TContainer;
implementation
var
FContainer: TContainer;
function ServiceLocator: TContainer;
begin
if FContainer = nil then
begin
FContainer := TContainer.Create;
end;
Result := FContainer;
end;
end.
The uServiceLocator uses the Spring.DI unit from the Delphi Spring Framework where the TContainer class is declared. I have named my container "ServiceLocator" because it, well, locates services.
You can use this unit anywhere you need to register classes and interfaces, as well as anywhere you need to use those registered interfaces.
TContainer is a powerful class that leverages both Generics and anonymous methods to let you register classes that implement given interfaces. In later articles, I’ll delve a little deeper into the mysterious inner workings of TContainer, but for now, we’ll just look at the practicalities of how it is used.
Registering With The Container
Now, if you want to use the container, either to register classes with interfaces, or to consume a given interface, all you need to is use the uServiceLocator unit and not any unit that actually declares classes. In fact, you can declare your classes in the implementation section of the unit and leave only an interface in the interface section. Remember the uNormalMathService unit from last time? Now, we can declare it as follows:
unit uNormalMathService;
interface
type
IMathService = interface
['{BFC7867C-6098-4744-9774-35E0A8FE1A1D}']
function Add(a, b: integer): integer;
function Multiply(a, b: integer): integer;
end;
implementation
uses
uServiceLocator;
type
TNormalMathServiceImplemenation = class(TInterfacedObject, IMathService)
function Add(a, b: integer): integer;
function Multiply(a, b: integer): integer;
end;
{ TNormalMathServiceImplemenation }
function TNormalMathServiceImplemenation .Add(a, b: integer): integer;
begin
Result := a + b;
end;
function TNormalMathServiceImplemenation .Multiply(a, b: integer): integer;
begin
Result := a * b;
end;
procedure RegisterNormalMathService;
begin
ServiceLocator.RegisterComponent<TNormalMathServiceImplemenation>.Implements<IMathService>('Normal');
ServiceLocator.Build;
end;
initialization
RegisterNormalMathService;
end.
First, note that the IMathService interface is the only thing available for use outside of the unit. Everything else is hidden in the implementation section. Obviously, the “money code” here is the RegisterNormalMathService procedure. This is where the implementation of IMathService gets registered. The procedure itself gets called in the unit’s initialization section, so the mere act of using the unit ensures that the TNormalAdditionService is registered and available for use by any other unit that uses the ServiceLocator. The call to ServiceLocator.Build simply ensures that the TContainer is ready to answer requests for implementations of interfaces.
So what is happening in the RegisterNormalMathService call? It’s almost self-explanatory. The code performs pretty much as it reads: “Register the class called TNormalMathServiceImplementation, which implements the IMathService interface, under the name "Normal”. (That’s what the last string parameter is – a named reference to the interface. This allows you to register multiple implementations of a given interface and request them by name).
Nota Bene: if you are going to provide multiple implementations of a given interface, you can declare that interface in a separate unit, use that new unit in the implementation section, and end up with no code at all in the interface section of your unit. See, I told you it would work. 
And once the service is registered, you can use it in a completely decoupled way:
unit uCalculator;
interface
implementation
uses
uServiceLocator, uNormalMathService;
type
TCalculator = class
private
FMathService: IMathService;
public
constructor Create;
function Addition(a, b: integer): integer;
function Multiplication(a, b: integer): integer;
end;
constructor TCalculator.Create;
begin
FMathService := ServiceLocator.Resolve<IMathService>('Normal');
end;
function TCalculator.Addition(a, b: integer): integer;
begin
Result := FMathService.Add(a, b);
end;
function TCalculator.Multiplication(a, b: integer): integer;
begin
Result := FMathService.Multiply(a, b);
end;
end.
In this case, the “money code” happens in the class’s constructor, where the ServiceLocator is asked to “resolve” (i.e., provide) a working instance of IMathService, using the implementation registered under the name 'Normal'. The Container then goes and looks into its list of registered items, finds the correct implementation, creates an instance of that class, and returns it as an interfaces. All of that happens “auto-magically” inside the inner-workings of the TContainer class.
Later on we’ll see how you can control the lifetime of the created classes, pool them, or create instances as a Singleton. You can even have complete control over how the resulting class is created via the power of anonymous methods.
Note that the class knows nothing about anything other than IMathService. It does use the uNormalMathService, but the implementation of that class is completely hidden. The only reason that uNormalMathService is in the uses clause is because that is where the IMathService interface is declared. If you really want, you can do as the quote block above says and declare the interface in a separate unit and not even require the use of the unit that implements IMathService at all.
Conclusion
Thus, we have a simple example of using the Delphi Spring DI Container that completely decouples the implementation of a class from the consumer of that class. In the above example, the TCalculator class knows nothing of nor is connected in anyway to the implementation of the IMathService interface. All of this happens inside of the Delphi Spring container.
So far, this is just a simple, basic look at what the Delphi Spring Container can do. The Delphi Spring Framework can actually automatically and seamlessly create implementations for interfaces without writing any code at all. So stay tuned for that in coming installments.