Я довольно прагматик, но моя главная проблема здесь в том, что вы можете позволить этому ConfigBlock
доминировать в дизайне вашего интерфейса, возможно, плохо. Когда у вас есть что-то вроде этого:
explicit MyGreatClass(const ConfigBlock& config);
... более подходящий интерфейс может быть таким:
MyGreatClass(int foo, float bar, const string& baz);
... вместо того, чтобы просто собирать эти foo/bar/baz
поля из массива ConfigBlock
.
Ленивый дизайн интерфейса
С другой стороны, этот вид дизайна позволяет легко создавать стабильный интерфейс для вашего конструктора, например, поскольку, если вам в конечном итоге понадобится что-то новое, вы можете просто загрузить это в ConfigBlock
(возможно, без каких-либо изменений кода), а затем cherry- выбрать любой новый материал, который вам нужен, без какого-либо изменения интерфейса, только изменение реализации MyGreatClass
.
Так что это как за, так и против, что это освобождает вас от разработки более тщательно продуманного интерфейса, который принимает только те входные данные, которые ему действительно необходимы. Он применяет мышление: «Просто дайте мне этот большой массив данных, я выберу из него то, что мне нужно», а не что-то вроде «Эти точные параметры - это то, что этот интерфейс должен работать».
Так что, безусловно, здесь есть некоторые плюсы, но они могут сильно перевесить минусы.
Связь
В этом сценарии все такие классы, создаваемые из ConfigBlock
экземпляра, в конечном итоге имеют зависимости:
Это может стать PITA, например, если вы хотите провести модульное тестирование Class2
в этой диаграмме изолированно. Возможно, вам придется поверхностно смоделировать различные ConfigBlock
входные данные, содержащие соответствующие поля Class2
, чтобы иметь возможность протестировать его в различных условиях.
В любом виде нового контекста (будь то модульное тестирование или целый новый проект) любые такие классы могут в конечном итоге стать более обременительным для (повторного) использования, поскольку в конечном итоге нам приходится всегда брать ConfigBlock
с собой в поездку и настраивать его. соответственно.
Повторное использование / готовность к развертыванию / Тестируемость
Вместо этого, если вы спроектируете эти интерфейсы надлежащим образом, мы можем отделить их ConfigBlock
и получить что-то вроде этого:
Если вы заметили на этой диаграмме выше, все классы становятся независимыми (их афферентные / исходящие связи уменьшаются на 1).
Это приводит к гораздо большему количеству независимых классов (по крайней мере, независимых ConfigBlock
), которые намного проще (повторно) использовать / тестировать в новых сценариях / проектах.
Теперь этот Client
код оказывается тем, который должен зависеть от всего и собирать все это вместе. Бремя в конечном итоге переносится в этот клиентский код для чтения соответствующих полей из ConfigBlock
и передачи их в соответствующие классы в качестве параметров. Тем не менее, такой клиентский код, как правило, узко разработан для конкретного контекста, и его потенциал для повторного использования, как правило, будет в любом случае нулевым или закрытым (это может быть main
функция точки входа вашего приложения или что-то в этом роде).
Таким образом, с точки зрения повторного использования и тестирования, это может помочь сделать эти классы более независимыми. С точки зрения интерфейса для тех, кто использует ваши классы, это также может помочь явно указать, какие параметры им нужны, а не только один массив, ConfigBlock
который моделирует весь набор полей данных, необходимых для всего.
Вывод
В общем, этот вид класс-ориентированного дизайна, который зависит от монолита, в котором есть все необходимое, как правило, имеет такие характеристики. Их применимость, возможность развертывания, возможность повторного использования, тестируемость и т. Д. Могут значительно ухудшиться. Тем не менее, они могут отчасти упростить дизайн интерфейса, если мы попытаемся сделать это положительно. Вы должны оценить эти плюсы и минусы и решить, стоят ли компромиссы того. Как правило, гораздо безопаснее ошибиться против такого дизайна, когда вы выбираете вишню из монолита в классах, которые обычно предназначены для моделирования более общего и широко применимого дизайна.
Последний, но тем не менее важный:
extern CodingBlock MyCodingBlock;
... это потенциально даже хуже (более искажено?) с точки зрения характеристик, описанных выше, чем подход с внедрением зависимостей, так как в итоге он связывает ваши классы не только с конкретным экземпляромConfigBlocks
, но и непосредственно с ним. Это дополнительно ухудшает применимость / разворачиваемость / тестируемость.
Мой общий совет заключается в том, чтобы ошибаться при разработке интерфейсов, которые не зависят от этих типов монолитов, для предоставления их параметров, по крайней мере, для наиболее общих применимых классов, которые вы разрабатываете. И избегайте глобального подхода без внедрения зависимостей, если можете, если у вас нет действительно веской и уверенной причины не избегать этого.