我正在使用 C# 4.7.2 和 PRISM 6 和 Unity 。
现在,在循环中需要 SomeClass 的实例。对于每个循环运行,我需要一个新的 SomeClass 实例。通用实现类似于MyMethod_CommonImplementation中的代码。
如何在 DI 模式中正确实现该代码(在 MyMethod 中(?当然,我可以注入 UnityContainer 并使用容器。解决。但我相信这将是提供服务,而不是依赖注入。
该示例显示了一个执行五次运行的循环。所以我可以注入 SomeClass 的五个实例。但这真的是正确的方法吗?
顺便说一下,注册工作正常,_exampleName设置正确。
public class MyClass
{
IMyInterface _exampleName;
public MyClass(IMyInterface exampleName)
{
_exampleName = exampleName;
}
private void MyMethod()
{
for ( int index = 0 ; index < 5 ; index++ )
{
// at this place I want to "reset" the instance of _exampleName for each index
_exampleName.PropertyName = index
_exampleName.DoSomeImportantWork();
}
}
private void MyMethod_CommonImplementation()
{
for ( int index = 0 ; index < 5 ; index++ )
{
SomeClass exampleClassName = new SomeClass();
exampleClassName.PropertyName = index
exampleClassName.DoSomeImportantWork();
}
}
}
首先,非常感谢您的回答和所有工作。
首先,示例
private void MyMethod_CommonImplementation()
{
for ( int index = 0 ; index < 5 ; index++ )
{
SomeClass exampleClassName = new SomeClass();
exampleClassName.PropertyName = index
exampleClassName.DoSomeImportantWork();
}
}
不是真实代码的一部分,我用它来阐明我需要什么。
在实际代码中,SomeClass 派生自服务器接口,这些接口将SomeClass的功能分离成属于一起的部分。
独立于任何 DI,实现将如下所示
private void MyMethod_CommonImplementation()
{
for ( int index = 0 ; index < 5 ; index++ )
{
ISomeFunctionality exampleFunc = new SomeClass();
exampleFunc.PropertyName = index
exampleFunc.DoSomeImportantWork();
}
}
事实上,人们仍然可以争辩说,这仍然是时间耦合。但是出于稳定性和可维护性的原因,我更喜欢这种实现而不是重载方法。但是,DoSomeImportantWork((会验证给定的数据。
在您的回答之后,我正在实现工厂模式。所以我的实现看起来像这样,现在
public interface IFactory<T>
{
T CreateInstance();
}
public interface ISomeClass
{
int PropertyName { get; set; }
void DoSomeImportantWork();
}
internal SomeClass : ISomeClass, IFactory<ISomeClass>
{
public int PropertyName { get; set; }
public void DoSomeImportantWork()
{
// ...
}
public ISomeClass CreateInstance()
{
return new SomeClass();
}
}
public class MyClass
{
IFactory<ISomeClass> _exampleFactory;
public MyClass(IFactory<ISomeClass> exampleFactory)
{
_exampleFactory = exampleFactory;
MyMethod();
}
private void MyMethod()
{
for ( int index = 0 ; index < 5 ; index++ )
{
ISomeClass exampleName = _exampleFactory.CreateInstance();
exampleName.PropertyName = index;
exampleName.DoSomeImportantWork();
}
}
}
我选择了工厂模式而不是代理模式,因为如果我做对了,我将不得不在第三个类中实现代理。
不过,我想知道,这是否是一种正确的方法。
您的代码受到多种设计气味的影响。让我们剖析您的代码。
在代码中执行以下操作:
_exampleName.PropertyName = index;
_exampleName.DoSomeImportantWork();
此代码表现出时间耦合设计气味。尽管在调用DoSomeImportantWork
之前必须设置PropertyName
,但在结构层面上,这个 API 没有给我们任何时间耦合的指示。
除此之外,通过改变IMyInterface
的状态,它使IMyInterface
有状态。但是,从使用者的角度来看,通过 DI 注入的服务应该是无状态的。或者如依赖注入原则、实践和模式所述:
从概念上讲,服务抽象只有一个实例。在使用者的生存期内,它不应关注依赖项的多个实例可能存在的可能性。否则,这会给消费者带来不必要的复杂性,这意味着抽象不是为了他们的利益而设计的。[第6.2节]
因此,要解决此问题,最好通过抽象方法传递该运行时值,如下所示:
_exampleName.DoSomeImportantWork(index);
这消除了时间耦合,并且消费者无需知道可能涉及多个实例,甚至可能完全不需要拥有多个实例。
但是,如果仍然必须创建多个短期实例,您可以将它们的存在隐藏在代理后面:
public class MyInterfaceProxy : IMyInterface
{
public void DoSomeImportantWork(int index)
{
var instance = new MyInterfaceImpl();
instance.DoSomeImportantWork(index);
}
}
您现在可以注入MyInterfaceProxy
,而不是将MyInterfaceImpl
直接注入MyClass
。
但是请注意,有很多方法可以定义此类代理。如果您MyInterfaceProxy
组合根的一部分,您甚至可以安全地将容器注入代理。这不会导致服务定位器反模式,因为服务定位器只能存在于组合根之外。 代码中发生的另一件事如下:
SomeClass exampleClassName = new SomeClass();
exampleClassName.PropertyName = index
exampleClassName.DoSomeImportantWork();
根据SomeClass
的功能和实现,代码可能会表现出 Control Freak 反模式(这是一种特定于 DI 的方式,表示您违反了依赖反转原则(。当SomeClass
包含某些不确定行为,或者包含要在以后的时间点模拟、替换或拦截的行为时,就是这种情况。在这种情况下,我们称SomeClass
为易失性依赖(参见本书第1.3.2节(。易失性依赖项应隐藏在抽象后面,并通过构造函数注入注入。
根据问题中提供的信息,SomeClass
是否真的是易失性依赖是不可能回答的。但是当它是时,它应该有一个类似于IMyInterface
的代码结构。