解耦一个 class 依赖于另一个 class 的构造函数接受参数的 class
Decoupling a class that depends on another class whose constructor takes an argument
我一直在练习如何使用 SOLID 编写干净的代码。我也一直在我的代码中使用 DI 来消除耦合,但只能通过构造函数注入。在我使用 DI 的许多情况下,我只是用它来调用方法。我还不明白的是当你有一个从属 class 的构造函数在另一个 class 中接受参数时如何解耦。如果 var obj = new A(month)
在 class B 中创建依赖和紧耦合,我该如何 decouple/abstract 呢?这是 属性 接口的用武之地吗?如果是这样,我如何在这里使用它?
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 的接口),它是任何 class 的占位符,它将替换 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;
}
}
将 B
与 A
分离的方法之一是为 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
)
通过为工厂定义合同,可以将解耦提升到另一个层次class
interface IAFactory {
IA CreateIA(string month);
}
并与 AFactory
执行此合同。
之后 IAFactory
的实例可以注入到 B
中
构造函数
public B(IAFactory aFactory){
_aFactory = aFactory;
...
}
TL;DR - 没有您需要修复的明显耦合。依赖抽象很棒,但也可能会得意忘形,将它们添加到我们不需要的地方。从其他 class 依赖它们的角度创建抽象 当你知道你需要它们时 这样你总是在编写你需要的代码,而不是你不需要的代码不需要。
为您 class 创建 A
的实例是不错的耦合。创建 A
的实例是 B
的明显目的。 B
不依赖于 A
,因为它不以任何方式使用它。它只是创建 A
和 return,因为这是它应该做的。
另一种看待它的方式 - returns A
的方法怎么能不以某种方式耦合到 A
? B
不需要工厂。它是一家工厂。
也没有明显的理由说明为什么需要抽象来表示 A
。如果你需要能够模拟 A
,你可以定义一个像 IA
这样的接口。但是这里没有任何显示表明您需要它。创建 A
的 "real" 实例非常容易,无需模拟它。
var a = new A("February");
。嘲笑什么?
并且如果您确实需要 A
的抽象(例如 IA
),那么 B
中立即需要的唯一更改是将 SomeMethod
更改为 return List<IA>
而不是 List<A>
。在该方法中,您将创建 List<IA>
而不是 List<A>
,但您仍会使用 A
的具体实例填充它。
这将删除的耦合(正如我强调的那样,未在您的代码中显示)将与其他 classes,未显示,目前取决于 A
, 并依赖 B
提供。现在他们将依赖 B
提供 IA
,因此 其他 classes 将不再耦合到 A
。
抽象很棒,但它可能会变成一个兔子洞,我们开始添加我们并不真正需要的接口和工厂。 (当你用 IA
替换 A
之后会发生什么?现在你耦合到 List<T>
- 怎么办?)如果你不能测试一个 class 也没有测试它的依赖关系,这是一个很好的迹象,表明依赖关系必须是一个你可以模拟的抽象。
如果您可以测试所有内容(在本例中,您可以),那么引入更多抽象只会创建您不需要的工作。这并不是说我永远不会从抽象开始。在许多情况下,很明显我需要一个。这通常是在我要对依赖项做一些事情的情况下。在这种情况下,您只是 returning 它。
这是另一种看待它的方式:如果你需要一个抽象,它可能是 表示 B
的抽象。如果此处未显示的其他 classes 需要 A
或 IA
的实例并且创建它们的逻辑稍微复杂一些,那么 those classes 可能取决于工厂,例如
interface ISomethingFactory
{
List<IA> Create();
}
B
将是该工厂的 实现 。
我们应该从依赖它们的 classes 的角度来定义抽象。他们将如何与这种依赖性互动?我是否需要模拟这种依赖性,如果需要,我将如何做?当我们将 classes 视为依赖(或 需要)依赖时,我们就是在根据实际需要编写代码。这可以防止我们陷入兔子洞,编写我们不需要的代码(我已经做过无数次了。)
我一直在练习如何使用 SOLID 编写干净的代码。我也一直在我的代码中使用 DI 来消除耦合,但只能通过构造函数注入。在我使用 DI 的许多情况下,我只是用它来调用方法。我还不明白的是当你有一个从属 class 的构造函数在另一个 class 中接受参数时如何解耦。如果 var obj = new A(month)
在 class B 中创建依赖和紧耦合,我该如何 decouple/abstract 呢?这是 属性 接口的用武之地吗?如果是这样,我如何在这里使用它?
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 的接口),它是任何 class 的占位符,它将替换 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;
}
}
将 B
与 A
分离的方法之一是为 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
)
通过为工厂定义合同,可以将解耦提升到另一个层次class
interface IAFactory {
IA CreateIA(string month);
}
并与 AFactory
执行此合同。
之后 IAFactory
的实例可以注入到 B
中
构造函数
public B(IAFactory aFactory){
_aFactory = aFactory;
...
}
TL;DR - 没有您需要修复的明显耦合。依赖抽象很棒,但也可能会得意忘形,将它们添加到我们不需要的地方。从其他 class 依赖它们的角度创建抽象 当你知道你需要它们时 这样你总是在编写你需要的代码,而不是你不需要的代码不需要。
为您 class 创建 A
的实例是不错的耦合。创建 A
的实例是 B
的明显目的。 B
不依赖于 A
,因为它不以任何方式使用它。它只是创建 A
和 return,因为这是它应该做的。
另一种看待它的方式 - returns A
的方法怎么能不以某种方式耦合到 A
? B
不需要工厂。它是一家工厂。
也没有明显的理由说明为什么需要抽象来表示 A
。如果你需要能够模拟 A
,你可以定义一个像 IA
这样的接口。但是这里没有任何显示表明您需要它。创建 A
的 "real" 实例非常容易,无需模拟它。
var a = new A("February");
。嘲笑什么?
并且如果您确实需要 A
的抽象(例如 IA
),那么 B
中立即需要的唯一更改是将 SomeMethod
更改为 return List<IA>
而不是 List<A>
。在该方法中,您将创建 List<IA>
而不是 List<A>
,但您仍会使用 A
的具体实例填充它。
这将删除的耦合(正如我强调的那样,未在您的代码中显示)将与其他 classes,未显示,目前取决于 A
, 并依赖 B
提供。现在他们将依赖 B
提供 IA
,因此 其他 classes 将不再耦合到 A
。
抽象很棒,但它可能会变成一个兔子洞,我们开始添加我们并不真正需要的接口和工厂。 (当你用 IA
替换 A
之后会发生什么?现在你耦合到 List<T>
- 怎么办?)如果你不能测试一个 class 也没有测试它的依赖关系,这是一个很好的迹象,表明依赖关系必须是一个你可以模拟的抽象。
如果您可以测试所有内容(在本例中,您可以),那么引入更多抽象只会创建您不需要的工作。这并不是说我永远不会从抽象开始。在许多情况下,很明显我需要一个。这通常是在我要对依赖项做一些事情的情况下。在这种情况下,您只是 returning 它。
这是另一种看待它的方式:如果你需要一个抽象,它可能是 表示 B
的抽象。如果此处未显示的其他 classes 需要 A
或 IA
的实例并且创建它们的逻辑稍微复杂一些,那么 those classes 可能取决于工厂,例如
interface ISomethingFactory
{
List<IA> Create();
}
B
将是该工厂的 实现 。
我们应该从依赖它们的 classes 的角度来定义抽象。他们将如何与这种依赖性互动?我是否需要模拟这种依赖性,如果需要,我将如何做?当我们将 classes 视为依赖(或 需要)依赖时,我们就是在根据实际需要编写代码。这可以防止我们陷入兔子洞,编写我们不需要的代码(我已经做过无数次了。)