【问题标题】:Delphi: Invoke parent constructor using interface (Spring4D framework)Delphi:使用接口调用父构造函数(Spring4D框架)
【发布时间】:2015-10-22 15:34:00
【问题描述】:

我面临从 Spring4D 框架容器解析的类型正确实例化对象的问题。

我有一堂课:

type
  TSurvey = class ( TInterfacedObject, ISurvey )

  private
        _id : Integer;
        _organization : IOrganization;

        function GetId () : Integer;
        procedure SetId ( const value : Integer );

        function GetOrganization () : IOrganization;
        procedure SetOrganization ( const value : IOrganization);

  public
        property Id : Integer read GetId write SetId;
        property Organization: IOrganization read GetOrganization write SetOrganization;
end;

...

initialization

  GlobalContainer.RegisterType<TSurvey>.Implements<ISurvey>.InjectField ( '_organization' );

...

我使用 GlobalContainer 来实例化一个对象:

survey := GlobalContainer.Resolve<ISurvey>;
survey.Organization.Id := 5;

一切都很好,运行良好。

现在我想为 TSurvey 创建一个后代类:

type
  TFieldSurvey = class ( TSurvey )
  ...
end;

问题是如何正确实例化 TFieldSurvey 类的对象?

如果我使用 Create(),那么我会得到一个异常:

 fieldSurvey := TFieldSurvey.Create ();
 fieldSurvey.Organization.Id := 5    <- exception is here

我是否必须在 TFieldSurvey 构造函数中显式调用 Organization 字段的构造函数,还是有其他方法?例如,使用 GlobalContainer?

提前致谢。

【问题讨论】:

  • 标题完全具有误导性,因为实际上您是在问如何正确实例化聚合。
  • 我现在明白了,谢谢。

标签: delphi interface spring4d


【解决方案1】:

您不应以仅适用于 DI 容器的方式编写代码。

DI 容器是一个工具,你应该避免依赖(直接或间接)它。

这意味着您应该避免使用字段注入,因为这样的代码不能与 pure DI 一起使用 - 请改用构造函数或属性注入。

另外,从您发布的代码 sn-p 中,我可以闻到 service locator anti pattern

如果您想创建调查,请使用调查工厂并将其注入您使用它的类中。 DI 容器通常不用于创建值对象,并且您的调查类(尽管不必要地具有接口)看起来像一个。

在开始使用 DI 容器之前,我真的建议您更多地了解 DI 的工作原理以及哪些技术可以让您使用 DI 编写干净的代码。然后才开始使用 DI 容器。反过来做只会导致错误,最终使代码更难维护。

【讨论】:

  • 亲爱的 Stefan,感谢您的评论。你能举一些例子为什么我不应该以这种方式使用 DI 容器吗?从这个角度来看,需要什么现场注入?
【解决方案2】:

注入仅在您通过容器创建对象时起作用,而不是直接调用对象上的构造函数。所以你需要用GlobalContainer注册TFieldSurvey,然后调用Resolve来获取你的对象。

注册:

GlobalContainer.RegisterType<TSurvey>.Implements<ISurvey>('SPRING_SURVEY').InjectField ( '_organization' );
GlobalContainer.RegisterType<TFieldSurvey>.Implements<ISurvey>('SPRING_FIELD_SURVEY').InjectField ( '_organization' );

然后获取实例:

GlobalContainer.Resolve<ISurvey>('SPRING_FIELD_SURVEY')

我添加了“SPRING_SURVEY”和“SPRING_FIELD_SURVEY”的名称,因为它们都实现了ISurvey,这使您可以选择所需的类实例,否则您最终会得到为该接口注册的最后一个实现。如果TFieldSurvey 将实现自己的接口(例如IFieldSurvey),您可以取消名称,然后在需要时将类型转换回ISurvey

您也可以始终在 _organization 字段上使用 [Inject] 属性,而不是使用 .InjectField(在您的使用中添加 Global.Container.Common 之后):

  TSurvey = class ( TInterfacedObject, ISurvey )
  private
    _id : Integer;
    [Inject]
    _organization : IOrganization;

    function GetId () : Integer;
    procedure SetId ( const value : Integer );

    function GetOrganization () : IOrganization;
    procedure SetOrganization ( const value : IOrganization);

  public
    property Id : Integer read GetId write SetId;
    property Organization: IOrganization read GetOrganization write SetOrganization;
  end;

您的注册将是:

  GlobalContainer.RegisterType<TSurvey>.Implements<ISurvey>('SPRING_SURVEY');
  GlobalContainer.RegisterType<TFieldSurvey>.Implements<ISurvey>('SPRING_FIELD_SURVEY');

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-20
    • 2017-10-04
    • 1970-01-01
    • 1970-01-01
    • 2018-01-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多