[英]How to initialize main application form in Spring4D GlobalContainer?
[英]How can I pass a string value as a parameter when calling GlobalContainer.Resolve in Spring4D?
使用Spring4D時,如何在調用GlobalContainer時將字符串值作為參數傳遞。 解析,以便在已解析的類構造函數上使用此字符串值?
我想解決映射到TWorker的類IWorker。 TWorker類在它的構造函數中依賴於ITool加上工作者名稱的字符串。
我猜的答案在於TValue數組,它可以作為GlobalContainer.Resolve的參數給出,但我不明白如何使用它。
我發現這篇文章關於在調用可能有效的GlobalContainer.Resolve時使用TParameterOverride作為參數,但這個功能似乎已經在Spring4D的1.1版本中消失了。
我想在注冊我的類型時避免調用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.
幫助將不勝感激。 謝謝!
正如Sam所說,你應該避免在整個代碼中使用容器作為服務定位器,因為這只是對容器調用的構造函數調用的替代,這導致代碼比硬連接的所有代碼更糟糕。
雖然可以將參數傳遞給決心調用它真的應該利用工廠來解決。
這將是如何傳遞name參數的值(該工具由容器注入,因為它知道它( TNamedValue
在Spring.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>;
這會自動創建一個代理,將工廠方法的參數進一步傳遞到容器中。
通常的解決方案是在容器中為工人注冊工廠,然后要求工廠使用特定工具和字符串(名稱?)返回工人。
您當前的代碼看起來可能期望在應用程序中使用容器,這是一種氣味,因為容器應該只在Composition根中使用
我明白這應該避免,但我找到了一種方法來使用我的字符串參數調用resolve。
以下代碼有效,但不是一個好主意:
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;
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.