CodeLAB
на главную карта сайта обратная связь

Популярные задачи:

#Преобразование сумм из цифрового представления в строковое. (178654 hits)
#Поверхностное клонирование. (28541 hits)
#Перестановка фрагментов строки(или одномерного массива). (61926 hits)
#Вращение 3D объекта. (36944 hits)
#Летающие, крутящиеся шарики. (45693 hits)
#Рисование Фрактала (листьев папоротника). (54177 hits)
#Рисование прямоугольника. (32177 hits)
#Посчитать количество пар чисел (number of equal pairs). (7249 hits)
#Улучшение быстрой сортировки. (78705 hits)
#Вычисление эксцесса и коэффициентов асимметрии заданной выборки. (46890 hits)
#Двусторонняя карта. (34945 hits)
#Интерактивная, динамическая подгрузка картинок. (70874 hits)
#Пирамидальная сортировка. (207789 hits)
#Глубокое полное клонирование. (36814 hits)
#Сапер. (54554 hits)
#Таймер. (41651 hits)
#Предварительная загрузка изображений. (48173 hits)
#Код. (182673 hits)
#Счетчик времени с точностью до микросекунд. (130887 hits)
#Рисование множества Мандельброта. (45615 hits)


Главная >> Каталог задач >> Паттерны >> Порождающие >> Абстрактная фабрика (Abstract Factory)

Абстрактная фабрика (Abstract Factory)

Aвтор:
Дата:
Просмотров: 126388
реализации(java: 15шт...) +добавить

Имя

«Паттерн Abstract Factory»
Абстрактная фабрика – паттерн, порождающий объекты

Условия, Задача, Назначение

Предоставляет интерфейс для создания группы объектов, бизнес объектов участников всей системы, использующихся далее в рамках всего приложения. Смысл в том, что их конкретные классы не объявляются, позволяя, таким образом, легко заменять их в дальнейшем, как отдельные объекты так и все взаимосвязанное семейство.

Мотивация

Рассмотрим инструментальную программу для создания пользовательского интерфейса, поддерживающего разные стандарты внешнего облика, например Motif и Presentation Manager.

Внешний облик определяет визуальное представление и поведение элементов пользовательского интерфейса («виджетов») – полос прокрутки, окон и кнопок. Чтобы приложение можно было перенести на другой стандарт, в нем не должен быть жестко закодирован внешний облик виджетов. Если инстанцирование классов для конкретного внешнего облика разбросано по всему приложению, то изменить облик впоследствии будет нелегко.

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

Для каждого стандарта внешнего облика существует определенный подкласс WidgetFactory. Каждый такой подкласс реализует операции, необходимые для создания соответствующего стандарту виджета. Например, операция CreateScrollBar в классе MotifWidgetFactory инстанцирует и возвращает полосу прокрутки в стандарте Motif, тогда как соответствующая операция в классе PMWidgetFactory возвращает полосу прокрутки в стандарте Presentation Manager. Клиенты создают виджеты, пользуясь исключительно интерфейсом WidgetFactory, и им ничего не известно о классах, реализующих виджеты для конкретного стандарта. Другими словами, клиенты должны лишь придерживаться интерфейса, определенного абстрактным, а не конкретным классом.

Признаки применения, использования паттерна Абстрактная фабрика (Abstract Factory)

Если:
  1. Система не должна зависеть от того, как создаются, компонуются и представляются входящие объекты. Объекты, построенные определенным образом, просто как бы подаются на вход.
  2. Конкретный вариант требуемого поведения системы дают не отдельные объекты, а четко выраженное семейство связанных объектов. Объекты одного семейства должны использоваться вместе.
  3. Для функционирования системы необходимо одно из таким семейств взаимосвязанных объектов. Иными словами, на вход системы подается только целое семейство таких объектов, система конфигурируется одним из семейств.
  4. Соблюдаются некоторые условия конфиденциальности, когда не желательно предоставлять весь api библиотеки объектов, их реализацию, вместо этого – только их интерфейсы.

Решение

Участники паттерна Абстрактная фабрика (Abstract Factory)

  1. AbstractFactory (WidgetFactory) - абстрактная фабрика.
    Объявляет общий интерфейс для операций создания абстрактных объектов – продуктов. Далее в приложении вместо абстрактных будут создаваться уже конкретные объекты какого-либо одного семейство.
  2. ConcreteFactory[N] (Motif WidgetFactory, PMWidgetFactory) – конкретная фабрика(класс).
    Определяет, реализует операции, создающие все объекты одного конкретного семейства.
  3. AbstractProduct[A-Z] (Window, ScrollBar) – абстрактный продукт(класс).
    Определяет общий интерфейс для типа объекта-продукта. A-Z в данном случае – множество таких продуктов, составляющих абстрактное семейство.
  4. ConcreteProduct[A-Z][N] (Motif Window, Motif ScrollBar) - конкретный продукт(класс). Определяет конкретный объект-продукт типа [A-Z], создаваемый соответствующей конкретной фабрикой [N].
  5. Client – клиент.
    Другой программный модуль, фрагмент кода, дающий команды на получение конкретного семейства продуктов, пользуясь только известными интерфейсами классов AbstractFactory и AbstractProduct.

