关于c#:如何转换此代码,以便它现在使用依赖注入模式?

How to convert this code so it now uses the Dependency Injection pattern?

好的,我有以下情况。我最初有一些这样的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MainBoard {
    private BoardType1 bt1;
    private BoardType2 bt2;
    private BoardType3 bt3;
    ...
    private readonly Size boardSize;

    public MainBoard(Size boardSize) {
        this.boardSize = boardSize;

        bt1 = new BoardType1(boardSize);
        bt2 = new BoardType2(boardSize);
        bt3 = new BoardType3(boardSize);
    }
}

现在,我决定重构代码,以便注入类的依赖项,而不是:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class MainBoard {
    private IBoardType1 bt1;
    private IBoardType2 bt2;
    private IBoardType3 bt3;
    ...
    private Size boardSize;

    public MainBoard(Size boardSize, IBoardType1 bt1, IBoardType2 bt2, IBoardType3 bt3) {
        this.bt1 = bt1;
        this.bt2 = bt2;
        this.bt3 = bt3;
    }
}

我的问题是如何处理电路板尺寸?我的意思是,在第一种情况下,我只是通过了班级所需的董事会规模,它将尽一切努力创造其他类型的董事会与正确的规模。在依赖注入的情况下,情况可能不会更糟。你们在这种情况下做什么?您是否对MainBoard的构造函数进行了某种检查,以确保传入了正确的大小?你只是假设班级的客户有足够的责任通过三种同样大小的板子,所以没有问题吗?

编辑

我为什么要这样做?因为我需要单元测试主板。我需要能够在特定的状态下设置3个子板,这样我就可以测试我的主板在做我期望的事情。

谢谢


在第二种情况下,我会说boardSize参数是不必要的,但我会添加一个断言,以确保三个板的大小确实相等。

但总的来说,第二个案例对我来说是可疑的。我建议采用第一种方法,除非您确实需要向主板注入不同类型的主板。即便如此,我还是会考虑使用一个电路板工厂,而不是将3个电路板参数传递给构造函数,例如。

1
2
3
4
5
public interface IBoardBuilderFactory {
    public IBoardType1 createBoardType1(Size boardSize);
    public IBoardType2 createBoardType2(Size boardSize);
    public IBoardType3 createBoardType3(Size boardSize);
}

这将确保3块电路板在"电路板系列"和尺寸方面的一致性。

我们应该更多地了解上下文/域模型,特别是主板和子主板的关系,以便决定DI是否是这里的正确选择。


在这种情况下,是否应该应用依赖注入(或依赖倒置)是值得怀疑的。在我看来,您的MainBoard负责管理它在第一个样本中创建的BoardTypes的生命周期。如果现在确实注入了董事会类型,则此职责必须由MainBoard的消费者处理。

这是在消费者方面的灵活性和附加义务之间的权衡。

另一方面,如果从外部处理BoardType的生命周期是明智的,那么可以使用依赖倒置。然后,EDOCX1上的构造函数(0)需要确保它的依赖关系已正确地建立起来。这包括检查它们的Size是否相等。


依赖性注入的主要优点是隔离被注入对象的变化。所以在你的例子中,一个明显的变量就是大小。你将把这些板注入主板,这样主板就不再需要知道或担心尺寸了。另外,除非应用程序需要在不同的板类型之间维护3种不同的行为,否则我建议对板类型接口使用一个抽象定义。

1
2
3
4
5
6
7
8
9
10
11
public class MainBoard {
    private IBoardType bt1;
    private IBoardType bt2;
    private IBoardType bt3;

    public MainBoard(IBoardType bt1, IBoardType bt2, IBoardType bt3) {
        this.bt1 = bt1;
        this.bt2 = bt2;
        this.bt3 = bt3;
    }
}

它成为执行注入(注入框架或组装代码)的事情的责任,以确保这些板被赋予适当的大小。这可以通过多种方式实现,一个例子是主板和注入板都是从一个外部源获得它们的大小。也许你的应用程序,在这种情况下,尺寸注入板相对于主板。

