Порождающие паттерны
Фабричный метод (Factory Method) — это паттерн, который определяет интерфейс для создания объектов некоторого класса, но непосредственное решение о том, объект какого класса создавать происходит в подклассах. То есть паттерн предполагает, что базовый класс делегирует создание объектов классам-наследникам.
Когда надо применять паттерн
- Когда заранее неизвестно, объекты каких типов необходимо создавать
- Когда система должна быть независимой от процесса создания новых объектов и расширяемой: в нее можно легко вводить новые классы, объекты которых система должна создавать.
- Когда создание новых объектов необходимо делегировать из базового класса классам наследникам
На языке UML паттерн можно описать следующим образом:
Формальное определение паттерна на языке C# может выглядеть следующим образом:
abstract class Product <> class ConcreteProductA : Product <> class ConcreteProductB : Product <> abstract class Creator < public abstract Product FactoryMethod(); >class ConcreteCreatorA : Creator < public override Product FactoryMethod() < return new ConcreteProductA(); >> class ConcreteCreatorB : Creator < public override Product FactoryMethod() < return new ConcreteProductB(); >>
Участники
- Абстрактный класс Product определяет интерфейс класса, объекты которого надо создавать.
- Конкретные классы ConcreteProductA и ConcreteProductB представляют реализацию класса Product. Таких классов может быть множество
- Абстрактный класс Creator определяет абстрактный фабричный метод FactoryMethod() , который возвращает объект Product.
- Конкретные классы ConcreteCreatorA и ConcreteCreatorB — наследники класса Creator, определяющие свою реализацию метода FactoryMethod() . Причем метод FactoryMethod() каждого отдельного класса-создателя возвращает определенный конкретный тип продукта. Для каждого конкретного класса продукта определяется свой конкретный класс создателя. Таким образом, класс Creator делегирует создание объекта Product своим наследникам. А классы ConcreteCreatorA и ConcreteCreatorB могут самостоятельно выбирать какой конкретный тип продукта им создавать.
Теперь рассмотрим на реальном примере. Допустим, мы создаем программу для сферы строительства. Возможно, вначале мы захотим построить многоэтажный панельный дом. И для этого выбирается соответствующий подрядчик, который возводит каменные дома. Затем нам захочется построить деревянный дом и для этого также надо будет выбрать нужного подрядчика:
class Program < static void Main(string[] args) < Developer dev = new PanelDeveloper("ООО КирпичСтрой"); House house2 = dev.Create(); dev = new WoodDeveloper("Частный застройщик"); House house = dev.Create(); Console.ReadLine(); >> // абстрактный класс строительной компании abstract class Developer < public string Name < get; set; >public Developer (string n) < Name = n; >// фабричный метод abstract public House Create(); > // строит панельные дома class PanelDeveloper : Developer < public PanelDeveloper(string n) : base(n) < >public override House Create() < return new PanelHouse(); >> // строит деревянные дома class WoodDeveloper : Developer < public WoodDeveloper(string n) : base(n) < >public override House Create() < return new WoodHouse(); >> abstract class House < >class PanelHouse : House < public PanelHouse() < Console.WriteLine("Панельный дом построен"); >> class WoodHouse : House < public WoodHouse() < Console.WriteLine("Деревянный дом построен"); >>
В качестве абстрактного класса Product здесь выступает класс House. Его две конкретные реализации — PanelHouse и WoodHouse представляют типы домов, которые будут строить подрядчики. В качестве абстрактного класса создателя выступает Developer, определяющий абстрактный метод Create() . Этот метод реализуется в классах-наследниках WoodDeveloper и PanelDeveloper. И если в будущем нам потребуется построить дома какого-то другого типа, например, кирпичные, то мы можем с легкостью создать новый класс кирпичных домов, унаследованный от House, и определить класс соответствующего подрядчика. Таким образом, система получится легко расширяемой. Правда, недостатки паттерна тоже очевидны — для каждого нового продукта необходимо создавать свой класс создателя.
Зачем нужен фабричный метод?
Какая выгода нам от создания объекта через Creator , если точно так же можно его создать через new ? Когда нужно использовать именно фабричный метод, и какое преимущество нам это может дать?
Отслеживать
задан 17 мая 2016 в 9:25
3,466 2 2 золотых знака 25 25 серебряных знаков 50 50 бронзовых знаков
2 ответа 2
Сортировка: Сброс на вариант по умолчанию
Выгод может быть много. Некоторые:
-
Вы можете дать имя вашему конструктору. Например:
Point PointCreator.FromPolar(double r, double phi)
Point PointCreator.FromPolar(double r, double phi) < . >Point PointCreator.FromCartesian(double x, double y)
или например
DateTime DateTime.FromSeconds(double seconds) < . >DateTime DateTime.FromMilliseconds(double ms)
WebRequest InitWebRequest(string uri)
async SerialDevice Create(PortInfo portInfo)
Отслеживать
ответ дан 17 мая 2016 в 9:49
206k 28 28 золотых знаков 292 292 серебряных знака 526 526 бронзовых знаков
Действительно, выгод много. Спасибо за ответ!
17 мая 2016 в 10:09
@Lightness: Пожалуйста!
17 мая 2016 в 10:55
Проблема в самом операторе new. Используя его, вы каждый раз создаёте экземпляр конкретного типа. А фабричный метод позволяет абстрагироваться от конкретных типов и передать ответственность за создание экземпляров реализации этого паттерна. На выходе вы получаете некий абстрактный тип Product, который обладает теми или иными интересующими вас свойствами, но конкретная реализация которого вам не важна.
Отслеживать
ответ дан 17 мая 2016 в 9:37
DreamChild DreamChild
36.1k 2 2 золотых знака 45 45 серебряных знаков 85 85 бронзовых знаков
- c#
- шаблоны-проектирования
-
Важное на Мете
Связанные
Похожие
Подписаться на ленту
Лента вопроса
Для подписки на ленту скопируйте и вставьте эту ссылку в вашу программу для чтения RSS.
Дизайн сайта / логотип © 2023 Stack Exchange Inc; пользовательские материалы лицензированы в соответствии с CC BY-SA . rev 2023.11.9.742
Нажимая «Принять все файлы cookie» вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.
Фабричный метод
Фабричный метод — это порождающий паттерн проектирования, который определяет общий интерфейс для создания объектов в суперклассе, позволяя подклассам изменять тип создаваемых объектов.
Проблема
Представьте, что вы создаёте программу управления грузовыми перевозками. Сперва вы рассчитываете перевозить товары только на автомобилях. Поэтому весь ваш код работает с объектами класса Грузовик .
В какой-то момент ваша программа становится настолько известной, что морские перевозчики выстраиваются в очередь и просят добавить поддержку морской логистики в программу.
Отличные новости, правда?! Но как насчёт кода? Большая часть существующего кода жёстко привязана к классам Грузовиков . Чтобы добавить в программу классы морских Судов , понадобится перелопатить всю программу. Более того, если вы потом решите добавить в программу ещё один вид транспорта, то всю эту работу придётся повторить.
В итоге вы получите ужасающий код, наполненный условными операторами, которые выполняют то или иное действие, в зависимости от класса транспорта.
Решение
Паттерн Фабричный метод предлагает создавать объекты не напрямую, используя оператор new , а через вызов особого фабричного метода. Не пугайтесь, объекты всё равно будут создаваться при помощи new , но делать это будет фабричный метод.
На первый взгляд, это может показаться бессмысленным: мы просто переместили вызов конструктора из одного конца программы в другой. Но теперь вы сможете переопределить фабричный метод в подклассе, чтобы изменить тип создаваемого продукта.
Чтобы эта система заработала, все возвращаемые объекты должны иметь общий интерфейс. Подклассы смогут производить объекты различных классов, следующих одному и тому же интерфейсу.
Например, классы Грузовик и Судно реализуют интерфейс Транспорт с методом доставить . Каждый из этих классов реализует метод по-своему: грузовики везут грузы по земле, а суда — по морю. Фабричный метод в классе ДорожнойЛогистики вернёт объект-грузовик, а класс МорскойЛогистики — объект-судно.
Для клиента фабричного метода нет разницы между этими объектами, так как он будет трактовать их как некий абстрактный Транспорт . Для него будет важно, чтобы объект имел метод доставить , а как конкретно он работает — не важно.
Структура
- Продукт определяет общий интерфейс объектов, которые может произвести создатель и его подклассы.
- Конкретные продукты содержат код различных продуктов. Продукты будут отличаться реализацией, но интерфейс у них будет общий.
- Создатель объявляет фабричный метод, который должен возвращать новые объекты продуктов. Важно, чтобы тип результата совпадал с общим интерфейсом продуктов. Зачастую фабричный метод объявляют абстрактным, чтобы заставить все подклассы реализовать его по-своему. Но он может возвращать и некий стандартный продукт. Несмотря на название, важно понимать, что создание продуктов не является единственной функцией создателя. Обычно он содержит и другой полезный код работы с продуктом. Аналогия: большая софтверная компания может иметь центр подготовки программистов, но основная задача компании — создавать программные продукты, а не готовить программистов.
- Конкретные создатели по-своему реализуют фабричный метод, производя те или иные конкретные продукты. Фабричный метод не обязан всё время создавать новые объекты. Его можно переписать так, чтобы возвращать существующие объекты из какого-то хранилища или кэша.
Псевдокод
В этом примере Фабричный метод помогает создавать кросс-платформенные элементы интерфейса, не привязывая основной код программы к конкретным классам элементов.
Фабричный метод объявлен в классе диалогов. Его подклассы относятся к различным операционным системам. Благодаря фабричному методу, вам не нужно переписывать логику диалогов под каждую систему. Подклассы могут наследовать почти весь код из базового диалога, изменяя типы кнопок и других элементов, из которых базовый код строит окна графического пользовательского интерфейса.
Базовый класс диалогов работает с кнопками через их общий программный интерфейс. Поэтому, какую вариацию кнопок ни вернул бы фабричный метод, диалог останется рабочим. Базовый класс не зависит от конкретных классов кнопок, оставляя подклассам решение о том, какой тип кнопок создавать.
Такой подход можно применить и для создания других элементов интерфейса. Хотя каждый новый тип элементов будет приближать вас к Абстрактной фабрике.
// Паттерн Фабричный метод применим тогда, когда в программе // есть иерархия классов продуктов. interface Button is method render() method onClick(f) class WindowsButton implements Button is method render(a, b) is // Отрисовать кнопку в стиле Windows. method onClick(f) is // Навесить на кнопку обработчик событий Windows. class HTMLButton implements Button is method render(a, b) is // Вернуть HTML-код кнопки. method onClick(f) is // Навесить на кнопку обработчик события браузера. // Базовый класс фабрики. Заметьте, что "фабрика" — это всего // лишь дополнительная роль для класса. Скорее всего, он уже // имеет какую-то бизнес-логику, в которой требуется создание // разнообразных продуктов. class Dialog is method render() is // Чтобы использовать фабричный метод, вы должны // убедиться в том, что эта бизнес-логика не зависит от // конкретных классов продуктов. Button — это общий // интерфейс кнопок, поэтому все хорошо. Button okButton = createButton() okButton.onClick(closeDialog) okButton.render() // Мы выносим весь код создания продуктов в особый метод, // который назвают "фабричным". abstract method createButton():Button // Конкретные фабрики переопределяют фабричный метод и // возвращают из него собственные продукты. class WindowsDialog extends Dialog is method createButton():Button is return new WindowsButton() class WebDialog extends Dialog is method createButton():Button is return new HTMLButton() class Application is field dialog: Dialog // Приложение создаёт определённую фабрику в зависимости от // конфигурации или окружения. method initialize() is config = readApplicationConfigFile() if (config.OS == "Windows") then dialog = new WindowsDialog() else if (config.OS == "Web") then dialog = new WebDialog() else throw new Exception("Error! Unknown operating system.") // Если весь остальной клиентский код работает с фабриками и // продуктами только через общий интерфейс, то для него // будет не важно, какая фабрика была создана изначально. method main() is this.initialize() dialog.render()
Применимость
Когда заранее неизвестны типы и зависимости объектов, с которыми должен работать ваш код.
Фабричный метод отделяет код производства продуктов от остального кода, который эти продукты использует.
Благодаря этому, код производства можно расширять, не трогая основной. Так, чтобы добавить поддержку нового продукта, вам нужно создать новый подкласс и определить в нём фабричный метод, возвращая оттуда экземпляр нового продукта.
Когда вы хотите дать возможность пользователям расширять части вашего фреймворка или библиотеки.
Пользователи могут расширять классы вашего фреймворка через наследование. Но как сделать так, чтобы фреймворк создавал объекты из этих новых классов, а не из стандартных?
Решением будет дать пользователям возможность расширять не только желаемые компоненты, но и классы, которые создают эти компоненты. А для этого создающие классы должны иметь конкретные создающие методы, которые можно определить.
Например, вы используете готовый UI-фреймворк для своего приложения. Но вот беда — требуется иметь круглые кнопки, вместо стандартных прямоугольных. Вы создаёте класс RoundButton . Но как сказать главному классу фреймворка UIFramework , чтобы он теперь создавал круглые кнопки, вместо стандартных?
Для этого вы создаёте подкласс UIWithRoundButtons из базового класса фреймворка, переопределяете в нём метод создания кнопки (а-ля createButton ) и вписываете туда создание своего класса кнопок. Затем используете UIWithRoundButtons вместо стандартного UIFramework .
Когда вы хотите экономить системные ресурсы, повторно используя уже созданные объекты, вместо порождения новых.
Такая проблема обычно возникает при работе с тяжёлыми ресурсоёмкими объектами, такими, как подключение к базе данных, файловой системе и т. д.
Представьте, сколько действий вам нужно совершить, чтобы повторно использовать существующие объекты:
- Сначала вам следует создать общее хранилище, чтобы хранить в нём все создаваемые объекты.
- При запросе нового объекта нужно будет заглянуть в хранилище и проверить, есть ли там неиспользуемый объект.
- А затем вернуть его клиентскому коду.
- Но если свободных объектов нет — создать новый, не забыв добавить его в хранилище.
Весь этот код нужно куда-то поместить, чтобы не засорять клиентский код.
Самым удобным местом был бы конструктор объекта, ведь все эти проверки нужны только при создании объектов. Но, увы, конструктор всегда создаёт новые объекты, он не может вернуть существующий экземпляр.
Значит, нужен другой метод, который бы отдавал как существующие, так и новые объекты. Им и станет фабричный метод.
Шаги реализации
- Приведите все создаваемые продукты к общему интерфейсу.
- В классе, который производит продукты, создайте пустой фабричный метод. В качестве возвращаемого типа укажите общий интерфейс продукта.
- Затем пройдитесь по коду класса и найдите все участки, создающие продукты. Поочерёдно замените эти участки вызовами фабричного метода, перенося в него код создания различных продуктов. В фабричный метод, возможно, придётся добавить несколько параметров, контролирующих, какой из продуктов нужно создать. На этом этапе фабричный метод, скорее всего, будет выглядеть удручающе. В нём будет жить большой условный оператор, выбирающий класс создаваемого продукта. Но не волнуйтесь, мы вот-вот исправим это.
- Для каждого типа продуктов заведите подкласс и переопределите в нём фабричный метод. Переместите туда код создания соответствующего продукта из суперкласса.
- Если создаваемых продуктов слишком много для существующих подклассов создателя, вы можете подумать о введении параметров в фабричный метод, которые позволят возвращать различные продукты в пределах одного подкласса. Например, у вас есть класс Почта с подклассами АвиаПочта и НаземнаяПочта , а также классы продуктов Самолёт , Грузовик и Поезд . Авиа соответствует Самолётам , но для НаземнойПочты есть сразу два продукта. Вы могли бы создать новый подкласс почты для поездов, но проблему можно решить и по-другому. Клиентский код может передавать в фабричный метод НаземнойПочты аргумент, контролирующий тип создаваемого продукта.
- Если после всех перемещений фабричный метод стал пустым, можете сделать его абстрактным. Если в нём что-то осталось — не беда, это будет его реализацией по умолчанию.
Преимущества и недостатки
- Избавляет класс от привязки к конкретным классам продуктов.
- Выделяет код производства продуктов в одно место, упрощая поддержку кода.
- Упрощает добавление новых продуктов в программу.
- Реализует принцип открытости/закрытости.
- Может привести к созданию больших параллельных иерархий классов, так как для каждого класса продукта надо создать свой подкласс создателя.
Отношения с другими паттернами
- Многие архитектуры начинаются с применения Фабричного метода (более простого и расширяемого через подклассы) и эволюционируют в сторону Абстрактной фабрики, Прототипа или Строителя (более гибких, но и более сложных).
- Классы Абстрактной фабрики чаще всего реализуются с помощью Фабричного метода, хотя они могут быть построены и на основе Прототипа.
- Фабричный метод можно использовать вместе с Итератором, чтобы подклассы коллекций могли создавать подходящие им итераторы.
- Прототип не опирается на наследование, но ему нужна сложная операция инициализации. Фабричный метод, наоборот, построен на наследовании, но не требует сложной инициализации.
- Фабричный метод можно рассматривать как частный случай Шаблонного метода. Кроме того, Фабричный метод нередко бывает частью большого класса с Шаблонными методами.
Примеры реализации паттерна
Дополнительные материалы
- Если вы уже слышали о Фабрике, Фабричном методе или Абстрактной фабрике, но с трудом их различаете — почитайте нашу статью Сравнение фабрик.
Не втыкай в транспорте
Лучше почитай нашу книгу о паттернах проектирования.
Теперь это удобно делать даже во время поездок в общественном транспорте.
Эта статья является частью нашей электронной книги Погружение в Паттерны Проектирования.
- Премиум контент
- Книга о паттернах
- Курс по рефакторингу
- Введение в рефакторинг
- Чистый код
- Технический долг
- Когда рефакторить
- Как рефакторить
- Раздувальщики
- Длинный метод
- Большой класс
- Одержимость элементарными типами
- Длинный список параметров
- Группы данных
- Операторы switch
- Временное поле
- Отказ от наследства
- Альтернативные классы с разными интерфейсами
- Расходящиеся модификации
- Стрельба дробью
- Параллельные иерархии наследования
- Комментарии
- Дублирование кода
- Ленивый класс
- Класс данных
- Мёртвый код
- Теоретическая общность
- Завистливые функции
- Неуместная близость
- Цепочка вызовов
- Посредник
- Неполнота библиотечного класса
- Составление методов
- Извлечение метода
- Встраивание метода
- Извлечение переменной
- Встраивание переменной
- Замена переменной вызовом метода
- Расщепление переменной
- Удаление присваиваний параметрам
- Замена метода объектом методов
- Замена алгоритма
- Перемещение метода
- Перемещение поля
- Извлечение класса
- Встраивание класса
- Сокрытие делегирования
- Удаление посредника
- Введение внешнего метода
- Введение локального расширения
- Самоинкапсуляция поля
- Замена простого поля объектом
- Замена значения ссылкой
- Замена ссылки значением
- Замена поля-массива объектом
- Дублирование видимых данных
- Замена однонаправленной связи двунаправленной
- Замена двунаправленной связи однонаправленной
- Замена магического числа символьной константой
- Инкапсуляция поля
- Инкапсуляция коллекции
- Замена кодирования типа классом
- Замена кодирования типа подклассами
- Замена кодирования типа состоянием/стратегией
- Замена подкласса полями
- Разбиение условного оператора
- Объединение условных операторов
- Объединение дублирующихся фрагментов в условных операторах
- Удаление управляющего флага
- Замена вложенных условных операторов граничным оператором
- Замена условного оператора полиморфизмом
- Введение Null-объекта
- Введение проверки утверждения
- Переименование метода
- Добавление параметра
- Удаление параметра
- Разделение запроса и модификатора
- Параметризация метода
- Замена параметра набором специализированных методов
- Передача всего объекта
- Замена параметра вызовом метода
- Замена параметров объектом
- Удаление сеттера
- Сокрытие метода
- Замена конструктора фабричным методом
- Замена кода ошибки исключением
- Замена исключения проверкой условия
- Подъём поля
- Подъём метода
- Подъём тела конструктора
- Спуск метода
- Спуск поля
- Извлечение подкласса
- Извлечение суперкласса
- Извлечение интерфейса
- Свёртывание иерархии
- Создание шаблонного метода
- Замена наследования делегированием
- Замена делегирования наследованием
- Введение в паттерны
- Что такое Паттерн?
- История паттернов
- Зачем знать паттерны?
- Критика паттернов
- Классификация паттернов
- Фабричный метод
- Абстрактная фабрика
- Строитель
- Прототип
- Одиночка
- Адаптер
- Мост
- Компоновщик
- Декоратор
- Фасад
- Легковес
- Заместитель
- Цепочка обязанностей
- Команда
- Итератор
- Посредник
- Снимок
- Наблюдатель
- Состояние
- Стратегия
- Шаблонный метод
- Посетитель
- C#
- C++
- Go
- Java
- PHP
- Python
- Ruby
- Rust
- Swift
- TypeScript
Паттерн Factory Method (фабричный метод)
В системе часто требуется создавать объекты самых разных типов. Паттерн Factory Method (фабричный метод) может быть полезным в решении следующих задач:
- Система должна оставаться расширяемой путем добавления объектов новых типов. Непосредственное использование выражения new является нежелательным, так как в этом случае код создания объектов с указанием конкретных типов может получиться разбросанным по всему приложению. Тогда такие операции как добавление в систему объектов новых типов или замена объектов одного типа на другой будут затруднительными (подробнее в разделе Порождающие паттерны). Паттерн Factory Method позволяет системе оставаться независимой как от самого процесса порождения объектов, так и от их типов.
- Заранее известно, когда нужно создавать объект, но неизвестен его тип.
Описание паттерна Factory Method
Для того, чтобы система оставалась независимой от различных типов объектов, паттерн Factory Method использует механизм полиморфизма — классы всех конечных типов наследуют от одного абстрактного базового класса, предназначенного для полиморфного использования. В этом базовом классе определяется единый интерфейс, через который пользователь будет оперировать объектами конечных типов.
Для обеспечения относительно простого добавления в систему новых типов паттерн Factory Method локализует создание объектов конкретных типов в специальном классе-фабрике. Методы этого класса, посредством которых создаются объекты конкретных классов, называются фабричными. Существуют две разновидности паттерна Factory Method:
Обобщенный конструктор, когда в том же самом полиморфном базовом классе, от которого наследуют производные классы всех создаваемых в системе типов, определяется статический фабричный метод. В качестве параметра в этот метод должен передаваться идентификатор типа создаваемого объекта.
UML-диаграмма классов паттерна Factory Method. Обобщенный конструктор
Классический вариант фабричного метода, когда интерфейс фабричных методов объявляется в независимом классе-фабрике, а их реализация определяется конкретными подклассами этого класса.
UML-диаграмма классов паттерна Factory Method. Классическая реализация
Реализация паттерна Factory Method
Рассмотрим оба варианта реализации паттерна Factory Method на примере процесса порождения военных персонажей для нашей стратегической игры. Ее подробное описание можно найти в разделе Порождающие паттерны. Для упрощения демонстрационного кода будем создавать военные персонажи для некой абстрактной армии без учета особенностей воюющих сторон.
Реализация паттерна Factory Method на основе обобщенного конструктора
// #include #include enum Warrior_ID < Infantryman_ID=0, Archer_ID, Horseman_ID >; // Иерархия классов игровых персонажей class Warrior < public: virtual void info() = 0; virtual ~Warrior() <>// Параметризированный статический фабричный метод static Warrior* createWarrior( Warrior_ID id ); >; class Infantryman: public Warrior < public: void info() < cout >; class Archer: public Warrior < public: void info() < cout >; class Horseman: public Warrior < public: void info() < cout >; // Реализация параметризированного фабричного метода Warrior* Warrior::createWarrior( Warrior_ID id ) < Warrior * p; switch (id) < case Infantryman_ID: p = new Infantryman(); break; case Archer_ID: p = new Archer(); break; case Horseman_ID: p = new Horseman(); break; default: assert( false); >return p; >; // Создание объектов при помощи параметризированного фабричного метода int main() < vectorv; v.push_back( Warrior::createWarrior( Infantryman_ID)); v.push_back( Warrior::createWarrior( Archer_ID)); v.push_back( Warrior::createWarrior( Horseman_ID)); for(int i=0; iinfo(); // . >
Представленный вариант паттерна Factory Method пользуется популярностью благодаря своей простоте. В нем статический фабричный метод createWarrior() определен непосредственно в полиморфном базовом классе Warrior . Этот фабричный метод является параметризированным, то есть для создания объекта некоторого типа в createWarrior() передается соответствующий идентификатор типа.
С точки зрения «чистоты» объектно-ориентированного кода у этого варианта есть следующие недостатки:
- Так как код по созданию объектов всех возможных типов сосредоточен в статическом фабричном методе класса Warrior , то базовый класс Warrior обладает знанием обо всех производных от него классах, что является нетипичным для объектно-ориентированного подхода.
- Подобное использование оператора switch (как в коде фабричного метода createWarrior() ) в объектно-ориентированном программировании также не приветствуется.
Указанные недостатки отсутствуют в классической реализации паттерна Factory Method.
Классическая реализация паттерна Factory Method
// #include #include // Иерархия классов игровых персонажей class Warrior < public: virtual void info() = 0; virtual ~Warrior() <>>; class Infantryman: public Warrior < public: void info() < cout ; >; class Archer: public Warrior < public: void info() < cout ; >; class Horseman: public Warrior < public: void info() < cout ; >; // Фабрики объектов class Factory < public: virtual Warrior* createWarrior() = 0; virtual ~Factory() <>>; class InfantryFactory: public Factory < public: Warrior* createWarrior() < return new Infantryman; >>; class ArchersFactory: public Factory < public: Warrior* createWarrior() < return new Archer; >>; class CavalryFactory: public Factory < public: Warrior* createWarrior() < return new Horseman; >>; // Создание объектов при помощи фабрик объектов int main() < InfantryFactory* infantry_factory = new InfantryFactory; ArchersFactory* archers_factory = new ArchersFactory ; CavalryFactory* cavalry_factory = new CavalryFactory ; vectorv; v.push_back( infantry_factory->createWarrior()); v.push_back( archers_factory->createWarrior()); v.push_back( cavalry_factory->createWarrior()); for(int i=0; iinfo(); // . >
Классический вариант паттерна Factory Method использует идею полиморфной фабрики. Специально выделенный для создания объектов полиморфный базовый класс Factory объявляет интерфейс фабричного метода createWarrior() , а производные классы его реализуют.
Представленный вариант паттерна Factory Method является наиболее распространенным, но не единственным. Возможны следующие вариации:
- Класс Factory имеет реализацию фабричного метода createWarrior() по умолчанию.
- Фабричный метод createWarrior() класса Factory параметризирован типом создаваемого объекта (как и у представленного ранее, простого варианта Factory Method) и имеет реализацию по умолчанию. В этом случае, производные от Factory классы необходимы лишь для того, чтобы определить нестандартное поведение createWarrior() .
Результаты применения паттерна Factory Method
Достоинства паттерна Factory Method
- Создает объекты разных типов, позволяя системе оставаться независимой как от самого процесса создания, так и от типов создаваемых объектов.
Недостатки паттерна Factory Method
- В случае классического варианта паттерна даже для порождения единственного объекта необходимо создавать соответствующую фабрику