解耦依赖于构造函数接受参数的另一个类的类



我一直在练习如何使用SOLID编写干净的代码。我也一直在代码中使用 DI 来消除耦合,但只能通过构造函数注入。在我使用 DI 的许多情况下,我只使用它来调用方法。我还不明白的是,当你有一个依赖类,其构造函数在另一个类中接受参数时,如何解耦。如果var obj = new A(month)在类 B 中创建依赖和紧密耦合,我如何解耦/抽象它?这是与属性的接口进来的地方吗?如果是这样,我如何在此处使用它?

public class A 
{
private string _month;
public A(string month) 
{
_month = month;
}
}
public class B 
{
public List<A> ListOfMonths;
public B() 
{
ListOfMonths = new List<A>();
}
public List<A> SomeMethod() 
{
string[] months = new [] 
{
"Jan",
"Feb",
"Mar"
};
foreach(var month in months) 
{
var obj = new A(month); // If this is coupling, how do I remove it?
ListOfMonths.Add(obj)
}
return ListOfMonths;
}
}

如果要解耦,则需要从 B 中删除对 A 的任何引用,并将它们替换为 IA(类似于 A 的接口),IA 是任何将替换 A 的类的占位符。

在 B 的构造函数中,你提供了一个能够创建 IA 实例的工厂。通过放置抽象工厂,您可以更进一步,这意味着您提供了一个能够创建 IA 实例的工厂接口。

下面是基于您的代码的示例:

public interface IA
{
}
public interface IAFactory
{
IA BuildInstance(string month);
}
public class AFactory : IAFactory
{
public IA BuildInstance(string month)
{
return new A(month);
}
}
public class A : IA
{
public A(string month)
{
}
}
public class B
{
private readonly IAFactory factory;
public List<IA> ListOfMonths;
public B(IAFactory factory)
{
this.factory = factory;
ListOfMonths = new List<IA>();
}
public List<IA> SomeMethod()
{
string[] months = new[] {"Jan", "Feb", "Mar"};
foreach (var month in months)
{
var obj = factory.BuildInstance(month);
ListOfMonths.Add(obj);
}
return ListOfMonths;
}
}

TL;DR - 没有需要修复的明显耦合。依赖抽象是很好的,但有可能得意忘形并将它们添加到我们不需要它们的地方。从其他类的角度创建抽象,当您知道需要它们时,这些类将依赖于它们,以便您始终编写所需的代码,而不是不需要的代码。


对于您的类来说,创建A实例并不是很好的耦合。创建A实例是B的明显目的。B不依赖于A,因为它不以任何方式使用它。它只是创建A并返回它,因为这是它应该做的。

另一种看待它的方式 - 返回A的方法如何不以某种方式耦合到AB不需要工厂。这是一个工厂。

也没有明显的理由为什么你需要一个抽象来表示A。如果你需要能够模拟A你可以定义一个像IA这样的接口。但此处显示的任何内容均未表明您需要它。创建一个"真实"的A实例非常容易,以至于不需要能够模拟它。

var a = new A("February");.有什么好嘲笑的?

如果你确实需要A的抽象(如IA),那么B中立即需要的唯一更改就是更改SomeMethod以返回List<IA>而不是List<A>。在该方法中,您将创建一个List<IA>而不是List<A>,但您仍然会用A的具体实例填充它。

这将删除的耦合(正如我所强调的,在您的代码中未显示)将与其他类(未显示)一起使用,这些类当前依赖于A,并且依赖于B来提供它。现在它们将依靠B来提供IA,所以那些其他类将不再耦合到A

抽象很棒,但它可能会成为一个兔子洞,我们开始添加我们并不真正需要的接口和工厂。(当您完成用IA替换A时会发生什么?现在你已经和List<T>结合 - 怎么办?如果你不能在不测试一个类的依赖关系的情况下测试它,这是一个好兆头,表明依赖关系必须是你可以模拟的抽象。

如果你可以测试所有东西——在这种情况下,你可以——那么引入更多的抽象只是创建你不需要的工作。这并不是说我永远不会从抽象开始。在许多情况下,很明显我会需要一个。这通常是在我要对依赖项做一些事情的情况下。在这种情况下,您只是将其退回。


这是一种不同的看待它的方式:如果您需要一个抽象,它很可能是一个抽象来表示B。如果其他类(此处未显示)需要AIA的实例,并且创建它们的逻辑稍微复杂一些,那么这些类可能依赖于工厂,例如

interface ISomethingFactory
{
List<IA> Create();
}

B将是该工厂的实施

我们应该从依赖于它们的类的角度来定义抽象。他们将如何与这种依赖关系相互作用?我是否需要嘲笑这种依赖关系,如果是这样,我将如何做到这一点?当我们把类看作是依赖于(或需要)依赖关系时,我们就是根据实际需求编写代码的。这可以防止我们在编写不需要的代码时陷入兔子洞(我已经做过无数次了)。

BA分离的方法之一是定义A的合约

interface IA { }

A实施

public class A : IA {
private string _month;
public A(string month) {
_month = month;
}
}

然后从B将所有对A的引用替换为IA

可以使用实例化依赖关系工厂来解决实例化依赖关系

public class AFactory {
public IA CreateIA(string month) {
return new A(month);
}
}

AFavtory可以在需要时用 B 实例化(在SomeMethod)

解耦可以通过定义工厂类的契约将解耦提升到另一个层次

interface IAFactory {
IA CreateIA(string month);
}

并与AFactory执行此合同.

之后,可以将IAFactory的实例注入到B构造 函数

public B(IAFactory aFactory){
_aFactory = aFactory;
...
}

相关内容

最新更新