что такое паттерн фабрика
«Фабричный метод» и «Абстрактная фабрика» во вселенной «Swift» и «iOS»
Слово «фабрика» – безусловно одно из самых часто употребляемых программистами при обсуждении своих (или чужих) программ. Но смысл в него вкладываемый бывает очень разным: это может быть и класс, порождающий объекты (полиморфно или нет); и метод, создающий экземпляры какого-либо типа (статический или нет); бывает, и даже просто любой порождающий метод (включая, конструкторы).
Конечно, не все, что угодно, порождающее экземпляры чего-либо, может называться словом «фабрика». Более того, под этим словом могут скрываться два разных порождающих шаблона из арсенала «Банды четырех» – «фабричный метод» и «абстрактная фабрика», в подробности которых я и хотел бы немного углубиться, уделяя особое внимание классическим их пониманию и реализации.
А на написание этого очерка меня вдохновил Джошуа Керивски (глава «Industrial Logic»), а точнее, его книга «Refactoring to Patterns», которая вышла в начале века в рамках серии книг, основанной Мартином Фаулером (именитым автором современной классики программирования – книги «Рефакторинг»). Если кто-то не читал или даже не слышал о первой (а я знаю таких много), то обязательно добавьте ее себе в список для чтения. Это достойный «сиквел» как «Рефакторинга», так и еще более классической книги – «Приемов объектно-ориентированного проектирования. Паттерны проектирования».
Книга, помимо прочего, содержит в себе несколько десятков рецептов избавления от различных «запахов» в коде с помощью шаблонов проектирования. В том числе и три (как минимум) «рецепта» на обсуждаемую тему.
Абстрактная фабрика
Керивски в своей книге приводит два случая, когда применение этого шаблона будет полезным.
Первый – это инкапсуляция знаний о конкретных классах, связанных общим интерфейсом. В таком случае этими знаниями будет обладать лишь тип, являющейся фабрикой. Публичный API фабрики будет состоять из набора методов (статических или нет), возвращающих экземпляры типа общего интерфейса и имеющих какие-либо «говорящие» названия (чтобы понимать, какой метод необходимо вызвать для той или иной цели).
Второй пример очень похож на первый (и, в общем-то, все сценарии использования паттерна более-менее подобны друг другу). Речь идет о случае, когда экземпляры одного или нескольких типов одной группы создаются в разных местах программы. Фабрика в этом случае опять-таки инкапсулирует знания о создающем экземпляры коде, но с несколько иной мотивацией. Например, это особенно актуально, если процесс создания экземпляров этих типов сложный и не ограничивается вызовом конструктора.
Я постараюсь сохранять примеры кода как можно ближе к классической реализации из книги «Банды четырех», но в реальной жизни часто код бывает упрощенным тем или иным образом. И лишь достаточное понимание шаблона открывает двери для его более вольного использования.
Подробный пример
Предположим, мы в приложении торгуем средствами передвижения, и от типа конкретного средства зависит отображение: мы будем использовать разные подклассы UIViewController для разных средств передвижения. Помимо этого, все средства передвижения различаются состоянием (новые и б/у):
Таким образом, у нас есть семейство объектов одной группы, экземпляры типов которых создаются в одних и тех же местах в зависимости от какого-то условия (например, пользователь нажал на товар в списке, и в зависимости от того, самокат это или велосипед, мы создаем соответствующий контроллер). Конструкторы контроллеров имеют некоторые параметры, которые также необходимо каждый раз задавать. Не свидетельствуют ли эти два довода в пользу создания «фабрики», которая одна будет обладать знаниями о логике создания нужного контроллера?
Конечно, пример достаточно простой, и в реальном проекте в похожем случае вводить «фабрику» будет явным «overengineering». Тем не менее, если представить, что типов транспортных средств у нас не два, а параметров у конструкторов – не один, то преимущества «фабрики» станут более очевидными.
Итак, объявим интерфейс, который будет играть роль «абстрактной фабрики»:
(Довольно краткий «гайдлайн» по проектированию «API» на языке «Swift» рекомендует называть «фабричные» методы начиная со слова «make».)
(Пример в книге банды четырех приведен на «C++» и основывается на наследовании и «виртуальных» функциях. Используя «Swift» нам, конечно, ближе парадигма протокольно-ориентированного программирования.)
Интерфейс абстрактной фабрики содержит всего два метода: для создания контроллеров для продажи велосипедов и самокатов. Методы возвращают экземпляры не конкретных подклассов, а общего базового класса. Таким образом, ограничивается область распространения знаний о конкретных типах пределами той области, в которой это действительно необходимо.
В качестве «конкретных фабрик» будем использовать две реализации интерфейса абстрактной фабрики:
В данном случае, как видно из кода, конкретные фабрики отвечают за транспортные средства разного состояния (новые и подержанные).
Создание нужного контроллера отныне будет выглядеть примерно так:
Инкапусляция классов с помощью фабрики
Теперь вкратце пробежимся по примерам использования, которые предлагает в своей книге Керивски.
Первый «кейс» связан с инкапсуляцией конкретных классов. Для примера возьмем те же контроллеры для отображения данных о транспортных средствах:
Перемещение знаний о создании объекта внутрь фабрики
Второй «кейс» описывает сложную инициализацию объекта, и Керивски, в качестве одного из путей упрощения кода и оберегания принципов инкапсуляции, предлагает ограничение распространения знаний о процессе инициализации пределами фабрики.
Предположим, мы захотели продавать заодно уж и автомобили. А это, несомненно, более сложная техника, обладающая бóльшим числом характеристик. Для примера ограничимся типом используемого топлива, типом трансмиссии и размером колесного диска:
Пример инициализации соответствующего контроллера:
Мы можем ответственность за все эти «мелочи» водрузить на «плечи» специализированной фабрики:
И создавать контроллер уже таким образом:
Фабричный метод
Книга «Банды четырех» сообщает, что шаблон также известен под названием «виртуальный конструктор», и это не зря. В «C++» виртуальной называется функция, переопределяемая в производных классах. Возможности объявить виртуальным конструктор язык не дает, и не исключено, что именно попытка сымитировать нужное поведение привела к изобретению данного паттерна.
Полиморфное создание объектов
В качестве классического примера пользы шаблона рассмотрим случай, когда в иерархии разные типы имеют идентичную реализацию одного метода за исключением объекта, который в этом методе создается и используется. В качестве решения предлагается создание этого объекта вынести в отдельный метод и реализовывать его отдельно, а общий метод – поднять выше в иерархии. Таким образом, разные типы будут использовать общую реализацию метода, а объект, необходимый для этого метода, будет создаваться полиморфно.
Для примера вернемся к нашим контроллерам для отображения транспортных средств:
И предположим, что для их отображения используется некая сущность, например, координатор, который представляет эти контроллеры модально из другого контроллера:
При этом метод start() используется всегда одинаково, за исключением того, что в нем создаются разные контроллеры:
Предлагаемое решение – это вынести создание используемого объекта в отдельный метод:
А основной метод – снабдить базовой реализацией:
Конкретные типы в таком случае примут вид:
Заключение
Я попытался данную несложную тему осветить, совместив три подхода:
Как оказалось, найти подробные материалы на тему, содержащие прикладные примеры довольно сложно. Большинство существующих статей и руководств содержат лишь поверхностные обзоры и сокращенные примеры, уже довольно урезанные по сравнению с хрестоматийными версиями реализаций.
Надеюсь, хотя бы отчасти мне удалось достичь поставленных целей, а читателю – хотя бы отчасти было интересно или хотя бы любопытно узнать или освежить свои знания по данной теме.
Другие мои материалы на тему шаблонов проектирования:
Паттерны проектирования: Singleton, Factory, FactoryMethod, AbstractFactory
— Сегодня у нас будет не просто интересная, а прямо-таки эпическая тема.
Сегодня я расскажу тебе, что такое шаблоны проектирования (design patterns).
— Круто. Много про них слышал. Жду с нетерпением.
— Опытным программистам приходится писать очень много классов. Но самая сложная часть этой работы – это решать, какие классы должны быть и как распределить работу между ними.
Чем чаще они решали такие вопросы, тем чаще стали понимать, что существуют некоторые удачные решения, и наоборот, неудачные.
Неудачные решения обычно создают проблем больше, чем решают. Они плохо расширяются, создают много лишних ограничений, и т.п. А удачные решения – наоборот.
— А можно какую-нибудь аналогию?
— Допустим, ты строишь дом. И думаешь — из чего же он должен состоять. Ты решил, что тебе нужны стены, пол и потолок. В результате ты построил дом без фундамента и с ровной крышей. Такой дом будет трескаться, а крыша – протекать. Это – неудачное решение.
И наоборот: дом, состоящий из фундамента, стен и двускатной крыши будет удачным решением. Ему нестрашны ни большие снегопады (снег будет скатываться с крыши), ни подвижки почвы – фундамент будет обеспечивать стабильность. Такое решение мы назовем удачным.
— Ок. Тогда я продолжу.
Со временем сборник удачных решений объявили «паттернами (шаблонами) проектирования», а сборник неудачных – антипаттернами.
Сам шаблон проектирования — это как бы ответ на вопрос. Его сложно понять, если не слышал самого вопроса.
Первая категория паттернов – это порождающие паттерны. Такие паттерны описывают удачные решения, связанные с созданием объектов.
— А что такого сложного в создании объектов?
— А вот как раз сейчас мы это и разберем.
Паттерн Singleton – Синглетон, Одиночка.
Часто в программе некоторые объекты могут существовать только в единственном экземпляре. Например, консоль, логгер, сборщик мусора и т.д.
Неудачное решение: отказаться от создания объектов, просто создать класс у которого все методы статические.
Удачное решение: создать единственный объект класса и хранить его в статической переменной. Запретить создание второго объекта этого класса. Пример:
Во-первых, мы сделали конструктор private. Теперь его можно вызвать только изнутри нашего класса. Мы запретили создание объекта Sun везде кроме методов класса Sun.
Во-вторых, получить объект этого класса можно только вызвав метод getInstance(). Это не только единственный метод, который может создавать объекты класса Sun, но он еще и следит, чтобы такой объект был единственным.
— Когда человек думает – как же именно это сделать? Паттерн говорит – можешь попробовать так – это одно из удачных решений.
— Спасибо. Теперь что-то начинает проясняться.
— Также про этот паттерн можно прочитать здесь.
Паттерн Factory – Фэктори, Фабрика.
Очень часто программисты сталкиваются вот с какой ситуацией. У тебя есть некоторый базовый класс и много подклассов. Например – персонаж игры – GamePerson и классы всех остальных персонажей игры, унаследованные от него.
Допустим, у тебя есть такие классы:
Вопрос состоит в том, как гибко и удобно управлять созданием объектов этих классов.
Если проблема кажется тебе надуманной, представь, что в игре нужно создавать десятки мечей и щитов, сотни магических заклинаний, тысячи монстров. Без удобного подхода к созданию объектов тут не обойтись.
Вот какое «удачное решение» предлагает паттерн Фабрика (Factory).
Во-первых, надо завести enum, значения которого будут соответствовать различным классам.
Во-вторых, сделать специальный класс – Factory, у которого будет статический метод или методы, которые и будут заниматься созданием объекта(ов) в зависимости от enum’а.
— Т.е. мы создали специальный класс для управления созданием объектов?
— А какие преимущества это дает?
— Во-первых, там эти объекты можно инициализировать нужными данными.
Во-вторых, между методами можно сколько угодно передавать нужный enum, чтобы в конце концов по нему создали правильный объект.
В третьих, количество значений enum не обязательно должно совпадать с количеством классов. Типов персонажей может быть много, а классов – мало.
Например, для Mag & Warrior можно использовать один класс – Human, но с различными настройками силы и магии (параметрами конструктора).
Вот как это может выглядеть (для наглядности добавил еще темных эльфов):
В примере выше мы использовали всего три класса, чтобы создавать объекты шести разных типов. Это очень удобно. Более того, у нас вся эта логика сосредоточена в одном классе и в одном методе.
Если мы вдруг решим создать отдельный класс для Огра, мы просто поменяем тут пару строк кода, а не будем перелопачивать половину приложения.
— Согласен. Удачное решение.
— А я тебе о чем говорю, шаблоны проектирования – это сборники удачных решений.
— Знать бы еще, где их применять…
— Ага. Сходу не разберёшься, согласен. Но все равно, лучше знать и не уметь, чем не знать и не уметь. Вот тебе еще полезная ссылка по этому паттерну: Паттерн Factory
— Паттерн Abstract Factory – Абстрактная Фабрика.
Иногда, когда объектов очень много, напрашивается создание фабрики фабрик. Такую фабрику принято называть Абстрактной Фабрикой.
— Это ж где такое может понадобиться?
— Ну, например, у тебя есть несколько групп идентичных объектов. Это легче показать на примере.
Смотри, допустим, у тебя в игре есть три расы – люди, эльфы и демоны. И у каждой расы для баланса есть воин, лучник и маг. В зависимости от того, за какую сторону играет человек, он может создавать только объекты своей расы. Вот как это могло бы выглядеть в коде:
А теперь создадим расы, ну или можно еще назвать их армиями.
Абстрактная фабрика на PHP
Абстрактная фабрика — это порождающий паттерн проектирования, который решает проблему создания целых семейств связанных продуктов, без указания конкретных классов продуктов.
Особенности паттерна на PHP
Сложность:
Популярность:
Применимость: Паттерн можно часто встретить в PHP-коде, особенно там, где требуется создание семейств продуктов (например, во всевозможных фреймворках).
Признаки применения паттерна: Паттерн можно определить по методам, возвращающим фабрику, которая, в свою очередь, используется для создания конкретных продуктов, возвращая их через абстрактные типы или интерфейсы.
Концептуальный пример
Этот пример показывает структуру паттерна Абстрактная фабрика, а именно — из каких классов он состоит, какие роли эти классы выполняют и как они взаимодействуют друг с другом.
После ознакомления со структурой, вам будет легче воспринимать второй пример, который рассматривает реальный случай использования паттерна в мире PHP.
Пример из реальной жизни
В этом примере паттерн Абстрактная фабрика предоставляет инфраструктуру для создания нескольких разновидностей шаблонов для одних и тех же элементов веб-страницы.
Чтобы веб-приложение могло поддерживать сразу несколько разных движков рендеринга страниц, его классы должны работать с шаблонами только через интерфейсы, не привязываясь к конкретным классам. Чтобы этого достичь, объекты приложения не должны создавать шаблоны напрямую, а поручать создание специальным объектам-фабрикам, с которыми тоже надо работать через абстрактный интерфейс.
Благодаря этому, вы можете подать в приложение фабрику, соответствующую одному из движков рендеринга, зная, что с этого момента, все шаблоны будут порождаться именно этой фабрикой, и будут соответствовать движку рендеринга этой фабрики. Если вы захотите сменить движок рендеринга, то всё что нужно будет сделать — это подать в приложение объект фабрики другого типа и ничего при этом не сломается.
Абстрактная фабрика на других языках программирования
Купи книгу Погружение в Паттерны и получи архив с десятками детальных примеров, которые можно открывать прямо в IDE.
Паттерны: Абстрактная фабрика
Каждый паттерн будет разбираться с трех сторон: общая теория, метафора, пример реализации.
Абстрактная фабрика
Теория из различных источников
Что мы знаем об Абстрактной фабрике из существующих источников знаний?! Назначение данного паттерна, если цитировать википедию, состоит в следующем: “Абстрактная фабрика предоставляет интерфейс для создания семейств взаимосвязанных или взаимозависимых объектов, не специфицируя их конкретных классов”. Для лучшего понимания происходящего нам пригодиться диаграмма (из той же википедии).
Метафора
В качестве метафоры, которая позволит пролить свет на данный паттерн, мне показалась уместной следующая.
Представьте себе автомат по розливу газированной воды (см. картинку выше). У него есть кран, через который напиток наливается в стакан и специальная система (шланги, тройники, штуцеры), к которой подключаются емкости с газ.водой.
Программная реализация Абстрактной фабрики на языке C#
Сделаем небольшое приложение, которое визуально выглядит так, как показано на рисунке ниже.
Структура проекта выглядит вот так.
Перечислим файлы, входящие в него:
Имя файла | Описание |
IAbstractFactory.cs | Интерфейс Абстрактной фабрики |
ConcreteFactoryGreen.cs | Конкретная фабрика, создающая зеленые фигуры |
ConcreteFactoryRed.cs | Конкретная фабрика, создающая красные фигуры |
ICircle.cs | Интерфейс для работы с фигурой круг |
ISquare.cs | Интерфейс для работы с фигурой квадрат |
CircleRed.cs, CircleGreen.cs | Классы, реализующие красный и зеленый круг |
SquareRed.cs, SquareGreen.cs | Классы, реализующие красный и зеленый квадрат |
Клиентом, в данном случае, выступает форма ( Form.cs ), а точнее один из ее методов (об этом чуть ниже).
Для начала посмотрим на Абстрактную фабрику.
Теперь перейдем к конкретным фабрикам, посмотрим на ту, что создает красные фигуры.
Фабрика, создающая зеленые фигуры (файл ConcreteFactoryGreen.cs ) выглядит практически также, поэтому приводить мы ее не будем.
Теперь перейдем к классам фигур. Для начала нужно создать интерфейсы для работы с фигурами.
Интерфейс круга позволяет получать и задавать радиус и получать цвет, квадрата – получать и задавать длину стороны и получать цвет.
Реализации классов красных кругов и квадратов представлены ниже.
Перейдем к клиенту, как мы уже определились выше – это форма ( Form.cs ). В ней мы должны создать две конкретные фабрики.
В зависимости от того, какой цвет выбран, вызывается метод DrawFigure() с той или иной конкретной фабрикой, создающей объекты нужно цвета.
Этот метод принимает в качестве аргумента Абстрактную фабрику и с помощью нее создает фигуры – круг и квадрат. После этого отрисовывает их на экране, используя свойства данных фигур (размеры и цвет).
На этом закончим наше обсуждение Абстрактной фабрики. Надеюсь было понятно. Если что, пишите пожалуйста комментарии.
Вот ещё несколько книжек в тему:
| Книга «Приемы объектно-ориентированного проектирования. Паттерны проектирования» Э. Гамма, Р. Хелм, Р. Джонсон, Дж. Влиссидес |
| Книга «Паттерны проектирования» Эрик Фримен, Элизабет Фримен, Кэтти Сьерра, Берт Бейтс |
Паттерны: Абстрактная фабрика : 6 комментариев
Для начала, паттерн Абстрактная фабрика предлагает выделить общие интерфейсы для отдельных продуктов, составляющих семейства. Так, все вариации кресел получат общий интерфейс
Измените код инициализации программы так, чтобы она создавала определённую фабрику и передавала её в клиентский код.
Фабричный метод
Фабричный метод — это порождающий паттерн проектирования, который определяет общий интерфейс для создания объектов в суперклассе, позволяя подклассам изменять тип создаваемых объектов.
В какой-то момент ваша программа становится настолько известной, что морские перевозчики выстраиваются в очередь и просят добавить поддержку морской логистики в программу.
Добавить новый класс не так-то просто, если весь код уже завязан на конкретные классы.
В итоге вы получите ужасающий код, наполненный условными операторами, которые выполняют то или иное действие, в зависимости от класса транспорта.
Подклассы могут изменять класс создаваемых объектов.
На первый взгляд, это может показаться бессмысленным: мы просто переместили вызов конструктора из одного конца программы в другой. Но теперь вы сможете переопределить фабричный метод в подклассе, чтобы изменить тип создаваемого продукта.
Чтобы эта система заработала, все возвращаемые объекты должны иметь общий интерфейс. Подклассы смогут производить объекты различных классов, следующих одному и тому же интерфейсу.
Все объекты-продукты должны иметь общий интерфейс.
Пока все продукты реализуют общий интерфейс, их объекты можно взаимозаменять в клиентском коде.
Продукт определяет общий интерфейс объектов, которые может произвести создатель и его подклассы.
Конкретные продукты содержат код различных продуктов. Продукты будут отличаться реализацией, но интерфейс у них будет общий.
Создатель объявляет фабричный метод, который должен возвращать новые объекты продуктов. Важно, чтобы тип результата совпадал с общим интерфейсом продуктов.
Зачастую фабричный метод объявляют абстрактным, чтобы заставить все подклассы реализовать его по-своему. Но он может возвращать и некий стандартный продукт.
Несмотря на название, важно понимать, что создание продуктов не является единственной функцией создателя. Обычно он содержит и другой полезный код работы с продуктом. Аналогия: большая софтверная компания может иметь центр подготовки программистов, но основная задача компании — создавать программные продукты, а не готовить программистов.
Конкретные создатели по-своему реализуют фабричный метод, производя те или иные конкретные продукты.
Фабричный метод не обязан всё время создавать новые объекты. Его можно переписать так, чтобы возвращать существующие объекты из какого-то хранилища или кэша.
В этом примере Фабричный метод помогает создавать кросс-платформенные элементы интерфейса, не привязывая основной код программы к конкретным классам элементов.
Пример кросс-платформенного диалога.
Фабричный метод объявлен в классе диалогов. Его подклассы относятся к различным операционным системам. Благодаря фабричному методу, вам не нужно переписывать логику диалогов под каждую систему. Подклассы могут наследовать почти весь код из базового диалога, изменяя типы кнопок и других элементов, из которых базовый код строит окна графического пользовательского интерфейса.
Базовый класс диалогов работает с кнопками через их общий программный интерфейс. Поэтому, какую вариацию кнопок ни вернул бы фабричный метод, диалог останется рабочим. Базовый класс не зависит от конкретных классов кнопок, оставляя подклассам решение о том, какой тип кнопок создавать.
Такой подход можно применить и для создания других элементов интерфейса. Хотя каждый новый тип элементов будет приближать вас к Абстрактной фабрике.
Когда заранее неизвестны типы и зависимости объектов, с которыми должен работать ваш код.
Фабричный метод отделяет код производства продуктов от остального кода, который эти продукты использует.
Благодаря этому, код производства можно расширять, не трогая основной. Так, чтобы добавить поддержку нового продукта, вам нужно создать новый подкласс и определить в нём фабричный метод, возвращая оттуда экземпляр нового продукта.
Когда вы хотите дать возможность пользователям расширять части вашего фреймворка или библиотеки.
Пользователи могут расширять классы вашего фреймворка через наследование. Но как сделать так, чтобы фреймворк создавал объекты из этих новых классов, а не из стандартных?
Решением будет дать пользователям возможность расширять не только желаемые компоненты, но и классы, которые создают эти компоненты. А для этого создающие классы должны иметь конкретные создающие методы, которые можно определить.
Когда вы хотите экономить системные ресурсы, повторно используя уже созданные объекты, вместо порождения новых.
Такая проблема обычно возникает при работе с тяжёлыми ресурсоёмкими объектами, такими, как подключение к базе данных, файловой системе и т. д.
Представьте, сколько действий вам нужно совершить, чтобы повторно использовать существующие объекты:
Весь этот код нужно куда-то поместить, чтобы не засорять клиентский код.
Самым удобным местом был бы конструктор объекта, ведь все эти проверки нужны только при создании объектов. Но, увы, конструктор всегда создаёт новые объекты, он не может вернуть существующий экземпляр.
Значит, нужен другой метод, который бы отдавал как существующие, так и новые объекты. Им и станет фабричный метод.
Приведите все создаваемые продукты к общему интерфейсу.
В классе, который производит продукты, создайте пустой фабричный метод. В качестве возвращаемого типа укажите общий интерфейс продукта.
Затем пройдитесь по коду класса и найдите все участки, создающие продукты. Поочерёдно замените эти участки вызовами фабричного метода, перенося в него код создания различных продуктов.
В фабричный метод, возможно, придётся добавить несколько параметров, контролирующих, какой из продуктов нужно создать.
На этом этапе фабричный метод, скорее всего, будет выглядеть удручающе. В нём будет жить большой условный оператор, выбирающий класс создаваемого продукта. Но не волнуйтесь, мы вот-вот исправим это.
Для каждого типа продуктов заведите подкласс и переопределите в нём фабричный метод. Переместите туда код создания соответствующего продукта из суперкласса.
Если создаваемых продуктов слишком много для существующих подклассов создателя, вы можете подумать о введении параметров в фабричный метод, которые позволят возвращать различные продукты в пределах одного подкласса.
Если после всех перемещений фабричный метод стал пустым, можете сделать его абстрактным. Если в нём что-то осталось — не беда, это будет его реализацией по умолчанию.
- Избавляет класс от привязки к конкретным классам продуктов. Выделяет код производства продуктов в одно место, упрощая поддержку кода. Упрощает добавление новых продуктов в программу. Реализует принцип открытости/закрытости.
- Может привести к созданию больших параллельных иерархий классов, так как для каждого класса продукта надо создать свой подкласс создателя.
Многие архитектуры начинаются с применения Фабричного метода (более простого и расширяемого через подклассы) и эволюционируют в сторону Абстрактной фабрики, Прототипа или Строителя (более гибких, но и более сложных).
Классы Абстрактной фабрики чаще всего реализуются с помощью Фабричного метода, хотя они могут быть построены и на основе Прототипа.
Фабричный метод можно использовать вместе с Итератором, чтобы подклассы коллекций могли создавать подходящие им итераторы.
Прототип не опирается на наследование, но ему нужна сложная операция инициализации. Фабричный метод, наоборот, построен на наследовании, но не требует сложной инициализации.
Фабричный метод можно рассматривать как частный случай Шаблонного метода. Кроме того, Фабричный метод нередко бывает частью большого класса с Шаблонными методами.
Не втыкай в транспорте
Лучше почитай нашу книгу о паттернах проектирования.
Теперь это удобно делать даже во время поездок в общественном транспорте.
Эта статья является частью нашей электронной книги Погружение в Паттерны Проектирования.