Схема использования паттерна Абстрактная фабрика (Abstract Factory)

Клиент(Client) создает единственный экземпляр ConcreteFactory, с помощью которого получает когда ему нужно все объекты соответствующего семейства. Поэтому для создания других объектов, выполняющих те же функции как-нибудь несколько иным способом, нужно лишь инстанцировать, создать другую ConcreteFactory.

Вопросы, касающиеся реализации паттерна Абстрактная фабрика (Abstract Factory)

  1. Абстрактная фабрика – синглтон.
    В общем случае приложению нужен лишь 1 экземпляр конкретной фабрики для каждого семейства продуктов, поэтому очень уместно применить здесь паттерн Одиночка(singleton)
  2. Фабричный метод для создания каждого продукта.
    Класс AbstractFactory объявляет только интерфейс для создания продуктов. Фактическое их создание - дело подклассов ConcreteProduct. Чаще всего для этой цели определяется фабричный метод для каждого продукта (см. паттерн фабричный метод). Конкретная фабрика специфицирует свои продукты путем замещения фабричного метода для каждого из них. Хотя такая реализация проста, она требует создавать новый подкласс конкретной фабрики для каждого семейства продуктов, даже если они почти ничем не отличаются.
    Если семейств продуктов может быть много, то конкретную фабрику удастся реализовать с помощью паттерна прототип. В этом случае она инициализируется экземпляром-прототипом каждого продукта в семействе и создает новый продукт путем клонирования этого прототипа. Подход на основе прототипов устраняет необходимость создавать новый класс конкретной фабрики для каждого нового семейства продуктов.
  3. Определение расширяемых фабрик.
    Класс AbstractFactory обычно определяет разные операции для каждого вида изготавливаемых продуктов. Виды продуктов кодируются в сигнатуре операции. Для добавления нового вида продуктов нужно изменить интерфейс класса AbstractFactory и всех зависящих от него классов.
    Более гибкий, но не такой безопасный способ - добавить параметр к операциям, создающим объекты. Данный параметр определяет вид создаваемого объекта. Это может быть идентификатор класса, целое число, строка или что-то еще, однозначно описывающее вид продукта. При таком подходе классу AbstractFactory нужна только одна операция Make с параметром, указывающим тип создаваемого объекта. (Данный прием применялся в обсуждавшихся выше абстрактных фабриках на основе прототипов и классов. Такой вариант проще использовать в динамически типизированных языках вроде Smalltalk, нежели в статически типизированных, каким является C++. Воспользоваться им в C++ можно только, если у всех объектов имеется общий абстрактный базовый класс или если объекты-продукты могут быть безопасно приведены к корректному типу клиентом, который их запросил. В разделе «Реализация» из описания паттерна фабричный метод показано, как реализовать такие параметризованные операции в C++. Но даже если приведение типов не нужно, остается принципиальная проблема: все продукты возвращаются клиенту одним и тем же абстрактным интерфейсом с уже определенным типом возвращаемого значения. Клиент не может ни различить классы продуктов, ни сделать какие-нибудь предположения о них. Если клиенту нужно выполнить операцию, зависящую отподкласса, то она будет недоступна через абстрактный интерфейс. Хотя клиент мог бы выполнить динамическое приведение типа (например, с помощью оператора dynamic_cas t в C++), это небезопасно и необязательно заканчивается успешно. Здесь мы имеем классический пример компромисса между высокой степенью гибкости и расширяемостью интерфейса)

Результаты

Достоинства и недостатки паттерна абстрактная фабрика. Достоинства:

  1. Скрываются реализации конкретных классов.
    Помогает контролировать классы объектов, создаваемых приложением. Т.е. вы остаетесь совершенно свободны от клиента в вопросе создания конкретных объектов – фабрика изолирует клиента от деталей реализации классов. Она также инкапсулирует ответственность за создание классов и сам процесс их создания. Клиенту известны лишь интерфейсы абстрактных классов, которыми через которые он только и может выполнять дальше свою бизнес логику с созданным семейством объектов. Классы отдаваемых вовне семейства объекты – известны лишь конкретной фабрике, поэтому дальше они ни в клиенте, ни в остальных частях приложения – не встречаются.
  2. Свободная замена семейства продуктов.
    Класс конкретной фабрики появляется в приложении только один раз: при инстанцировании. Это облегчает замену используемой приложением конкретной фабрики. Приложение может изменить конфигурацию продуктов, просто подставив новую конкретную фабрику. Поскольку абстрактная фабрика создает все семейство продуктов, то и заменяется сразу все семейство. В нашем примере пользовательского интерфейса перейти от виджетов Motif к виджетам Presentation Manager можно, просто переключившись на продукты соответствующей фабрики и заново создав интерфейс.
  3. Гарантия использования только одного вида продуктов
    Если продукты некоторого семейства спроектированы для совместного использования, то важно, чтобы приложение в каждый момент времени работало только с продуктами единственного семейства. Класс AbstractFactory позволяет легко соблюсти это ограничение;

