3

With Spring4d, you can register custom factories like so

aContainer.RegisterInstance<TFunc<string, TMyObject>>(function(aName : string):TMyObject
begin
    Result := TMyObject.Create(aName);
end);

In this manner, I would beleive that for every dependency that inherits from TComponent, one who wants to pass the owner would either do

// Registrations 

aContainer.RegisterInstance<TFunc<TComponent, TMyObject>>(function(Owner : TComponent):TMyObject
begin
    Result := TMyObject.Create(Owner);
end);

// Then in code

constructor TMyClass.Create(aFctry : TFunc<TComponent, TMyObject>);
begin
    fObj := aFctry(Self);
end;

Or one could also do

aContainer.RegisterType<TMyObject, TMyObject>;

// In code

constructor TMyClass.Create(aObj : TMyObject);
begin
    fObj := aObj;
    InsertComponent(aObj);
end;

Though, this is error prone / adds code just to pass in the owner. Is there a built-in way of getting a factory that takes a TComponent as parameter without having to register it in the container beforehand ?

Because often I will use

constructor MyObject.Create(aDep : TFunc<TMyDep>);

Without registering the TFunc<TMyDep> dependency, but only the TMyDep type.

It it possible to pass something like

constructor MyObject.Create(aDep : TFunc<TComponent, TMyDep>);

Without having to register it in the container ?

Ludovic C
  • 2,855
  • 20
  • 40

2 Answers2

3

From what I know, this isnt possible without registration.

But, there is a way getting rid of a manual factory implementation for 1-4 parameters using the differentIFactory<T,TResult> interfaces from Spring.Container.Common, which will get automagically implemented by the DI Container when registered.

So you would register that like this:

aContainer.RegisterType<TMyObject>;
aContainer.RegisterType<IFactory<TComponent, TMyObject>>.AsFactory;

Registering the factory like this, does not require an implementation on your side - the container will resolve it for you.

That means, that whenever you need an instance of TMyObject, you do not request it directly anymore (from the container). Instead you do request an instance of IFactory<TComponent, TMyObject>, where TComponent is the only parameter accepted by the TMyObject constructor.

As a example of usage with constructor injection from another class TSomeClass, (where TSomeClass is also a TComponent descendant) it would look like the following:

constructor TSomeClass.Create(const AMyObjectFactory: IFactory<TComponent, TMyObject>);
begin
  fMyObjectInstance := AMyObjectFactory(Self);
end;

At least for me, that made things much easier.

codejanovic
  • 522
  • 5
  • 9
0

If I understand correctly, you try to avoid routine code like that:

aContainer.RegisterInstance<TFunc<TComponent, TMyFirstObject>>(function(Owner : TComponent):TMyFirstObject
begin
    Result := TMyFirstObject.Create(Owner);
end);

aContainer.RegisterInstance<TFunc<TComponent, TMySecondObject>>(function(Owner : TComponent):TMySecondObject
begin
    Result := TMySecondObject.Create(Owner);
end);

aContainer.RegisterInstance<TFunc<TComponent, TMyThirdObject>>(function(Owner : TComponent):TMyThirdObject
begin
    Result := TMyThirdObject.Create(Owner);
end);

Well, you can define helper class with procedure RegisterComponentDescendant, which will create fabrics for you, so code you write will be like that:

aContainer.RegisterComponentDescendant<TMyFirstObject>;
aContainer.RegisterComponentDescendant<TMySecondObject>;
aContainer.RegisterComponentDescendant<TMyThirdObject>;

and do the same as before. Helper class is defined like this:

  TContainerHelper = class helper for TContainer
  public
    procedure RegisterComponentDescendant<TMyObject: TComponent>;
  end;

And its implementation:

procedure TContainerHelper.RegisterComponentDescendant <TMyObject>;
begin
  self.RegisterInstance<TFunc<TComponent, TMyObject>>(
    function(Owner : TComponent):TMyObject
    begin
        Result := TMyObject.Create(Owner);
    end);
end;
Yuriy Afanasenkov
  • 1,440
  • 8
  • 12
  • Thank you both, but the more I am reading the answers, the more I am asking myself if the lifetime management (ownership in this case) should be injected? – Ludovic C Dec 16 '15 at 16:41
  • @Ludo We can't advice you about lifetime management without knowing what exactly you're implementing. There are so many possibilities in Delphi: access your object only by interface references and destroy it if refcount is zero, or make hierarchical structure, where owner is responsible for creating and destroying its components, or make it like in VCL, where forms owns all the components, or have manual memory management in some places. There is no universal solution. – Yuriy Afanasenkov Dec 16 '15 at 21:23