gpt4 book ai didi

parameters - 非默认构造函数中的IOC容器处理状态参数

转载 作者:行者123 更新时间:2023-12-04 01:58:18 24 4
gpt4 key购买 nike

出于讨论的目的,对象构造函数可能采用两种参数:状态依赖性或服务依赖性。使用IOC容器提供服务依赖关系很容易:DI接管了。但是相比之下,状态依赖项通常仅是客户端知道的。即,对象请求者。

事实证明,让客户通过IOC容器提供状态参数非常痛苦。我将展示几种不同的方法来解决此问题,所有这些方法都有很大的问题,并询问社区是否还有其他选择。让我们开始:

在将IOC容器添加到我的项目代码之前,我从这样的类开始:

class Foobar {
//parameters are state dependencies, not service dependencies
public Foobar(string alpha, int omega){...};
//...other stuff
}


我决定将Logger服务依赖关系添加到Foobar类,也许我将通过DI提供:

class Foobar {
public Foobar(string alpha, int omega, ILogger log){...};
//...other stuff
}


但是随后我又被告知,我需要使Foobar类本身“可互换”。也就是说,我需要对Foobar实例进行服务定位。我添加了一个新的界面:

class Foobar : IFoobar {
public Foobar(string alpha, int omega, ILogger log){...};
//...other stuff
}


当我进行服务定位器调用时,它将为我创建ILogger服务依赖项。不幸的是,状态依赖Alpha和Omega并非如此。一些容器提供了解决此问题的语法:

//Unity 2.0 pseudo-ish code:
myContainer.Resolve<IFoobar>(
new parameterOverride[] { {"alpha", "one"}, {"omega",2} }
);


我喜欢该功能,但我不喜欢该功能未键入且对开发人员不明显,必须传递哪些参数(通过intellisense等)。所以我看另一种解决方案:

//This is a "boiler plate" heavy approach!
class Foobar : IFoobar {
public Foobar (string alpha, int omega){...};
//...stuff
}
class FoobarFactory : IFoobarFactory {
public IFoobar IFoobarFactory.Create(string alpha, int omega){
return new Foobar(alpha, omega);
}
}

//fetch it...
myContainer.Resolve<IFoobarFactory>().Create("one", 2);


上面的方法解决了类型安全和智能感知的问题,但是(1)强制类Foobar通过服务定位器而不是DI来获取ILogger,并且(2)要求我制作一堆样板(XXXFactory,IXXXFactory)对于我可能使用的所有Foobar实现,如果我决定采用纯服务定位器方法,那可能不是问题。但是我仍然无法忍受完成这项工作所需的所有模板。

因此,然后尝试使用容器提供的支持的另一种形式:

//overall, this is a pseudo-memento pattern.
class Foobar : IFoobar {
public Foobar (FoobarParams parms){
this.alpha = parms.alpha;
this.omega = parms.omega;
};
//...stuff
}

class FoobarParams{
public FoobarParams(string alpha, int omega){...};
}

//fetch an instance:
FoobarParams parms = new FoobarParams("one",2);
//Unity 2.0 pseudo-code...
myContainer.resolve<IFoobar>(new parameterOverride(parms) );


通过这种方法,我已经中途恢复了智能。但是我必须等到运行时才能检测到错误,而我可能忘记提供“ FoobarParams”参数。

因此,让我们尝试一种新方法:

