可能最简单的方法是使用抽象工厂。大多数 IOC 框架都可以为您自动创建它们,但这里是您可以手动完成的方法(我总是喜欢先手动完成,所以我知道它可以工作,然后您可以查看该框架如何帮助您实现自动化)
现在要提一件事 - 我建议对最终解决方案的工作方式进行轻微调整,但一旦我展示了它目前的工作方式,我将继续讨论它。下面的例子假设Ninject,请原谅任何错别字等。
首先为你的依赖创建一个接口
public interface IDependentObject
{
void DoSomething();
}
然后为IDependentObject的每个具体实现声明空标记接口
public interface INormalDependentObject:IDependentObject{};
public interface ISpecialDependentObject:IDependentObject{}
并实施它们:
public class NormalDependentObject:INormalDependentObject
{
readonly int _clientID;
public DependentObject(int clientID)
{
_clientID=clientID;
}
public void DoSomething(){//do something}
}
public class DependentObject:ISpecialDependentObject
{
readonly int _clientID;
public DependentObject(int clientID)
{
_clientID=clientID;
}
public void DoSomething(){//do something really special}
}
当然,正如您所提到的,您可能有更多 IDependentObject 的实现。
可能有一种更优雅的方式可以让您的 IOC 框架在运行时解析而无需声明标记接口;但现在我发现使用它们很有用,因为它使绑定声明易于阅读:)
接下来,声明IDependentObjectFactory的接口和实现:
public interface IDependentObjectFactory
{
IDependentObject GetDependenObject(int clientID);
}
public class DependentObjectFactory: IDependentObjectFactory
{
readonly _kernel kernel;
public DependentObjectFactory(IKernel kernel)
{
_kernel=kernel;
}
public IDependentObject GetDependenObject(int clientID)
{
//use whatever logic here to decide what specific IDependentObject you need to use.
if (clientID==100)
{
return _kernel.Get<ISpecialDependantObject>(
new ConstructorArgument("clientID", clientID));
}
else
{
return _kernel.Get<INormalDependentObject>(
new ConstructorArgument("clientID", clientID));
}
}
}
将这些连接到您的合成根中:
_kernel.Bind<INormalDependentObject>().To<NormalDependentObject>();
_kernel.Bind<ISpecialDependentObject>().To<SpecialDependentObject>();
_kernel.Bind<IDependentObjectFactory>().To<DependentObjectFactory>();
最后将你的工厂注入到服务类中:
public class MyService
{
IDependentObject _dependentObject;
readonly IDependentObjectFactory _factory;
//in general, when using DI, you should only have a single constructor on your injectable classes. Otherwise, you are at the mercy of the framework as to which signature it will pick if there is ever any ambiguity; most all of the common frameworks will make different decisions!
public MyService(IDependentObjectFactory factory)
{
_factory=factory;
}
public void DoAThing(int clientID)
{
var dependent _factory.GetDependentObject(clientID);
dependent.DoSomething();
}
}
建议的更改
- 与上述结构的一个直接变化是我将
clientID 排除在服务构造函数之外,并将其移至DoAThing 的方法参数;这是因为服务本身是无状态的对我来说更有意义;当然,根据您的情况,您可能不想这样做。
我提到我有一个轻微的调整建议,就是这样;上面的解决方案取决于(没有双关语!)IDependentObject 的实现具有具有此签名的构造函数:
public SomeDependency(int clientID)
如果他们没有该签名,那么工厂将无法工作;就我个人而言,我不希望我的 DI 必须了解有关构造函数参数的任何信息,因为它使您摆脱了纯粹处理接口并迫使您在具体类上实现特定的 ctor 签名。
这也意味着由于强制 ctor 签名,您无法可靠地使您的 IDependentObjects 成为整个 DI 过程的一部分(即它们本身具有您希望框架解决的依赖关系图)。
出于这个原因,我建议将IDependentObject.DoSomething() 本身更改为DoSomething(int clientID),这样您就可以省略工厂代码的new ConstructorArgument 部分;这意味着您的 IDependentObject 现在都可以具有完全不同的 ctor 签名,这意味着如果需要它们可以具有不同的依赖关系。当然这只是我的看法,你会知道在你的具体情况下什么最有效。
希望对您有所帮助。