Избегайте конструкторов со многими аргументами


10

Так что у меня есть фабрика, которая создает объекты разных классов. Все возможные классы происходят от абстрактного предка. Фабрика имеет файл конфигурации (синтаксис JSON) и решает, какой класс создать, в зависимости от конфигурации пользователя.

Чтобы добиться этого, фабрика использует boost :: property_tree для JSON-анализа. Он идет по дереву и решает, какой конкретный объект создать.

Однако у объектов-продуктов есть много полей (атрибутов). В зависимости от конкретного класса, объект имеет около 5-10 атрибутов, в будущем может быть даже больше.

Поэтому я не уверен, как должен выглядеть конструктор объектов. Я могу придумать два решения:

1) Конструктор продукта ожидает каждый атрибут в качестве параметра, поэтому у конструктора будет более 10 параметров. Это будет ужасно и приведет к длинным нечитаемым строкам кода. Однако преимущество заключается в том, что фабрика может анализировать JSON и вызывать конструктор с правильными параметрами. Класс продукта не должен знать, что он был создан из-за конфигурации JSON. Нет необходимости знать, что JSON или конфигурация задействованы вообще.

2) Конструктор продукта ожидает только один аргумент, объект property_tree. Затем он может разобрать необходимую информацию. Если информация в конфигурации отсутствует или выходит за границы, каждый класс продукта может реагировать должным образом. Фабрике не нужно знать, какие аргументы нужны нескольким продуктам. Завод также не должен знать, как реагировать в случае неправильной конфигурации. И интерфейс конструктора унифицирован и мал. Но, как недостаток, продукт должен извлекать необходимую информацию из JSON, поэтому он знает, как он построен.

Я склонен предпочесть решение 2). Однако я не уверен, что это хорошая фабричная модель. Как-то неправильно, сообщая продукту, что он создан с конфигурацией JSON. С другой стороны, новые продукты могут быть введены очень просто.

Есть мнения по этому поводу?



1
Я перешел по вашей ссылке. В ответе с самым высоким рейтингом от фаната-трещотки есть пример. Но какую проблему решает этот «строительный» подход? Там есть строка кода DataClass data = builder.createResult () ;. Но метод createResults () по-прежнему должен получить 10 параметров в объекте DataClass. Но как? Кажется, у вас просто есть еще один уровень абстракции, но конструктор DataClass не становится меньше.
lugge86

Посмотрите на конструктор и прототип.
Сильвиу Бурча

Сильвиу Бурча, я сделал. Тем не менее, при использовании компоновщика, как компоновщик получает параметры в продукт? Где-то там должен быть толстый интерфейс. Конструктор - это еще один слой, но каким-то образом параметры должны найти свой путь в класс продукта.
lugge86

1
Если ваш класс слишком велик, смещение аргументов конструктора не сделает его слишком большим .
Теластин

Ответы:


10

Я бы не стал использовать вариант 2, потому что тогда вы навсегда свернули конструкцию вашего объекта с анализом дерева свойств boost. Если вас устраивает класс, которому нужно столько параметров, вам должен быть удобен конструктор, которому нужно столько параметров, такова жизнь!

Если ваша основная задача - читабельность кода, вы можете использовать шаблон компоновщика, в основном это пробел c ++ / java из-за отсутствия именованных аргументов. Вы получите вещи, которые выглядят так:

MyObject o = MyObject::Builder()
               .setParam1(val1)
               .setParam2(val2)
               .setParam3(val3)
             .build();

Так что теперь MyObject будет иметь приватный конструктор, который вызывается в Builder :: build. Приятно то, что это будет единственное место, где вам когда-либо придется вызывать конструктор с 10 параметрами. Фабрика дерева свойств boost будет использовать построитель, и впоследствии, если вы захотите создать MyObject напрямую или из другого источника, вы должны пройти через построитель. Кроме того, компоновщик позволяет четко указывать каждый параметр при его передаче, чтобы он был более читабельным. Это, очевидно, добавляет некоторый шаблон, так что вам придется решить, стоит ли это того по сравнению с простым вызовом грязного конструктора или объединением некоторых ваших существующих параметров в структуры и т. Д. Просто добавив еще один параметр в таблицу.

https://en.wikipedia.org/wiki/Builder_pattern#C.2B.2B_Example


5

НЕ используйте второй подход.

Это определенно не решение и приведет только к созданию экземпляров классов в вашей бизнес-логике, а не к той части вашего приложения, где находятся фабрики.