//code named "concrete creator"
class Foobar : IFoobar {
public Foobar(string alpha, int omega, ILogger log){...};
static IFoobar Create(string alpha, int omega){
//unity 2.0 pseudo-ish code. Assume a common
//service locator, or singleton holds the container...
return Container.Resolve<IFoobar>(
new parameterOverride[] {{"alpha", alpha},{"omega", omega} }
);
}

//Get my instance:
Foobar.Create("alpha",2);


我实际上并不介意使用具体的“ Foobar”类来创建IFoobar。它代表了我不希望在代码中更改的基本概念。我也不介意在静态“创建”中缺少类型安全性,因为它已经被封装了。我的智慧也正在工作!如果这样的具体实例不适用,则它们将忽略提供的状态参数(Unity 2.0行为)。也许其他的具体实现“ FooFoobar”可能有一个正式的arg名称不匹配,但我对此仍然很满意。

但是这种方法的最大问题是,它只能与Unity 2.0一起有效(Structure Map中不匹配的参数将引发异常)。因此,仅当我留在Unity时,这才是好事。问题是,我开始非常喜欢Structure Map。因此,现在我进入另一个选择:

class Foobar : IFoobar, IFoobarInit {
public Foobar(ILogger log){...};

public IFoobar IFoobarInit.Initialize(string alpha, int omega){
this.alpha = alpha;
this.omega = omega;
return this;
}
}

//now create it...
IFoobar foo = myContainer.resolve<IFoobarInit>().Initialize("one", 2)


现在,我与其他方法有了一些不错的折衷:(1)我的参数支持类型安全/智能感知(2)我可以选择通过DI(如上所示)或服务定位符获取ILogger,( 3)无需制作一个或多个单独的具体FoobarFactory类(与之前的冗长的“样板”示例代码进行对比),并且(4)合理地坚持了“使接口易于正确使用且难以实现的原则”。使用不当。”至少可以说它并不比前面讨论的替代方案差。

一个接受障碍仍然存在:我还想应用“按合同设计”。

我提出的每个示例都故意支持构造函数注入(用于状态依赖性),因为我想保留最常用的“不变”支持。即,当构造函数完成时就建立了不变式。

在上面的示例中,对象构建完成后未建立不变式。只要我正在进行自行设计的“按合同设计”,我就可以告诉开发人员在调用Initialize(...)方法之前不要测试不变式。

但更重要的是,当.net 4.0推出时,我想使用其“代码契约”支持按契约进行设计。根据我的阅读,它与最后一种方法不兼容。

诅咒!

当然,我也想到我的全部哲学思想都不对。也许有人会告诉我,通过服务定位器来构造Foobar:IFoobar意味着它是一项服务-并且服务仅具有其他服务依赖项,而没有状态依赖项(例如这些示例的Alpha和Omega)。我也乐于听这样的哲学问题,但是我也想知道阅读什么半权威性的参考文献会引导我走上这一思考道路。

所以现在我把它转向社区。我应该考虑采用哪种方法呢?我是否必须真的相信我已经用尽了所有选择?

ps。 along with others这种问题促使我相信,至少在.net中,整个IOC容器的想法还为时过早。它使我想起了人们站起来让“ C”语言感到面向对象(添加怪异的宏等)的日子。我们应该寻找的是IOC容器的CLR和语言支持。例如,设想一种称为“启动”的新型接口。发起程序的工作方式类似于接口,但也需要特定的构造函数签名。剩下的我留给学生做练习...

最佳答案

您正在寻找的是“工厂适配器”或Func

需要动态创建另一个组件的参数化实例的组件使用Func 依赖关系类型来表示以下内容:

class Bar
{
Func<string, int, IFoo> _fooCreator;

public Bar(Func<string, int, IFoo> fooCreator) {
_fooCreator = fooCreator;
}

public void Go() {
var foo = _fooCreator("a", 42);
// ...
}
}


例如,只要注册了IFoo,Autofac就会自动提供Func。它还会将参数合并到Foo构造函数中(包括ILogger),并丢弃所有不必要的参数,而不是抛出错误。

Autofac还支持自定义委托,例如:

delegate IFoo FooCreator(string alpha, int omega);


这样可以重写Bar:

class Bar
{
FooCreator _fooCreator;

public Bar(FooCreator fooCreator) {
_fooCreator = fooCreator;
}

public void Go() {
var foo = _fooCreator("a", 42);
// ...
}
}


使用自定义委托时,参数将按名称匹配,而不是像Func那样按类型匹配。

Autofac提供了一些文档,您可以查看: http://code.google.com/p/autofac/wiki/DelegateFactories

在几个IOC容器社区之间的工作中有一个协作项目,称为“公共上下文适配器”,以标准化这些以及其他高阶依赖类型。项目站点位于 http://cca.codeplex.com

CCA提出的第一个规范涵盖了工厂适配器,您可以在此处阅读: http://cca.codeplex.com/wikipage?title=FuncFactoryScenarios&referringTitle=Documentation

其他一些链接可能会有所帮助:

http://nblumhardt.com/2010/04/introducing-autofac-2-1-rtw/
http://nblumhardt.com/2010/01/the-relationship-zoo/

我希望您会发现,尽管IoC容器在完全变得透明之前还有很长的路要走,但实际上我们正在相当努力地实现这一目标:)

缺口

关于parameters - 非默认构造函数中的IOC容器处理状态参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2656548/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com