Хорошо, Таким образом, у меня есть следующая ситуация. У меня первоначально был некоторый код как это:
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, делает то, к чему я ожидаю это.
Спасибо
Я бы сказал, что параметр boardSize
не нужен во втором случае, но я бы добавил утверждение, чтобы гарантировать, что 3 размера платы действительно равны.
Но в целом второй случай мне кажется сомнительным. Я бы предложил первый подход, если вам действительно не нужно вставлять разные типы плат в основную плату. Даже в этом случае я бы подумал об использовании, например, фабрика платы вместо передачи 3 параметров платы конструктору, например
public interface IBoardBuilderFactory {
public IBoardType1 createBoardType1(Size boardSize);
public IBoardType2 createBoardType2(Size boardSize);
public IBoardType3 createBoardType3(Size boardSize);
}
Это обеспечит единообразие трех досок как в отношении «семейства досок», так и в отношении размера.
Нам следует больше узнать о модели контекста / предметной области, в частности, о взаимоотношениях основной и дочерней плат, чтобы решить, является ли здесь DI правильным выбором.
Сомнительно, следует ли вообще применять в этом случае внедрение зависимостей (или инверсию зависимостей). Мне кажется, что ваша MainBoard
отвечает за управление жизненным циклом BoardTypes
, созданных в первом примере. Если вы сейчас вводите свои BoardTypes, эту ответственность должны выполнять потребители из MainBoard
.
Это компромисс между гибкостью и дополнительными пошлинами со стороны потребителя.
С другой стороны, если есть смысл, чтобы жизненный цикл BoardType
обрабатывался извне , то можно использовать инверсию зависимостей. Затем ваш конструктор на MainBoard
должен убедиться, что его зависимости правильно определены . Это будет включать проверку равенства их размера
.
Основным преимуществом внедрения зависимостей является изоляция от изменений вводимых объектов. Итак, в вашем случае одна очевидная переменная - это размер.Вы бы вставили платы в основную плату, чтобы основной плате больше не нужно было знать или беспокоиться о размере. Кроме того, если вашему приложению не требуется поддерживать 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 и всех ее зависимостей.
-EDIT- Удалил большую часть моего ответа, потому что другие опередили меня :)
Другой вариант - фабрики. В зависимости от ваших требований вам может быть лучше (или нет) использовать фабрики для решения вашей проблемы. Есть хорошее обсуждение SO здесь о Factories Vs DI. Вы могли бы даже подумать о передаче factroy через DI в конструктор вашего класса - так что ваш конструктор принимает размер и фабричный класс и подчиняется фабричному классу (передавая размер), чтобы получить платы.
У вас может быть BoardTypeFactory, который создает BoardTypes следующим образом:
IBoardType bt1 = BoardTypeFactory.Create (boardSize);
Обратите внимание, что существует множество сообщений в блогах и SO-ответов о том, как лучше всего написать фабрику, приведенный выше простой пример.
Затем вы можете вызвать
new MainBoard (boardSize, bt1, ....
, передав исходный размер, который использовался для создания плат.
Какую информацию содержит класс BoardTypeX
? Имеет ли смысл вставлять этот объект в вашу MainBoard.
Внедрение зависимостей и шаблонов в целом не всегда решение, и вы не должны использовать его просто так, как вы можете. Что-то вроде заводского паттерна здесь может сработать лучше
public class MainBoard {
private IBoardType1 bt1;
private IBoardType2 bt2;
private IBoardType3 bt3;
...
private Size boardSize;
public MainBoard(IBoardBuilderFactory factory) {
this.bt1 = factory.buildBoard(boardSize);
//...
}
}
И если размер платы должен определяться извне, возможно, вы его вообще не храните. Возможно, фабрика определяет, какой размер платы использовать, когда она собирается в другом месте.
В любом случае, суть в том, что шаблоны проектирования нужны, чтобы помочь вам выполнять задачи чистым и поддерживаемым способом. Это не жесткие правила, которым необходимо следовать
Вы можете получить размер доски от одной из плат, которые проходят через DI. Таким образом можно полностью потерять переменную boardSize.
В рамках MainBoard
, boardSize
фактически является константой. Вы хотите, чтобы существовало только одно его значение. Вам понадобится такой код:
int boardSize = 24; // Or maybe you get this from reading a file or command line
MainBoardScope mainBoardScope = new mainBoardScope( boardSize );
Если вы наивно сделали 24 глобальной или константой, у вас были бы трудно увидеть зависимости здесь, потому что классы будут полагаться на получение этой константы или глобального статически, а не через объявленный интерфейс ( т.е. конструктор).
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 - обеспечить ее правильную конструкцию. Это задача фабрики - создать ее, это юнит-тест фабрики, чтобы убедиться, что все сделано правильно.