Delphi: 使用接口调用父类构造函数(Spring4D框架)

4

我面临的问题是如何正确地从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?
提前感谢。

标题完全误导,实际上你正在问如何正确实例化一个聚合。 - Stefan Glienke
我现在明白了,谢谢。 - Aptem
2个回答

4

只有通过容器创建对象,而不是直接调用对象的构造函数,才能使注入起作用。因此,您需要使用 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');

2

你不应该编写只能与DI容器配合工作的代码。

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

这意味着你应该避免使用字段注入,因为这样的代码无法使用纯DI - 而应该使用构造函数或属性注入。

此外,从你发布的代码片段中,我可以感受到服务定位器反模式的存在。

如果你想创建调查问卷,则应使用调查问卷工厂,并将其注入到使用它的类中。 DI容器通常不用于创建值对象,而你的调查问卷类(尽管不必要地拥有一个接口)看起来就像一个值对象。

在深入使用DI容器之前,我真的建议你学习更多关于DI如何工作以及使用DI编写干净代码的技术。然后才开始使用DI容器。反过来做只会导致错误,最终使代码难以维护。


亲爱的Stefan,感谢您的评论。您能举些例子说明我为什么不应该以那种方式使用DI容器吗?从这个角度来看,字段注入需要什么? - Aptem

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接