Я довольно прагматик, но моя главная проблема здесь в том, что вы можете позволить этому 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
, но и непосредственно с ним. Это дополнительно ухудшает применимость / разворачиваемость / тестируемость.
Мой общий совет заключается в том, чтобы ошибаться при разработке интерфейсов, которые не зависят от этих типов монолитов, для предоставления их параметров, по крайней мере, для наиболее общих применимых классов, которые вы разрабатываете. И избегайте глобального подхода без внедрения зависимостей, если можете, если у вас нет действительно веской и уверенной причины не избегать этого.
switch
утверждению илиif
проверке утверждения по значению, считанному из файлов конфигурации.Да. Лучше централизовать константы и значения времени выполнения и код для их чтения.
Это плохо: большинству ваших конструкторов не понадобится большинство значений. Вместо этого создайте интерфейсы для всего, что нетривиально построить:
старый код (ваше предложение):
новый код:
создать экземпляр MyGreatClass:
Вот
current_config_block
экземпляр вашегоConfigBlock
класса (тот, который содержит все ваши значения), иMyGreatClass
класс получаетGreatClassData
экземпляр. Другими словами, передавайте конструкторам только те данные, которые им нужны, и добавляйте в них средстваConfigBlock
для создания этих данных.Этот код предполагает, что у вас будет глобальный экземпляр CodingBlock. Не делайте этого: обычно вы должны иметь экземпляр, объявленный глобально, в любой точке входа, которую ваше приложение использует (основная функция, DllMain и т. Д.), И передавать ее как аргумент везде, где вам нужно (но, как объяснено выше, вы не должны передавать весь класс вокруг, просто выставить интерфейсы вокруг данных и передать их).
Кроме того, не привязывайте ваши клиентские классы (ваши
MyGreatClass
) к типуCodingBlock
; Это означает, что если выMyGreatClass
берете строку и пять целых чисел, вам будет лучше передать эту строку и целые числа, чем вы будете передавать вCodingBlock
.источник
Короткий ответ:
Вам не нужны все настройки для каждого из модулей / классов в вашем коде. Если вы это сделаете, значит, что-то не так с вашим объектно-ориентированным дизайном. Особенно в случае модульного тестирования установка всех переменных, которые вам не нужны, и передача этого объекта не поможет с чтением или обслуживанием.
источник
ConfigBlock
классом. Смысл здесь в том, чтобы не предоставлять весь, в данном случае, контекст состояния системы, а только конкретные, необходимые значения для этого.