我一直在练习如何使用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;
}
}
答案 0 :(得分:4)
如果要解耦,则需要从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;
}
}
答案 1 :(得分:3)
TL; DR-没有明显的耦合需要修复。依赖抽象是很棒的,但是有可能迷失方向并在我们不需要它们的地方添加它们。从其他类的角度创建抽象,这些抽象依赖于它们当您知道需要它们时,以便您始终在编写所需的代码,而不是不需要的代码。
为您的班级创建A
的实例还不错。创建A
的实例是B
的明显目的。 B
不依赖于A
,因为它不以任何方式使用它。它只是创建A
并返回它,因为这是应该做的。
另一种查看方式-返回A
的方法如何不以某种方式与A
耦合? B
不需要工厂。 是工厂。
也没有明显的理由说明为什么需要抽象来表示A
。如果需要模拟A
,则可以定义一个类似IA
的接口。但是这里没有显示表明您需要它。创建A
的“真实”实例非常容易,因此无需模拟它。
var a = new A("February");
。什么玩笑?
如果您确实需要A
的抽象(如IA
),那么B
中唯一需要的唯一更改就是更改SomeMethod
以返回{{1 }},而不是List<IA>
。在该方法中,您将创建一个List<A>
而不是List<IA>
,但仍将使用List<A>
的具体实例填充它。
这将消除的耦合(如我所强调的,未在您的代码中显示)将与其他类(未显示)一起使用,这些类当前取决于A
,并且取决于{{1 }}提供。现在他们将依靠A
提供B
,因此其他这些类将不再与B
耦合。
抽象虽然很棒,但是它可能会变成一个兔子洞,在这里我们开始添加我们不需要的接口和工厂。 (将IA
替换为A
后会发生什么?现在您已经耦合到A
了,该怎么办?)如果您不能同时测试一个班级,测试其依赖项,这是一个很好的信号,表明依赖项必须是可以模拟的抽象。
如果您可以测试所有内容(在这种情况下可以进行测试),那么引入更多抽象只是创建您不需要的工作。这并不是说我永远不会从抽象开始。在许多情况下,很明显我需要一个。通常是在我要对依赖项做一些事情的情况下。在这种情况下,您只是将其退回。
这里是另一种查看方式:如果需要抽象,它可能是 represent IA
的抽象。如果此处未显示的其他类需要List<T>
或B
的实例,并且创建它们的逻辑稍微复杂一些,那么那些类可能取决于工厂,例如
A
IA
将是该工厂的实现。
我们应该从依赖于抽象的类的角度来定义抽象。他们将如何与这种依赖关系互动?我是否需要模拟这种依赖关系,如果是的话,我该怎么做?当我们将类视为依赖于(或需要)依赖项时,我们将根据实际需求编写代码。这样可以避免我们陷入编写不需要的代码的麻烦中(我已经无数次这样做了。)
答案 2 :(得分:1)
将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
,SomeMethod
可以用B实例化
通过为工厂类别定义合同,可以将去耦提升到另一个层次
interface IAFactory {
IA CreateIA(string month);
}
并与AFactory
签订合同。
之后,可以使用以下命令将IAFactory
的实例注入到B
中:
构造函数
public B(IAFactory aFactory){
_aFactory = aFactory;
...
}