May 06
Some month ago I wrote a simple article about an MVP variant called PassiveView.
That example was very simple. Now I’ll present a more “advanced” version of that example.
The main problem with first example was the following method:
-
procedure TfrmCalculatorView.FormCreate(Sender: TObject);
-
begin
-
//Link controls with related interface
-
IFirstOperand := TGUIEdit.Create(EditFirstOp);
-
ISecondOperand := TGUIEdit.Create(EditSecondOp);
-
ICalcResult := TGUIEdit.Create(EditResult);
-
IOperators := TGUISelectableList.Create(ComboOperators);
-
IError := TGUIEdit.Create(EditError);
-
-
//link view and presenter
-
FPresenter := TCalculatorPresenter.Create(Self); //<<– THIS IS THE BAD LINE
-
end;
The “BAD” line links the View with the Presenter but it’s in the view code, so this is meaning that View KNOWS the presenter… and this is not a good thing becouse the view is not so “passive”.
In a more advanced (and complex) version the View should be completely ignorant about the class that implement the presenter and the service.
In the main dpr file now the code now looks like the following.
-
var
-
MainPresenter: ICalculatorPresenter;
-
CalculatorView: TForm;
-
begin
-
Application.Initialize;
-
Application.MainFormOnTaskbar := True;
-
//SETUP THE MAIN APPLICATION FORM FOR VCL PURPOSE
-
Application.CreateForm(TfrmCalculatorView, CalculatorView);
-
//SETUP ALL THE LINKS BETWEEN THE MVP TRIAD
-
MainPresenter := TCalculatorPresenter.Create(CalculatorView as ICalculatorView, TCalculatorService.Create);
-
//LETS START!
-
Application.Run;
-
end.
Now the presenter take care about all links between the MVP triad.
-
constructor TCalculatorPresenter.Create(CalculatorView: ICalculatorView;
-
CalculatorService: ICalculatorService);
-
begin
-
inherited Create;
-
FCalculatorService := CalculatorService;
-
FCalculatorView := CalculatorView;
-
FCalculatorView.SetPresenter(self);
-
InitView; //does the links
-
end;
There is another addition to the previous example. Now there is only one constructor in the presenter, and using dependency injection take 2 interface for the view and the service.
-
constructor Create(CalculatorView: ICalculatorView; CalculatorService: ICalculatorService);
Another plus is the possibility to open the same form a number of times without change the code for create it.
This is the GUI for this simple application.

3 instance of the same view with different presenter and service
As bonus, unit tests and mock object arent changed.
As usual the source code is here.
Jan 06
As wikipedia says:
“Dependency injection (DI) in computer programming refers to the process of supplying an external dependency to a software component. It is a specific form of inversion of control where the concern being inverted is the process of obtaining the needed dependency. The term was first coined by Martin Fowler to describe the mechanism more clearly.”
Many of us have already read this historical article from Martin Fowler about dependency injection pattern, but actually there isn’t a real framework for implement dependency injection in Delphi.
There are already the following implementation for DI in Delphi
- Emballo (work with pre-D2010 too, but the implementation require changes in the service classes. I really hate it)
- Delphi Spring Framework (very nice, but still not realeased)
So, I decided to write my own simple DI framework.
You can find the code at google code project here: http://code.google.com/p/delphidicontainer/
This is the first public version and come with sample, documentation and unit tests.
Folow some sample code.
-
program Test01;
-
{$APPTYPE CONSOLE}
-
-
uses
-
SysUtils,
-
DIContainer in '..\..\src\DIContainer.pas',
-
ServiceTestObjectsU in '..\..\UnitTest\ServiceTestObjectsU.pas';
-
-
var
-
DIContainer: TDIContainer;
-
s1: TService1;
-
s2: TService2;
-
s3: TService3;
-
s6: TService6;
-
s7: TService7;
-
begin
-
try
-
DIContainer := TDIContainer.Create;
-
try
-
// AddComponent with TClass with and InitType = Singleton
-
DIContainer.AddComponent(TService1, TDIContainerInitType.Singleton);
-
// AddComponent with QualifiedName and InitType = Singleton
-
DIContainer.AddComponent('ServiceTestObjectsU.TService2',
-
TDIContainerInitType.Singleton);
-
// AddComponent with QualifiedName and InitType = CreateNewInstance
-
DIContainer.AddComponent('ServiceTestObjectsU.TService3',
-
TDIContainerInitType.CreateNewInstance);
-
-
// GetComponent with QualifiedName
-
s1 := DIContainer.GetComponent('ServiceTestObjectsU.TService1')
-
as TService1;
-
s1.Message := 'I''m the first message';
-
WriteLn(s1.Message);
-
-
// GetComponent with TClass
-
s2 := DIContainer.GetComponent(TService2) as TService2;
-
s2.Message := 'I''m the second message';
-
WriteLn(s2.Message);
-
-
// GetComponent with a dependent service (TService3 depends upon TService1 and TService2)
-
s3 := DIContainer.GetComponent('ServiceTestObjectsU.TService3')
-
as TService3;
-
WriteLn(s3.GetCompoundMessage);
-
// s3 is not created as Singleton, so after use it I must free it
-
s3.Free;
-
-
// AddComponent with QualifiedClassName, a custom initializer, an alias.
-
// Component will be created as singleton (single instance managed by Container)
-
-
DIContainer.AddComponent(DIContainerUtils.GetQualifiedClassName
-
(TService6),
-
function: TObject
-
begin
-
Result := TService6.Create(DIContainer.Get(TService1) as TService1,DIContainer.Get(TService1) as TService1);
-
end,
-
'srv6',
-
TDIContainerInitType.Singleton);
-
-
s6 := DIContainer.Get('srv6') as TService6;
-
WriteLn(s6.ToString);
-
s6 := DIContainer.Get('srv6') as TService6;
-
WriteLn(s6.ToString);
-
-
// AddComponent with QualifiedClassName, a custom initializer, an alias.
-
// Component will be created as singleton (single instance managed by Container)
-
DIContainer.AddComponent(DIContainerUtils.GetQualifiedClassName
-
(TService7),
-
function: TObject
-
begin
-
Result := TService7.Create(DIContainer.Get(TService1) as TService1,DIContainer.Get(TService1) as TService1);
-
end,
-
'srv7intf',
-
TDIContainerInitType.Singleton);
-
-
s7 := DIContainer.Get('srv7intf') as TService7;
-
WriteLn(s7.ToString);
-
finally
-
DIContainer.Free;
-
end;
-
except
-
on E: Exception do
-
WriteLn(E.ClassName, E.Message);
-
end;
-
readln;
-
end.
DelphiDIContainer also handle constructor injection with automatic dependency resolution for service.
Enjoy and stay tuned.
Recent Comments