Плюсы и минусы использования фабрики против обычного конструктора
(Использование Python 3.2, хотя я сомневаюсь, что это имеет значение.)
я имею class Data
, class Rules
и класс Result
, Я использую строчные буквы для обозначения экземпляра класса.
rules
объект содержит правила, которые, если применяются к data
объект, может создать result
объект.
Я решаю, куда поместить (довольно сложный и развивающийся) код, который на самом деле применяет правила к данным. Я вижу два варианта:
Поместите этот код в класс
Result
метод, скажемparse_rules
,Result
конструктор будет принимать в качестве аргументаrules
объект и передать его наself.parse_rules
,Поместите этот код в новый класс
ResultFactory
,ResultFactory
будет одноэлементный класс, который имеет метод, скажем,build_result
, который занимаетrules
в качестве аргумента и возвращает вновь построенныйresult
объект.
Каковы плюсы и минусы двух подходов?
5 ответов
Принципы проектирования GRASP содержат рекомендации по распределению ответственности между классами и объектами в объектно-ориентированном проектировании. Например, шаблон Creator предлагает: В общем, класс B должен отвечать за создание экземпляров класса A, если применяется одно или, предпочтительно, несколько из следующего:
- Экземпляры B содержат или составно агрегируют экземпляры A
- Экземпляры B записывают экземпляры A
- Экземпляры B тесно используют экземпляры A
- Экземпляры B имеют начальную информацию для экземпляров A и передают ее при создании.
В вашем примере у вас есть сложный и развивающийся код для применения правил к данным. Это предполагает использование фабричного шаблона.
Помещение кода в Results противопоказано, потому что 1) результаты не создают результатов, и 2) результаты не являются экспертом по информации (то есть они не обладают большей частью необходимых знаний).
Короче говоря, ResultFactory кажется разумным местом для концентрации знаний о том, как применять правила к данным для получения результатов. Если бы вы попытались внедрить всю эту логику в конструкторы классов для Результатов или Правил, это привело бы к тесной связи и потере сцепления.
Третий сценарий:
Вы можете рассмотреть третий сценарий:
- Поместите код в метод
Rules.__call__
,
ИнстанцированиеResult
лайк:result = rules(data)
Плюсы:
Result
могут совершенно не знать оRules
что генерирует их (и, возможно, даже оригиналData
).- каждый
Rules
подкласс может настроить егоResult
создание. - Это естественно (для меня)
Rules
применительно кData
УступатьResult
, - И у вас будет пара принципов GRASP на вашей стороне:
- Создатель: экземпляры
Rules
иметь инициализирующую информацию для экземпляровResult
и передать его на создание. - Информационный эксперт: Информационный эксперт приведет к возложению ответственности на класс с наибольшим количеством информации, необходимой для его выполнения.
- Создатель: экземпляры
Побочные эффекты:
- Муфта: Вы поднимите муфту между
Rules
а такжеData
:- Вам нужно передать весь набор данных каждому
Rules
- Это означает, что каждый
Rules
должен быть в состоянии решить, к каким данным он будет применяться.
- Вам нужно передать весь набор данных каждому
Можете ли вы сделать ResultFactory чистой функцией? Бесполезно создавать одноэлементный объект, если все, что вам нужно, это функция.
Почему бы не поставить правила в своих классах? Если вы создаете класс RuleBase, то каждое правило может быть его производным. Таким образом, полиморфизм может быть использован, когда Data требует применения правил. Данные не должны знать или заботиться о том, какие экземпляры Правил были применены (если только Данные сами не знают, какие правила следует применять).
Когда необходимо вызвать правила, экземпляр данных может все RuleBase.ExecuteRules() и передать себя в качестве аргумента. Правильный подкласс Rule может быть выбран непосредственно из Data, если Data знает, какое правило необходимо. Или можно использовать другой шаблон проектирования, например, Chain of Responsibility, где Data вызывает шаблон и позволяет возвращать результат.
Это сделало бы отличное обсуждение доски.
Ну, второе совершенно глупо, особенно со всей единственностью. Если Result
требует Rules
чтобы создать экземпляр, и вы не можете создать его без него, он должен принять это в качестве аргумента __init__
, Нет необходимости делать покупки.