我已经使用了以下两种模式来实现可测试性,我想知道哪种OOP / SOLID更好。
模式1:提供两个构造函数,其中一个创建具体的依赖关系
public MyClass
{
private IDependency dependency;
public MyClass() : this(new ConcreteDependency()) { }
public MyClass(IDependency dependency)
{
this.dependency = dependency;
}
}
模式2:基类具有依赖关系初始化器,派生类以具体的依赖关系对其进行调用
public abstract MyClassBase
{
private IDependency dependency;
protected void Initialize(IDependency dependency)
{
this.dependency = dependency;
}
}
public MyClass : MyClassBase
{
public MyClass()
{
Initialize(new ConcreteDependency());
}
}
答案 0 :(得分:2)
如Dependency Injection, Second Edition Principles, Practices, Patterns中所述,两个示例均提供了 Control Freak 反模式。
发生控制怪胎反模式:
您每次在Composition Root以外的任何地方都依赖 Volatile Dependency 。这违反了Dependency Inversion Principle
在第一个示例中,即使MyClass
使用了IDependency
抽象,它也会沿着对具体ConcreteDependency
组件的引用进行拖动,导致这两个类紧密耦合。
在您的第二个示例中,发生了同样的紧密耦合,并且实际上是相同的反模式。此外,第二个示例甚至使用Initialize
方法将依存关系应用于已经创建的MyClassBase
实例。这会导致Temporal Coupling,它本身就是一种代码味道。
正确的解决方案可防止引起紧密耦合和时间耦合,这意味着您使用构造函数注入并仅定义一个构造函数:
public MyClass
{
private IDependency dependency;
public MyClass(IDependency dependency)
{
if (dependency == null) throw new ArgumentNullException("dependency");
this.dependency = dependency;
}
}
答案 1 :(得分:0)
您的两种模式的不利之处在于它们可能会破坏Dependency Inversion Principle:MyClass
是更抽象的东西,但是知道ConcreteDependency
是更具体的东西。确实:
MyClass
类,ConcreteDependency
类都必须可用。ConcreteDependency
更改为ConcreteDependency2
,则必须更改您的MyClass
类,在某些情况下这是不好的,例如:有{ {1}}从未使用过默认构造函数的人。更不用说,模式2可能会使您的对象处于意外状态,因为派生类可能会忘记调用MyClass
方法,除非您的文档非常好。
我建议使用工厂模式:
Initialize