Или:

  • попытаться сгруппировать определенные параметры, которые, кажется, представляют подобные вещи в объекты
  • разделить текущий класс на несколько меньших классов (класс обслуживания с 10 параметрами кажется, что класс делает слишком много вещей)
  • оставьте все как есть, если ваш класс на самом деле не сервис, а объект значения

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


Итак, логика bsiness использует фабрику, которая возвращает продукты, поэтому бизнес-логика не видит JSON / ptree. Но я понимаю вашу точку зрения, что иметь код Parser в construcotr кажется неправильным.
lugge86

Класс представляет виджет в графическом интерфейсе для встроенной системы, таким образом, мне кажется, что 5+ атрибутов в порядке: x_coord, y_coord, backgroundcolor, framesize, framecolor, text ...
lugge86

1
@ lugge86 Даже если вы используете фабрику для анализа JSON и, таким образом, избегаете вызова newили конструирования объектов внутри вашей бизнес-логики, это не очень хороший дизайн. Чек « Не ищите вещей» говорит Мишко Хевери , который более подробно объясняет, почему заводской подход, о котором вы намекнули, плох с точки зрения как тестирования, так и чтения. Кроме того, ваш класс выглядит как объект данных, и для него обычно лучше иметь больше параметров, чем обычный класс обслуживания. Я не был бы слишком обеспокоен.
Энди

Я чувствую себя хорошо с моим заводским подходом, но я пойду по вашей ссылке и подумаю об этом. Тем не менее, завод не рассматривается в этой теме. Вопрос все еще в том, как настроить продукты ...
lugge86

«Наличие класса обслуживания с 10 параметрами кажется, что класс делает слишком много вещей» Не в машинном обучении. Любой алгоритм ML будет иметь множество настраиваемых параметров. Интересно, как правильно бороться с этим при кодировании ML?
Сиюань Рен

0

Вариант 2 почти правильный.

Улучшенный вариант 2

Создайте «фронтальный» класс, задачей которого является взять этот объект JSON-структуры, выбрать биты и вызвать конструктор (ы) фабрики. Он берет то, что делает фабрика, и дает это клиенту.

  • Фабрика абсолютно не подозревает, что такая штука JSON вообще существует.
  • Клиент не должен знать, какие именно биты нужны фабрике.

По сути, «передний конец» говорит двум Бобам: «Я имею дело с отредактированными клиентами, поэтому инженеры не должны! У меня есть навыки работы с людьми!» Бедный Том. Если бы он только сказал: «Я отделил клиента от строительства. Этот результат - очень сплоченная фабрика»; он мог бы сохранить свою работу.

Слишком много доводов?

Не для клиента - общение переднего плана.

Передняя часть - заводская? Если не 10 параметров, то лучшее, что вы можете сделать, это отложить распаковку, если не оригинальную JSON, то немного DTO. Это лучше, чем передавать JSON на завод? Та же разница, что я говорю.

Я бы настоятельно рекомендовал передать отдельные параметры. Придерживайтесь цели чистой, сплоченной фабрики. Избегайте проблем ответа @DavidPacker.

Смягчение "слишком много аргументов"

  • Конструкторы класса или фабрики

    • принимать только аргументы для конкретной конструкции класса / объекта.
    • параметры по умолчанию
    • необязательные параметры
    • именованные аргументы
  • Группировка аргументов переднего конца

    • Проверяет, оценивает, проверяет, устанавливает и т. Д. Значения аргументов, руководствуясь указанными выше сигнатурами конструктора.

«Фабрика не имеет ни малейшего представления, что такая штука JSON вообще существует» - ну и зачем тогда фабрика? Он скрывает детали создания продукта как от продукта, так и от потребителя. Почему еще один класс должен помочь? Я в порядке с фабрикой, говорящей JSON. Можно реализовать другую фабрику для разбора XML и реализовать «Абстрактную фабрику» в будущем ...
lugge86

Мистер Фабрика: «Какой объект вы хотите? ... Что это? Просто скажите мне, какой объект класса построить». Файл конфигурации JSON является источником данных, как говорит дядя Боб, «это детали реализации». Это может быть из другого источника и / или в другой форме. Как правило, мы хотим отделить от конкретных деталей источника данных. В случае изменения источника или формы, завод не будет. Учитывая исходный + анализатор и фабрику в виде отсоединенных модулей, можно использовать оба.
радаробоб
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.