2015-04-07 1 views
3

При использовании Spring4D, как я могу передать строковое значение в качестве параметра при вызове GlobalContainer. Разрешить так, чтобы это строковое значение использовалось в разрешенном конструкторе класса?Как передать строковое значение в качестве параметра при вызове GlobalContainer.Resolve в Spring4D?

Я хочу разрешить класс IWorker, который сопоставляется с TWorker. Класс TWorker имеет зависимость в своем конструкторе с ITool и строку для имени рабочего.

Я бы предположил, что ответ лежит в массиве TValue, который может быть задан как параметр для GlobalContainer.Resolve, но я не понимаю, как его использовать.

Я нашел это сообщение об использовании TParameterOverride в качестве параметра при вызове GlobalContainer.Resolve, который мог бы сработать, но эта функциональность, похоже, исчезла в версии 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. 

Помощь будут оценены. Спасибо!

ответ

6

Как сказал Сэм, вы должны избегать использования контейнера в качестве службы локатора на протяжении всего кода, как это просто замена вызова конструктора на вызов контейнер, который ведет к еще худшему коду, чем все, что связано с проводкой.

В то время как это является, можно передать аргументы на вызов Resolve, это действительно должно быть разрешено с помощью заводов.

Это было бы, как передать значение для имени аргумента (инструмент вводится в контейнер, так как он знает об этом (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>; 

Это автоматически создает прокси-сервер, который передает аргументы метода фабрики далее в контейнер.

1

Обычным решением для этого является регистрация фабрики для рабочего в контейнере, а затем попросить фабрику вернуть работника, используя конкретный инструмент и строку (Name?).

ваш текущий код выглядит, как он может ожидать, чтобы использовать контейнер внутри приложения, который является запах в качестве контейнера следует использовать только в 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; 

 Смежные вопросы

  • Нет связанных вопросов^_^