【问题标题】:How pass an Interfaced parameter to Spring4D?如何将接口参数传递给 Spring4D?
【发布时间】:2016-06-27 20:51:53
【问题描述】:

我有以下示例。
有没有更简单的方法来传递已经为 Container 注册的接口变量来解析它?
或者,还有其他方法可以使用此功能吗?


这是我的界面
    IString = interface
      function Value: string;  
    end;  

    IUser = interface
      function Name: IString;
    end;

    ILogin = interface
      function User: IUser;
      function Password: IString;
    end;

类实现

    TString = class(TInterfacedObject, IString)
    private
      FValue: String;
    public
      constructor Create(const AValue: String);
      function Value: String;
    end;

    TUser = class(TInterfacedObject, IUser)
    private
      FName: IString;
    public
      constructor Create(const AName: IString);
      function Name: IString;
    end;

    TLogin = class(TInterfacedObject, ILogin)
    private
      FUser: IUser;
      FPassword: IString;
    public
      constructor Create(const AUser: IUser; const APassword: IString);
      function User: IUser;
      function Password: IString;
    end;

容器上的注册

    GlobalContainer.RegisterType<TString>.Implements<IString>;
    GlobalContainer.RegisterType<TUser>.Implements<IUser>;
    GlobalContainer.RegisterType<TLogin>.Implements<ILogin>;

通话
因此,当我必须以这种方式创建这些接口时,嵌套参数会使代码变得复杂且难以阅读(并查看)

    GlobalContainer.Resolve<ILogin>([
      TValue.From(
        GlobalContainer.Resolve<IUser>([
          TValue.From(
            GlobalContainer.Resolve<IString>(['UserName'])
          )
        ])
      ),
      TValue.From(
        GlobalContainer.Resolve<IString>(['SuperSecretPassword'])
      )
    ]);

【问题讨论】:

    标签: delphi dependency-injection spring4d


    【解决方案1】:

    首先,这闻起来像 Service Locator anti pattern - 你应该使用工厂。

    当前的构造需要有关对象图的知识并将两个不同的值传递给相同的类型。工厂非常适合做的事情。

    因此,您可以为您的案例注册一个工厂,而不仅仅是在消费者代码中用解析调用替换 ctor 调用(您应该尽可能避免这种情况):

    type
      ILoginFactory = interface
        [some guid]
        function CreateLogin(const userName, password: string): ILogin;
      end;
    
      TLoginFactory = class(TInterfacedObject, ILoginFactory)
        function CreateLogin(const username, password: string): ILogin;
      end;
    
      function TLoginFactory.CreateLogin(const username, password: string): ILogin;
      begin
        // can also inject the container to the factory and use resolve here - 
        // but doing it the pure DI way for now because the graph to create is simple
        Result := TLogin.Create(
          TUser.Create(
            TString.Create(username)),
          TString.Create(password));
      end;
    

    然后应该将这个ILoginFactory 注入到当前具有GlobalContainer.Resolve 调用的类中。这也有助于升级到composition root

    【讨论】:

    • 好像是这样。但usernamepassword 是接口,它们不是主要类型。
    • 在你的问题中他们是
    • 不是直接的。 ILogin 接收 IUserIString 作为参数。 IUser 接收 IString 来创建。最后IString 接收String 作为参数。这就是重点,我没有直接传递主要类型,我会将它发送到另一个Interface,然后我传递这个Interface
    • 我在TLoginFactory 中所做的是什么 - 那么你的意思是什么?
    • 是的。但是当水平越来越高。我要调用另一个工厂里面的工厂吗?
    【解决方案2】:

    我找到了一种让通话更轻松的方法。
    也许我没有以正确的方式做到这一点。但它使调用更简单。

    我创建了一个帮助类

        TContainerHelper = class helper for TContainer
        public
          ResolveAsTValue<T>: TValue; overload;
          ResolveAsTValue<T>(name: string): TValue; overload;
          ResolveAsTValue<T>(arguments: array of TValue): TValue; overload;
          ResolveAsTValue<T>(name: string;arguments: array of TValue): TValue; overload; 
        end;
    

    现在,当我需要解决依赖于另一个接口的 接口 时,我只是:

        GlobalContainer.Resolve<ILogin>([
          GlobalContainer.ResolveAsTValue<IUser>([
            GlobalContainer.ResolveAsTValue<IString>(['UserName'])
          ]),
          GlobalContainer.ResolveAsTValue<IString>(['SuperSecretPassword'])
        ]);
    

    【讨论】:

      猜你喜欢
      • 2023-03-24
      • 2012-08-29
      • 2013-12-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-09
      相关资源
      最近更新 更多