Как преобразовать этот код, чтобы он теперь использовал шаблон внедрения зависимостей? - PullRequest
7 голосов
/ 05 августа 2010

Хорошо, у меня следующая ситуация. У меня изначально был такой код:

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

Мой вопрос: что делать с размером доски? Я имею в виду, что в первом случае я просто передал классу нужный размер платы, и он сделал бы все, чтобы создать другие типы плат с правильным размером. В случае внедрения зависимости это может быть не так. Что вы, ребята, делаете в этой ситуации? Вы ставите какие-либо проверки на конструктор MainBoard, чтобы убедиться, что передаются правильные размеры? Вы просто предполагаете, что клиент класса будет достаточно ответственным, чтобы пропустить 3 вида досок одинакового размера, чтобы не было проблем?

Редактировать

Почему я это делаю? Потому что мне нужно юнит-тест MainBoard. Мне нужно иметь возможность устанавливать 3 вспомогательные платы в определенных состояниях, чтобы я мог проверить, что моя MainBoard делает то, что я ожидаю.

Спасибо

Ответы [ 8 ]

5 голосов
/ 05 августа 2010

Я бы сказал, что параметр boardSize не нужен во втором случае, но я бы добавил утверждение, чтобы гарантировать, что 3 размера платы действительно равны.

Но в целом второй случай мне кажется сомнительным. Я бы предложил первый подход, если только вам действительно не нужно вставлять разные платы в основную плату. Несмотря на это, я хотел бы рассмотреть, например, использование. доска фабрика вместо передачи 3 параметров платы конструктору, например

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

Это обеспечит согласованность трех досок как по «семейству досок», так и по размеру.

Нам следует больше узнать о модели контекста / домена, в частности, об отношениях основной платы и дочерних плат, чтобы решить, является ли DI правильным выбором здесь.

3 голосов
/ 05 августа 2010

Сомнительно, следует ли вообще применять в этом случае инъекцию зависимости (или инверсию зависимости). Мне кажется, что ваш MainBoard является ответственным за управление жизненным циклом BoardTypes, созданного в первом примере. Если вы сейчас вводите свой BoardTypes, эту ответственность должны выполнять потребители из MainBoard.

Это обмен между гибкостью и дополнительными обязанностями на стороне потребителя.

С другой стороны, если целесообразно обрабатывать жизненный цикл BoardType s извне , можно использовать инверсию зависимости. Затем ваш конструктор на MainBoard должен убедиться, что его зависимости правильно зарегистрированы . Это будет включать проверку того, что их Size равно.

2 голосов
/ 05 августа 2010

Основным преимуществом внедрения зависимостей является изоляция от изменений объектов, которые вводятся.Так что в вашем случае единственной видимой переменной является размер.Вы вставили бы платы в основную плату, так что основной плате больше не нужно знать или беспокоиться о размере.Кроме того, если вашему приложению не нужно поддерживать 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 и все его зависимости.

1 голос
/ 05 августа 2010

-EDIT- Удалил большую часть моего ответа, потому что другие избили меня до него:)

Другой вариант - фабрики. В зависимости от ваших требований, вам может быть лучше (или нет) использовать фабрики для решения вашей проблемы. Здесь хорошая дискуссия SO здесь о фабриках против DI. Вы могли бы даже подумать о передаче factroy через DI в конструктор вашего класса - так что ваш конструктор принимает размер и фабричный класс и переносится к фабричному классу (передавая размер), чтобы получить доски.

1 голос
/ 05 августа 2010

Какой тип информации содержится в классе BoardTypeX?Имеет ли смысл вводить этот объект в MainBoard. Dependency-Injection, и шаблоны в целом не являются всегда решением, и вы не должны использовать его только потому, что можете.Здесь лучше подойдет заводская модель

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

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

И если размер платы должен быть определен внешне, возможно, вы его вообще не храните.Может быть, фабрика определяет, какой размер платы использовать, когда она строится где-то еще.

В любом случае, дело в том, что шаблоны проектирования существуют для того, чтобы помочь вам выполнять задачи чистым и обслуживаемым способом.Это не жесткие и быстрые правила, которым нужно следовать

0 голосов
/ 16 августа 2010

В пределах MainBoard boardSize фактически является константой. Вы хотите, чтобы существовало только одно значение. Вам нужен код вроде этого:

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

Если бы вы наивно сделали 24 глобальным или константным, у вас бы здесь были трудно различимые зависимости, потому что классы полагались бы на выбор этой константы или глобально статически, а не через объявленный интерфейс (т.е. конструктор). 1006 *

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, чтобы гарантировать, что это построено правильно. Это работа фабрики по ее созданию, это модульное тестирование работы фабрики, чтобы проверить, что все сделано правильно.

0 голосов
/ 05 августа 2010

У вас может быть BoardTypeFactory, который создает BoardTypes следующим образом:

IBoardType bt1 = BoardTypeFactory.Create(boardSize);

Обратите внимание, что существует множество постов в блогах и ТАК о том, как лучше написать фабрику, приведенный выше простой пример.

Вы можете позвонить

new MainBoard(boardSize, bt1, ....

передавая исходный размер, который использовался для создания плат.

0 голосов
/ 05 августа 2010

Вы можете получить размер доски от одной из досок, которые передаются через DI.Таким образом, вы можете полностью потерять переменную boardSize.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...