Introducing the SBD Dependency Injection Framework

Happy
From today the Delphi community can leverage the power of the SBD Dependency Injection Framework. Oh happy developer!, you can find the source code at …

… and download with a Subversion client.

The SBD Dependency Injection Framework is … well … a Dependency Injection Framework for Delphi. It consists of two units accompanied by a demo program and fluff. The framework supports all versions of Delphi from 2010 onwards and all platforms and is available for use via the popular aqnd permissive MPL 2.0 license.

Features

For the sake of brevity, I will list only the main features of the framework.

Service oriented

The framework is Service oriented. In this context “Service” means a service accessed via an interface pointer with a defined API. Central to the framework is the Service Provider. The service provider is a service container for it’s clients. The service provider is also a service.

Usage is Attribute oriented

Yes, that’s right – the lazy developer can use attributes to specify automatic injection points. In other Injection Frameworks, the developer would have to write code support constructor injection. In SBD Injection Framework, auto-magical code-less injection of services occurs before the constructor is called, and is driven by the [Injection] attribute.

Example

First let’s start with a couple of service definitions …

type
  IBankAccount = interface
  ['{some guid}']
    function Credit( Amount: currency; const Payee: string): boolean;
    // Attempt to pay someone (Payee) something (Amount) from my bank account.
    // Return True if payment was made.
    end;

  IProfitAndLossAccount = interface
  ['{some other guid}']
    function DistributeDividend( GrossDividendToPayOut: double): boolean;
    // Distribute the dividend to all the share-holders.
    end;

Our task is to distribute a company’s profit to it’s shareholders, by implementing an instance of the IProfitAndLossAccount. We will assume that some-one else has already written a suitable IBankAccount service. Here is how we do it ….

type
  TProfitAndLossAccount = class( TInterfacedObject, IProfitAndLossAccount)
  private
    [Injection] FBankAccount: IBankAccount;
    function DistributeDividend( GrossDividendToPayOut: double): boolean;
  public
    [configuration] constructor ServiceModeCreate;
  end;

constructor TProfitAndLossAccount.ServiceModeCreate;
begin
end;  // Empty constructor! 

function TProfitAndLossAccount.DistributeDividend( GrossDividendToPayOut: double): boolean;
begin
  // The two shareholders are Jack and Jill
  result := FBankAccount.Credit( GrossDividendToPayOut / 2, 'Jack') and
            FBankAccount.Credit( GrossDividendToPayOut / 2, 'Jill') and
end;

Notice there is no constructor logic for the dependent service FBankAccount. A suitable service instance is inserted by the framework. If we have a service provider (ServiceProvider: IServiceProvider),

var
  PandL: IProfitAndLossAccount;
begin
if ServiceProvider.Gn.Acquire<IProfitAndLossAccount>( PandL) then
  PanL.DistributeDividend( 10000.0);
end;

Service implementation discrimination

If we have two different service definitions that just happen to use the same interface pointer type, but serve different purposes (not just different implementations serving the same purpose), the we can discriminate between the two with a “Configuration string”. This is what it looks like …

type
  TProfitAndLossAccount = class( TInterfacedObject, IProfitAndLossAccount)
  private
    [Injection] FBankAccount: IBankAccount;
    function DistributeDividend( GrossDividendToPayOut: double): boolean;
  public
    [configuration('accounting')] constructor ServiceModeCreate;
  end;

Now this …

if ServiceProvider.Gn.Acquire<IProfitAndLossAccount>( PandL, 'accounting') then
  PanL.DistributeDividend( 10000.0);

will use the above service implementation, but …

if ServiceProvider.Gn.Acquire<IProfitAndLossAccount>( PandL, 'something-else') then
  PanL.DistributeDividend( 10000.0);
... will not.

Competing service array versus co-operative service array

You can register an array (“Service file”) of alternate implementations of the one service definition (Interface pointer type, guid + config string). In other frameworks you can only register one implementation per service definition, if you can do multiple, then the service file can only be used competitively (that is to say the client asks for a service instance, and the provider gives one). SBD Dependency Injection framework can offer a file of service implementations either competitively or co-operatively.

Imagine a task of compressing a message. The service definition is “compress this data”. There is lots of ways to compress data. You might have many algorithms at your disposal in the form of registered services. You pick one, acquire an instance from the service provider and solve your problem. That is an example of a competitive service array.

Competing versus Cooperating
But what about the reverse problem of de-compression, where a standard has not been followed. You need to find the correct compressor to correctly decompress it. As the selection of compressor was not recorded, you really need to get all the decompressor services in stock to examine the compressed data, just to see which one is the one to use. Now you are using an array of service implementations in a co-operative fashion.

Some hard problems can be simplified by leveraging a co-operative array of services. SBD Injection framework, makes acquisition and usage of co-operative services easy and almost code-free. When you acquire co-operative services, instead of one selected service being injected, you get a collection of them. At service registration time, you can define how the collection is made and how it is added to – or just use IInterfaceList.

Documentation

The documentation will be in form of a demo program, some wiki pages of text (like this blog post), and some Help Insight ///<summary> notes. This is a work-in-progress. At the moment, I have done about 30% of the demo.

No singletons

The framework does not define singletons, nor require you to use them. Nice!

But how does it compare to the DI in Delphi-Spring?

I don’t know. Perhaps you can tell me?

This entry was posted in Delphi and tagged . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

Comments Protected by WP-SpamShield Spam Plugin