通常情况下,我们会采取以下措施:
public class MyService
{ DependentObject _dependentObject;
public MyService(int clientSiteID)
{
// ...
_dependentObject = new dependentObjectFactory(clientSiteID).GetDependentObject();
}
public void DoAThing()
{
//...
_dependentObject.DoSomething();
}
}
我想做什么:
public class MyService
{ DependentObject _dependentObject;
public MyService(int clientSiteID)
{
// ...
_dependentObject = MyTypeResolver.GetWIthClientContext<IDependentObject>(clientSiteID);
}
public MyService(int clientSiteID, IDependentObject dependentObject)
{
// ...
_dependentObject = dependentObject;
}
public void DoAThing()
{
//...
_dependentObject.DoSomething();
}
}
我会设置IoC容器,以便我可以使用MyTypeResolver
传递clientSiteID
,并让容器调用我的DependentObjectFactory
并返回正确的对象结果
我是IoC容器的新手,虽然我正在努力学习文献,但我觉得它可能比我制作它更容易,所以我要问这里。
答案 0 :(得分:2)
最简单的方法可能是使用抽象工厂。大多数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签名。
这也意味着您无法可靠地使IDependentObjects
成为整个DI流程的一部分(即,他们自己拥有您希望框架解析的依赖图),因为强迫ctor签名。
出于这个原因,我建议将IDependentObject.DoSomething()
本身更改为DoSomething(int clientID)
,以便您可以忽略工厂代码的new ConstructorArgument
部分;这意味着您的IDependentObject
现在可以拥有完全不同的ctor签名,这意味着如果需要,它们可以具有不同的依赖关系。当然这只是我的意见,你会知道在你的具体情况下哪种方法最有效。
希望有所帮助。