Unity DI 替换容器.正确解析



我正在使用 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的代码结构。

最新更新