我真的很感谢其他人的回答。他们会帮助我解决这个问题。我很可能会接受 Remo 的回答,因为它符合我当前实际面临的问题。
根据我的理解,我还想就我提出的这个更广泛的答案获得反馈。
我不确定依赖注入是否直接支持我刚才谈到的机制,通过构造函数、属性或方法注入。在这一点上,我认为这些是“纯粹的”DI——尽管我愿意被影响。
我认为注入依赖意味着一个相对静态的对象图。它可以从配置文件加载,也可以通过程序生成,但它无法直接适应不可知的运行时状态,例如用户反复请求新实例。
但是,在考虑了其中一些替代方案后,我开始认为有一些解决方法可以支持纯度,而且我所描述的纯度可能并不像我想象的那么重要。一些不太“纯粹”的选项仍然可以以一种基本干净的方式与大多数容器一起使用,并且似乎很容易为容器添加支持以在其余部分清理它们。
这是我目前考虑过的变通方法(其中一些已经提到过)。
参考定制工厂中的容器,然后洗手:
您的组件可以随心所欲地实现。您可以使用任何您想要的容器(只要它支持瞬态实例)。您只需要接受这样一个事实,即您将向代码中注入工厂,并且这些工厂将直接从容器中解析。当你可以务实时,谁需要纯洁?
示例代码:
public class ComponentFactory // Might inherit from an interface...
{
private readonly IContainer container;
public ComponentFactory(IContainer container)
{
this.container = container;
}
public IComponent Create(IOtherComponent otherComponent)
{
return container.Get<IComponent>(otherComponent);
}
}
使用容器特定的工厂扩展:
您的组件可以随心所欲地实现。但是您的容器必须直接支持将工厂注入您的代码,并自动实现这些工厂,这样他们就不必对容器有任何特定的了解。
示例代码:
// Black magic - the container implemented it for us!
// But the container basically implemented our code from the previous example...
public interface IComponentFactory
{
public IComponent Create(IOtherComponent otherComponent);
}
使用容器特定的对象池:
确保您的组件是无状态的,并允许它们被池化。容器将负责为您分配或池化对象。这或多或少是一个具有奇特实现的托管工厂,您必须同时分配和解除分配。
伪代码(我之前没有使用过基于容器的对象池):
public class SomeUI
{
private readonly IComponentPool componentPool;
public OtherComponent(IComponentPool componentPool)
{
this.componentPool = componentPool;
}
public void DoSomethingWhenButtonPushed()
{
var component = componentPool.Get();
component.DoSomething();
componentPool.Release(component);
}
}
此伪代码的优点是您不必为工厂定义接口。缺点是你必须依赖池接口,所以你的容器有它的卷须。我也没有将任何东西传递给Get 方法。这可能是有道理的,因为对象必须支持实例重用。
如果真正的池不是这样工作的,它们可能看起来与上面的“容器特定工厂扩展”示例相同,只是它们总是需要 Release 方法和 Create 方法。
使用享元模式:
(Flyweight Pattern - 不确定我是否正确识别了模式,或者我只是有一个奇怪的用途)
注入充当对象行为的无状态组件,或“重量级”组件。使用传递给行为组件的享元状态对象支持组件的单独“实例”,或者让它们包装行为组件。
这将极大地影响组件的架构。您的实现必须是无状态的,并且您的状态对象的设计方式必须使其适用于所有可能的组件实现。但它完全支持“纯”注入模型(只注入到构造函数、属性、方法中)。
但这不适用于 UI。视图类通常需要直接创建,我们不希望我们的“享元”成为 UI 类...
public class ComponentState
{
// Hopefully can be less generic than this...
public Dictionary<string, object> Data { get; set; }
}
public interface IComponent
{
int DoSomething(ComponentState state);
}
public SomeUI
{
private readonly IComponent component;
public OtherComponent(IComponent component)
{
this.component = component;
}
public void DoSomethingWhenButtonPushed()
{
var state = new ComponentState();
component.DoSomething(state);
}
}
为用户请求的每个新实例使用子容器:
容器在从单个根创建对象图时效果最佳。与其试图与之抗争,不如与之抗争。当用户单击一个按钮来创建算法的新实例时,为这些对象创建一个新的子容器并调用全局配置代码,但这需要完成。然后将子容器附加到父容器。
这意味着生成代码需要在某种程度上了解容器。也许把它包装在工厂里是最好的方法。
public class SubComponentFactory // Might inherit from an interface...
{
private readonly IContainer container;
public ComponentFactory(IContainer container)
{
this.container = container;
}
public IComponent Create(IOtherComponent otherComponent)
{
// Todo: Figure out any lifecycle issues with this.
// I assume the child containers get disposed with the parent container...
var childContainer = container.CreateChildContainer();
childContainer.Configure(new SubComponentConfiguration());
return childContainer.Get<SubComponent>(otherComponent);
}
}
有点像我们开始的地方。但是我们有一个新的对象图根,所以我不确定我是否可以使用服务定位器模式来调用它。这种方法的问题是与容器耦合最紧密。不仅直接引用容器,而且工厂依赖于容器支持子容器的实现细节。