Search code examples
delphidependency-injectionspring4d

Spring4d : Automatic factory with Owner : TComponent parameter?


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 ?


Solution

  • 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.