解耦一个 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;
        }
    }

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

通过为工厂定义合同,可以将解耦提升到另一个层次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 的方法怎么能不以某种方式耦合到 AB 不需要工厂。它一家工厂。

也没有明显的理由说明为什么需要抽象来表示 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 需要 AIA 的实例并且创建它们的逻辑稍微复杂一些,那么 those classes 可能取决于工厂,例如

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

B 将是该工厂的 实现

我们应该从依赖它们的 classes 的角度来定义抽象。他们将如何与这种依赖性互动?我是否需要模拟这种依赖性,如果需要,我将如何做?当我们将 classes 视为依赖(或 需要)依赖时,我们就是在根据实际需要编写代码。这可以防止我们陷入兔子洞,编写我们不需要的代码(我已经做过无数次了。)