So far, we’ve been registering interfaces and implementations in a one-to-one relationship. Each interface has one implementing class registered against it. But what if you want to implement an interface many different ways, choosing which implementation to use depending on user input or other external factors?
As always, you can download the Delphi Spring Framework from GoogleCode.
Well, lucky for us, the Spring Container lets us do just that. The Delphi Spring Framework container registration system allows you to specify a name for any giving implementation registration, thus distinguishing different registrations from on another, even if you register multiple implementers for the same interface.
If you register multiple implementers for a given interface without specifying a name for each one, then the “last one in wins'”.
So, for instance, you might declare a simple credit card interface as follows:
type
ICreditCard = interface
['{6490640C-0E2B-4F7D-908C-0E6A74DCC0A0}']
function IsValid(aCreditCardNumber: string): boolean;
function ChargeAmount(aCreditCardNumber: string; aAmount: Double): Boolean;
end;
There are any number of credit cards that customers might want to use, so you’ll need to have credit card implementations for the various common vendors:
GlobalContainer.RegisterType<TVisa>.Implements<ICreditCard>('VISA');
GlobalContainer.RegisterType<TMasterCard>.Implements<ICreditCard>('MasterCard');
GlobalContainer.RegisterType<TDiscover>.Implements<ICreditCard>('Discover');
GlobalContainer.RegisterType<TAMEX>.Implements<ICreditCard>('AMEX');
This code registers four different classes (TVisa, TMasterCard, TDiscover, TAMEX) for the same interface (ICreditCard) via the string parameter on the GetService call. Once these are registered, you can pick and choose whichever credit card processing class you want as the implementation of ICreditCard. You can even change the selection at runtime based on, say, user input or different orders being processed, etc.
For instance, if you have four radio buttons that allow the user to select one of four credit cards, you can do the following:
var
CurrentCard: ICreditCard
...
procedure TMultipleImplementationsForm.RadioButton1Click(Sender: TObject);
begin
CurrentCard := ServiceLocator.GetService<ICreditCard>('VISA');
end;
procedure TMultipleImplementationsForm.RadioButton2Click(Sender: TObject);
begin
CurrentCard := ServiceLocator.GetService<ICreditCard>('MasterCard');
end;
procedure TMultipleImplementationsForm.RadioButton3Click(Sender: TObject);
begin
CurrentCard := ServiceLocator.GetService<ICreditCard>('Discover');
end;
procedure TMultipleImplementationsForm.RadioButton4Click(Sender: TObject);
begin
CurrentCard := ServiceLocator.GetService<ICreditCard>('AMEX');
end;
The above code will assign an instance of the appropriate implementing object to the single variable CurrentCard depending on which radio button the user selects. The proper object is returned based upon the string parameter passed to the GetService call. That string value, of course, corresponds to the object registered with that same string as shown above.
Thus, you can register by name and then use as many implementing objects for a single interface as you want. This is obviously very powerful, as you can choose from any number of implementations as well as add new implementations anytime you want.
A sample application showing this technique as well as some other interesting features can be found in the samples that come along with the Delphi Spring Framework.