Delphi: 使用接口调用父构造函数(Spring4D框架)
Delphi: Invoke parent constructor using interface (Spring4D framework)
我遇到了从 Spring4D 框架容器解析的类型中正确实例化对象的问题。
我有一个 class:
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 创建一个后代 class:
type
TFieldSurvey = class ( TSurvey )
...
end;
问题是如何为 TFieldSurvey 更正实例化对象 class?
如果我使用Create(),那么我得到一个异常:
fieldSurvey := TFieldSurvey.Create ();
fieldSurvey.Organization.Id := 5 <- exception is here
是否必须在 TFieldSurvey 构造函数中显式调用 Organization 字段的构造函数,还是有其他方法?例如,使用 GlobalContainer?
提前致谢。
注入仅在您通过容器创建对象时起作用,而不是通过直接调用对象的构造函数来起作用。所以你需要用 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
,这让你可以选择你想要的 class 实例,否则你最终会得到为该接口注册的最后一个实现。如果 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');
您不应以仅适用于 DI 容器的方式编写代码。
DI 容器是一个工具,您应该避免(直接或间接)依赖于它。
这意味着您应该避免使用字段注入,因为这样的代码不能与 pure DI 一起使用 - 请改用构造函数或 属性 注入。
另外,从您发布的代码片段中,我可以闻到 service locator anti pattern。
如果您想创建调查,请使用调查工厂并将其注入到您正在使用它的 class 中。 DI 容器通常不用于创建值对象,您的调查 class(尽管不必要地具有接口)看起来像一个。
在深入使用 DI 容器之前,我真的建议您更多地了解 DI 的工作原理以及哪些技术可以让您使用 DI 编写干净的代码。然后才开始使用 DI 容器。反其道而行之只会导致错误,最终使代码更难维护。
我遇到了从 Spring4D 框架容器解析的类型中正确实例化对象的问题。
我有一个 class:
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 创建一个后代 class:
type
TFieldSurvey = class ( TSurvey )
...
end;
问题是如何为 TFieldSurvey 更正实例化对象 class?
如果我使用Create(),那么我得到一个异常:
fieldSurvey := TFieldSurvey.Create ();
fieldSurvey.Organization.Id := 5 <- exception is here
是否必须在 TFieldSurvey 构造函数中显式调用 Organization 字段的构造函数,还是有其他方法?例如,使用 GlobalContainer?
提前致谢。
注入仅在您通过容器创建对象时起作用,而不是通过直接调用对象的构造函数来起作用。所以你需要用 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
,这让你可以选择你想要的 class 实例,否则你最终会得到为该接口注册的最后一个实现。如果 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');
您不应以仅适用于 DI 容器的方式编写代码。
DI 容器是一个工具,您应该避免(直接或间接)依赖于它。
这意味着您应该避免使用字段注入,因为这样的代码不能与 pure DI 一起使用 - 请改用构造函数或 属性 注入。
另外,从您发布的代码片段中,我可以闻到 service locator anti pattern。
如果您想创建调查,请使用调查工厂并将其注入到您正在使用它的 class 中。 DI 容器通常不用于创建值对象,您的调查 class(尽管不必要地具有接口)看起来像一个。
在深入使用 DI 容器之前,我真的建议您更多地了解 DI 的工作原理以及哪些技术可以让您使用 DI 编写干净的代码。然后才开始使用 DI 容器。反其道而行之只会导致错误,最终使代码更难维护。