Фабричный шаблон — статический CreateInstance или нет?

#design-patterns #factory #factory-pattern #factory-method #static-factory

#шаблоны проектирования #фабрика #фабричный шаблон #фабричный метод #статический-завод

Вопрос:

Речь идет о фабричном шаблоне. Я немного смущен.

Я видел реализации, в которых createInstance() метод является статическим, и некоторые реализации, которые не являются статическими.

Некоторые говорят, что это зависит от «стиля» или «вкуса», а некоторые говорят, что это не так. Википедия говорит, что он должен быть нестатическим, и http://www.dofactory.com/Patterns/PatternFactory.aspx также говорится, что он должен быть нестатичным, согласно Gang of Four.

Мой вопрос: зависит ли это от стиля и вкуса или нарушает фабричный шаблон, если он реализован статическим способом? Что правильно?

Ответ №1:

Я очень не решаюсь классифицировать «экземпляр против статического» как дело вкуса. Это подразумевает, что это эстетично, как любимый цвет или, что более уместно, camelCase против PascalCase.

Сравнение экземпляра со статическим — это скорее вопрос компромиссов. Используя элементы экземпляра любого типа, вы получаете все преимущества полиморфизма, поскольку вы можете реализовывать интерфейсы и наследовать от других классов, когда у вас есть экземпляры и члены экземпляра. Со статикой вы не получаете этих преимуществ. Как правило, статический по сравнению с экземпляром — это компромисс между начальной простотой и последующей простотой. Статика проста, потому что она доступна во всем мире, и вам не нужно думать о таких вещах, как «когда это должно быть создано и кем?» Вам не нужно передавать их с помощью средств доступа / мутаторов или конструкторов, и ваш API выглядит чище. Это упрощает предварительные рассуждения. Но это усложняет обслуживание и будущие реализации.

Если у вас есть статический метод — скажем, фабричный метод в вашем случае — и вы позже хотите, чтобы он вел себя по-другому в определенных ситуациях, вы как бы поливаете из шланга. Вы должны создать второй метод и скопировать и вставить функциональность за вычетом всего, что вы хотите изменить, а затем попросить клиентов разобраться с этим. Или, что еще хуже, вы предоставляете глобальную переменную, и клиенты устанавливают ее до и после использования вашего метода, при этом глобальный указывает методу, как себя вести.

Если бы вы прошли путь экземпляра заранее, это было бы легко. Вы просто унаследуете и переопределите свой первоначальный фабричный метод и предоставите производные классы, где вам нужна новая функциональность. Вы не создаете дополнительной нагрузки на клиентский код и почти не вносите изменений в существующие классы (принцип open / closed).

Мой совет состоял бы в том, чтобы в будущем оказать вам и / или другим сопровождающим услугу и использовать реализацию экземпляра. Это не вопрос того, что хочет или предпочитает Gang of Four или кто-либо еще — это вопрос вашего собственного здравомыслия перед лицом гнили кода.

Ответ №2:

Статический метод не нарушает шаблон, но он противоречит многим другим объектно-ориентированным практикам (инверсия управления внедрение зависимостей в качестве одного примера), поэтому лучше использовать экземпляры.

Редактировать:

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

У вас может быть статический CreateInstance метод для создания экземпляра типа — в этом нет ничего плохого — люди часто называют это фабричным методом, но это не то, что называется шаблоном фабричного метода. Как только вы начнете вводить логику в этот метод для создания экземпляров разных типов в зависимости от некоторых условий, вам может понадобиться шаблон фабричного метода, описанный GoF.

Суть шаблона фабричного метода GoF заключается в замене условной логики внутри CreateInstance наследованием и полиморфизмом, и поэтому он не может быть статичным. Фабричный метод — это метод экземпляра, более того, он виртуальный. Ваш базовый тип обычно имеет абстрактную CreateInstance и условную логику, заменяемую деревом наследования, где каждый подтип переопределяет CreateInstance и создает только определенный продукт для этого подтипа.

Комментарии:

1. Не могли бы вы использовать статическую инъекцию, например static void setFactory(Factory fact) ?

2. Хорошо, спасибо. Мне не нужно ничего менять. Ваш аргумент соответствует моему представлению об ооп. Спасибо, что подтвердили это.

3. @corsiKlauseHoHoHo — Это означает, что вы должны помнить об использовании setFactory() каждый раз, когда вам нужно использовать factory. В отличие от класса экземпляра, вам необходимо использовать конструктор, что избавляет вас от необходимости запоминать требуемые методы.

4. @Yorro нет, вам нужно будет установить завод только один раз при запуске системы или по требованию из-за изменения конфигурации по запросу пользователя. Вы все равно можете вводить его динамически на основе параметров конфигурации. Если бы я каждый раз вызывал setFactory(), я бы предположил, что вместо фабрик я бы использовал конструкторы, а запуск их через статический метод включал бы обман, такой как threadlocal внутри… В этом случае я бы предпочел иметь фабрику разработчиков…

Ответ №3:

Если abstract factory это, то уровень экземпляра нормальный. И функциональность уровня экземпляра, как правило, легче имитировать и модульно тестировать, чем static уровень