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

时间:2019-07-09 12:52:55

标签: c# dependency-injection solid-principles

我一直在练习如何使用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;
    }
}

3 个答案:

答案 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)

BA分离的一种方法是为A定义合同

interface IA { }

并使用A

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

然后从BA替换对IA的所有引用

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

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

在需要时(在AFavtorySomeMethod可以用B实例化

通过为工厂类别定义合同,可以将去耦提升到另一个层次

interface IAFactory {
 IA CreateIA(string month);
}

并与AFactory签订合同。

之后,可以使用以下命令将IAFactory的实例注入到B中: 构造函数

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