Один из основных архитектурных недостатков – почти невозможно расширить семейство продуктов в дальнейшем. Можно сказать, что это также сложно, как и расширять интерфейс после более-менее продолжительного развития системы на его основе. Интерфейс AbstractFactory фиксирует набор продуктов, которые можно создать. Для поддержки новых продуктов необходимо расширить интерфейс фабрики, то есть изменить класс AbstractFactory и все его подклассы.

Пример

Применим паттерн Абстрактная фабрика к построению некоего парка автомобилей. Допустим нам нужно создавать определенное семейство автомобилей для конкретных целей. А именно нам нужны: легковые машины, грузовые и автобусы. Т.е. наша фабрика должна производить конкретные экземпляры данных видов. А дальше уже клиентская часть, используя эти возможности, будет создавать, наполнять этот парк и использовать данные автомобили по своему усмотрению в соответствии с интерфейсами их абстракных типов.
Класс FleetFactory может создавать разновидности автомобилей: легковые, грузовики, автобусы: FleetFactory.java
Это фабрика. Она будет использоваться клиентской частью/программой, которая знает как создавать парк, т.е. в каких количествах наполнять его различными видами транспортных средств(например, она считывает этот план автопарка из конфигурационного файла). Этой программе на вход в качестве аргумента передается параметр типа фабрика, т.е. типа FleetFactory.
Далее нужны соответственно абстрактные классы продуктов (легковая машина, грузовая, автобус): Car.java, Truck.java, Bus.java
Допустим, нужны 2 семейства автомобилей: русские марки и американские. Соответственно, реализуем 2 абстрактные фабрики и 2 ряда продуктов-автомобилей.
Русская фабрика автомобилей: RusFleetFactory.java, Kamaz.java, Liaz.java, Vaz2110.java
Американская фабрика автомобилей: AmericanFleetFactory.java, Dodge.java, CityJet.java, GeneralMotorsTruck.java
И теперь – непосредственно программа-клиент. Создадим общий файл настроек, план парка, в котором будет определяться его наполнение различными видами машин, а также признак конкретной фабрики, с помощью которой он будет создаваться:

factory=ru
carNum=10
truckNum=2
busNum=4

Следует отметить, что тот факт, что вид используемой фабрики задается в конфигурационном файле приложения, вместо того, чтобы быть жестко зашитым в его код – играет важную роль, т.к. для изменения поведения программы и используемых в ней конкретных продуктов – нужно лишь отредактировать текстовый файл. В противном случае пришлось бы перекомпилировать саму программу клиента.
Программа-клиент: FleetRun.java
Таким образом, используемый паттерн абстрактная фабрика позволяет нам в данном конкретном случае – легко и быстро менять семейство марок машин нашего парка и их количество, иными словами – прозрачно обновлять наш автопарк любыми другими машинами, не изменяя ни одной строчки в программных файлах приложения:

factory=us
carNum=15
truckNum=4
busNum=2

Отметим, что RusFleetFactory или AmericanFleetFactory – по сути, набор фабричных методов. Это самый распространенный способ реализации паттерна абстрактная фабрика.
Еще заметим, что RusFleetFactory/AmericanFleetFactory - не абстрактные классы, то есть они работает и как AbstractFactory, и как ConcreteFactory. Это еще одна типичная реализация для простых применений паттерна абстрактная фабрика. Поскольку RusFleetFactory - конкретный класс, состоящий только из фабричных методов – легко получить новую фабрику FleetFactory, породив подкласс и заместив в нем только необходимые операции, т.е. не все – а лишь какие-либо необходимые!
Например, нужны русские легковые и грузовые машины, но вот автобусы – американские, без проблем: MixedFleetFactory.java
 

Известные применения паттерна Абстрактная фабрика (Abstract Factory)


Родственные паттерны

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

Реализации:

java(15)   +добавить

1) Car.java на java, code #400[автор:this]
2) Truck.java на java, code #401[автор:this]
3) Bus.java на java, code #402[автор:this]
4) Kamaz.java на java, code #404[автор:this]
5) Liaz.java на java, code #405[автор:this]
6) Vaz2110.java на java, code #406[автор:this]
7) RusFleetFactory.java на java, code #410[автор:this]
8) CityJet.java на java, code #407[автор:this]
9) Dodge.java на java, code #408[автор:this]
10) GeneralMotorsTruck.java на java, code #409[автор:this]
11) AmericanFleetFactory.java на java, code #411[автор:this]
12) FleetRun.java на java, code #403[автор:this]
13) MixedFleetFactory.java на java, code #412[автор:this]
14) FleetFactory.java на java, code #413[автор:this]
15) Fleet на java, code #613[аноним:XoLmC]