2015-04-07 65 views
3

使用Spring4D時,如何在調用GlobalContainer時將字符串值作爲參數傳遞。解析以便在解析的類構造函數上使用此字符串值?在Spring4D中調用GlobalContainer.Resolve時如何傳遞字符串值作爲參數?

我想解析映射到TWorker的類IWorker。 TWorker類在ITool的構造函數中加上一個依賴項,並在工作者的名字中加上一個字符串。

我猜想答案在於可以作爲參數給予GlobalContainer.Resolve的TValue數組,但我不明白如何使用它。

我發現當調用GlobalContainer.Resolve時使用TParameterOverride作爲參數的這篇文章可能已經工作,但是這個功能似乎已經在1.1版本的Spring4D中消失了。

我想避免在註冊我的類型時調用InjectConstructor。

的一部分,我需要幫助的是

GlobalContainer.Resolve<IWorker>([{what do I put here?}]).Work; 

這裏是我的一個小項目

program Project1; 

{$APPTYPE CONSOLE} 

{$R *.res} 

uses 
    System.SysUtils, 
    Spring.Container; 

type 
    IWorker = interface 
    ['{2BBD7E9C-4806-4F01-9B05-9E9DD928D21D}'] 
     procedure Work; 
    end; 

    ITool = interface 
    ['{F962209D-4BC3-41C4-9089-0A874632ED1A}'] 
     procedure Use; 
    end; 

    TWorker = class(TInterfacedObject, IWorker) 
    private 
     FTool: ITool; 
     FName: string; 
     procedure Work; 
    public 
     constructor Create(tool: ITool; name: string); 
    end; 

    THammer = class(TInterfacedObject, ITool) 
    private 
     procedure Use; 
    end; 

{ TWorker } 
constructor TWorker.Create(tool: ITool; name: string); 
begin 
    FTool := tool; 
    FName := name; 
end; 

procedure TWorker.Work; 
begin 
    Writeln(FName + ' is working'); 
    FTool.Use; 
end; 


{ THammer } 
procedure THammer.Use; 
begin 
    Writeln('Using a hammer'); 
end; 


begin 
    try 
     GlobalContainer.RegisterType<ITool, THammer>; 
     GlobalContainer.RegisterType<IWorker, TWorker>; // TWorker constructor = Create(tool: ITool; name: string); 
     GlobalContainer.Build; 

     GlobalContainer.Resolve<IWorker>([{what do I put here?}]).Work; 
     GlobalContainer.Resolve<IWorker>(['THammer.Create', 'Bob']).Work; //--> 'Unsatisfied constructor on type: TWorker' 
     GlobalContainer.Resolve<IWorker>([THammer.Create, 'Bob']).Work; //--> Access violation 
     GlobalContainer.Resolve<IWorker>([nil, 'Bob']).Work; //--> 'Unsatisfied constructor on type: TWorker' 
     Readln; 
    except 
     on E: Exception do 
     begin 
     Writeln(E.ClassName, ': ', E.Message); 
     Readln; 
     end; 
    end; 
end. 

幫助,將不勝感激。 謝謝!

回答

6

薩姆說,你應該避免使用整個代碼容器的服務定位爲僅僅是更換一個構造函數調用來的呼叫容器導致比所有硬連線更糟糕的代碼。

雖然它可能將參數傳遞給Resolve調用它實際上應該通過使用工廠來解決。

這將是如何傳遞的值的名稱參數(該工具是由容器注入的,因爲它知道有關(TNamedValueSpring.pas聲明)。

GlobalContainer.Resolve<IWorker>([TNamedValue.Create('name', 'Bob')]).Work; 

但是,我們可以將這些代碼與註冊工廠(遺憾的是,因爲RTTI缺乏關於類型是一個匿名方法的類型信息,我們必須使用TFunc<...>

type 
    TWorkerFactory = TFunc<string, IWorker>; 

... 

GlobalContainer.RegisterType<ITool, THammer>; 
GlobalContainer.RegisterType<IWorker, TWorker>; 
GlobalContainer.RegisterInstance<TWorkerFactory>(
    function (name: string): IWorker 
    begin 
    Result := GlobalContainer.Resolve<IWorker>([TNamedValue.Create('name', name)]); 
    end); 
GlobalContainer.Build; 

GlobalContainer.Resolve<TWorkerFactory>.Invoke('Bob').Work; 

所以這讓你把一個TWorkerFactory參數在您的代碼的某處,然後容器可以注入它。這樣你就可以使用依賴注入來解耦代碼,但不會直接依賴容器(事實上,你仍然可以手動連接所有的東西,這是我之前說過的規則)

隨着1.2版本的發佈,容器將支持自動化工廠因此您可以編寫如下代碼:

type 
    {$M+} 
    TWorkerFactory = reference to function(const name: string): IWorker; 

... 

GlobalContainer.RegisterFactory<TWorkerFactory>; 

這會自動創建一個代理,將工廠方法的參數進一步傳遞到容器中。

1

通常的解決方案是在容器中爲工人註冊工廠,然後要求工廠使用特定的工具和字符串(名稱?)返回工人。

您當前的代碼看起來可能期待使用容器的應用,這是一種氣味作爲容器只應在Composition root真的

1

我明白這一點應該避免,但我發現使用內一種用我的字符串參數調用解析的方法。

下面的代碼工作,但不是一個好主意:

GlobalContainer.RegisterType<ITool, THammer>; 
    GlobalContainer.RegisterInstance<TFunc<string, IWorker>>(
    function(workerName: string): IWorker 
    begin 
      Result := TWorker.Create(GlobalContainer.Resolve<ITool>, workerName); 
    end); 
    GlobalContainer.Build; 

    GlobalContainer.Resolve<TFunc<string, IWorker>>.Invoke('Bob').Work; 
相關問題