所以你可以有外部逻辑,比如:

1
2
3
4
5
6
public class BoardAssembler {
   public static MainBoard assembleBoard(Size size) {
      Size innerBoardSize = deriveSizeOfInternalBoards(size);
      return new MainBoard(new BoardType1(innerBoardSize), new BoardType2(innerBoardSize), new BoardType3(innerBoardSize));
   }
}

本质上,您所追求的是在构建主板的任何地方对构建逻辑进行反转。从那里开始,把所有的东西都提取到一个工厂或者某个邪恶的单体工厂或者静态方法中。问问自己,"主板是在哪里创建的?"还要问,"需要什么组件和参数?"一旦将所有实例化逻辑移入工厂,维护主板及其所有依赖项可能会变得更简单。


-编辑-删除了我的大部分回复,因为其他人击败了我:)

另一个选择是工厂。根据您的需求,您可能会发现使用工厂来解决您的问题更好(或更好)。这里有一个关于工厂和DI的很好的讨论。您甚至可以考虑通过DI将factroy传递到类构造函数中——因此,您的构造函数采用大小和工厂类,并遵从工厂类(传递大小)来获取板。


BoardTypeX类包含什么类型的信息?把这个对象注入到你的MainBoard.依赖注入中是否有意义?一般来说,模式并不总是解决方案,你不应该只使用B/C就可以了。各种各样的工厂模式在这里可能更有效。

1
2
3
4
5
6
7
8
9
10
11
12
public class MainBoard {
    private IBoardType1 bt1;
    private IBoardType2 bt2;
    private IBoardType3 bt3;
    ...
    private Size boardSize;

    public MainBoard(IBoardBuilderFactory factory) {
        this.bt1 = factory.buildBoard(boardSize);
        //...
    }
}

如果板的尺寸是由外部决定的,也许你根本不储存它。也许工厂决定在别处建造时使用什么尺寸的电路板。

总之,关键是,设计模式可以帮助您以一种干净和可维护的方式完成任务。他们不是必须遵守的硬性规定。


MainBoard的范围内,boardSize实际上是一个常数。您只希望它的单个值存在。您需要这样的代码:

1
2
int boardSize = 24;  // Or maybe you get this from reading a file or command line
MainBoardScope mainBoardScope = new mainBoardScope( boardSize );

如果您天真地将24设置为全局或常量,那么在这里很难看到依赖项,因为类将依赖于静态地获取该常量或全局,而不是通过声明的接口(即构造函数)。

mainBoardScope为一组具有相同寿命的对象持有"单子"。与老式的单例程序不同,这些程序不是全局的,不能静态访问。然后,考虑在启动应用程序(或在更大的应用程序中启动此范围)以构建对象图时运行的代码:

1
2
   MainBoardFactory factory = new MainBoardFactory( mainBoardScope );
   MainBoard board = factory.createMainBoard();

在该createMainBoard方法中,您将使用作用域中的boardSize创建三个子板:

1
2
3
4
   IBoardType1 b1 = injectBoardType1( myScope );
   IBoardType2 b2 = injectBoardType2( myScope );
   IBoardType3 b3 = injectBoardType3( myScope );
   return new MainBoard( scope.getBoardSize, b1, b2, b3 );

您是否需要主板来检查传递给建设者的三个主板的大小是否正确?如果是您的代码创建板,那么为injectMainBoard()创建一个单元测试。确保主板的正确构造不是主板的工作。创建它是工厂的工作,检查它是否正确是工厂工作的单元测试。


您可以有一个boardtypefactory来创建这样的boardtypes:

江户十一〔九〕号

请注意,有大量的博客文章,所以回答如何最好地写一个工厂,上面是一个简单的例子。

然后你可以打电话

埃多克斯1〔10〕

传递用于创建板的源大小。


您可以从一个经过DI的板中获得板大小。这样就可以将boardsize变量放在一起。