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

时间:2010-08-05 12:49:49

标签: c# java oop dependency-injection

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

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);
    }
}

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

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;
    }
}

我的问题是如何处理Board Size?我的意思是,在第一种情况下,我只是通过了所需的电路板尺寸,它将尽一切努力创建具有正确尺寸的其他类型的电路板。在依赖注入案例中,情况可能并非如此。你们在这种情况下做了什么?你是否对MainBoard的构造函数进行了任何检查,以确保传入正确的大小?你是否只是假设班级的客户有足够的责任通过相同大小的3种电路板,那么没有问题吗?

修改

为什么我这样做?因为我需要单元测试MainBoard。我需要能够在某些状态下设置3个子板,这样我就可以测试我的MainBoard正在按照我的预期进行测试。

由于

8 个答案:

答案 0 :(得分:5)

我想说在第二种情况下boardSize参数是不必要的,但我会添加一个断言来确保3个电路板尺寸真的相等。

但总的来说,第二种情况对我来说似乎很可疑。我会建议第一种方法,除非你真的需要在主板上注入不同类型的板。即便如此,我会考虑使用例如一个板factory而不是将3个板参数传递给构造函数,例如

public interface IBoardBuilderFactory {
    public IBoardType1 createBoardType1(Size boardSize);
    public IBoardType2 createBoardType2(Size boardSize);
    public IBoardType3 createBoardType3(Size boardSize);
}

这将确保3个董事会在“董事会家庭”和规模方面的一致性。

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

答案 1 :(得分:3)

在这种情况下是否应该应用依赖注入(或依赖性反转)是值得怀疑的。在我看来,您的MainBoard 负责管理在第一个示例中创建的BoardTypes生命周期。如果您现在注射了BoardTypes,则此责任必须由MainBoard消费者处理。

这是消费者方面的灵活性和附加职责之间的交易。

另一方面,如果将BoardType的生命周期处理为外部是明智的,则可以使用依赖性反转。然后,MainBoard上的构造函数需要确保它的依赖项 正确满足 。这包括检查他们的Size是否相等。

答案 2 :(得分:2)

依赖性注射的主要优点是可以防止被注射物体的变化。所以在你的情况下,一个变量明显是大小。您可以将电路板注入主板,这样主板就不再需要了解或担心尺寸。此外,除非您的应用程序需要在不同的板类型之间维护3个不同的行为,否则我建议对板类型接口使用单个抽象定义。

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;
    }
}

进行注射(注射框架或组装代码)的东西成为确保这些板具有适当尺寸的责任。这可以通过多种方式完成,一个例子是主板,注入板都是从单个外部源获得它们的大小。在这种情况下,也许您的应用程序会根据主板对注入的板进行调整。

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

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));
   }
}

基本上你所追求的是构建MainBoard的构造逻辑的反转。从那里开始,将所有东西都提取到工厂或一些邪恶的单件工厂或静态方法中。问问自己,“MainBoard创建在哪里?”还要问,“需要哪些组件和参数?”将所有实例化逻辑移动到工厂后,维护Mainboard及其所有依赖项可能会变得更加简单。

答案 3 :(得分:1)

BoardTypeX类包含哪些类型的信息?将此对象注入到MainBoard.依赖注入中是否有意义,并且模式一般来说,总是解决方案,并且您不应该只使用b / c能够。各种工厂模式在这里可能会更好用

public class MainBoard {
    private IBoardType1 bt1;
    private IBoardType2 bt2;
    private IBoardType3 bt3;
    ...
    private Size boardSize;

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

如果电路板尺寸应在外部确定,也许您根本不存储它。也许工厂确定在其他地方建造时要使用的板尺寸。

无论如何,关键是,设计模式可以帮助您以干净和可维护的方式完成任务。它们不是必须遵守的硬性规则

答案 4 :(得分:1)

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

另一种选择 - 工厂。根据您的要求,您可能会发现使用工厂解决问题更好(或不是)。关于Factories Vs DI有一个很好的SO讨论here。你甚至可以考虑通过DI将一个factroy传递给你的类构造函数 - 所以你的构造函数需要一个大小和一个工厂类,并按照工厂类(传入大小)来获取板。

答案 5 :(得分:0)

您可以从通过DI传递的其中一个板卡获得板卡尺寸。这样你就可以完全松开boardSize变量了。

答案 6 :(得分:0)

您可以使用BoardTypeFactory创建BoardTypes,如下所示:

IBoardType bt1 = BoardTypeFactory.Create(boardSize);

请注意,关于如何最好地编写工厂,有大量的博客帖子和SO答案,上面是一个简单的例子。

然后你可以打电话

new MainBoard(boardSize, bt1, ....

传递用于创建电路板的源尺寸。

答案 7 :(得分:0)

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

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

如果天真地将24作为全局变量或常量变量,那么这里将会有难以看到的依赖项,因为类将依赖于静态地获取该常量或全局,而不是通过声明的接口(即构造函数)。 / p>

mainBoardScope为一组具有相同生命周期的对象保留“单身人士”。与旧式单身人士不同,这些不是全球性的,不会静态访问。然后,考虑在启动应用程序时运行的代码(或在更大的应用程序中使用此范围)来构建对象图:

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

createMainBoard方法中,您可以使用范围中的boardSize来创建三个子板:

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

您是否需要MainBoard来检查传递给构造函数的三块板的大小是否正确?如果是创建板的代码,则为injectMainBoard()创建单元测试。 MainBoard的工作不是确保正确构建。创建它是工厂的工作,它是工厂工作的单元测试,检查它是否正确。