необходимо предоставить библиотеку объектов, раскрывая только их интерфейсы, но не реализацию.
Структура паттерна Абстрактная фабрика
По сути речь идет о формировании независимых семейств объектов непосредственно не связанных между собой по использованию. Однако наличие общего предка для каждого из продукта показывает, что возможно совместное использование в качестве альтернатив специализаций, порождаемых от одного продукта.
Переводя это на "язык животных", можно говорить о том что независимо от того, к какому из семейств относятся утки, они все могут принадлежать к одной общей категории, и в дальнейшем обрабатываться полиморфно, если будут совместно использоваться в одной программе. Это же касается собак и других возможных животных. Понятно, что в целом абстрактная фабрика подразумевает раздельное применение альтернативных семейств, но чем черт не шутит...
Объектно-ориентированная реализация Абстрактной фабрики
Учитывая специфику данного паттерна, не будем объединять в альтернативы одного обобщения различных животных. То есть, утки отдельно, собаки отдельно. В качестве семейств при этом сформируем домашних (Domestic
) животных и животных-игрушек (Toy
). Исходя из этого появляются некоторые абстрактные утки и собаки:
// Абстрактная утка, от которой наследуюют утки разных семейств.
class Duck {
public:
Duck() {std::cout << "Duck created! ";}
};
// Абстрактная собака, от которой наследуют собаки разных семейств
class Dog {
public:
Dog() {std::cout << "Dog created! ";}
};
Они используются для формирования конкретных уток и собак различных семейств: домашних животных и игрушек.
//-----------------------------------------------
// Семейство домашних животных
//-----------------------------------------------
// Домашняя утка
class DomesticDuck: public Duck {
public:
DomesticDuck(): Duck() {
std::cout << "It is Domestic duck.\n";
}
};
// Домашняя собака
class DomesticDog: public Dog {
public:
DomesticDog(): Dog() {
std::cout << "It is Domestic dog.\n";
}
};
//-----------------------------------------------
// Семейство животных - игрушек
//-----------------------------------------------
// Игрушечная утка
class ToyDuck:public Duck {
public:
ToyDuck(): Duck() {
std::cout << "It is Toy duck.\n";
}
};
// Игрушечная собака
class ToyDog:public Dog {
public:
ToyDog(): Dog() {
std::cout << "It is Toy dog.\n";
}
};
Для создания экземпляров различных семейств используются конкретные фабрики животных, порождаемые об общего родителя, являющегося абстрактной фабрикой:
// Абстрактная фабрика, используемая для порождения фабрик различных семейств.
class AbstractFactory {
public:
virtual Duck *CreateDuck() const = 0;
virtual Dog *CreateDog() const = 0;
};
// Фабрика домашних животных
class DomesticFactory : public AbstractFactory {
public:
virtual Duck *CreateDuck() const override {
return new DomesticDuck();
}
virtual Dog *CreateDog() const override {
return new DomesticDog();
}
};
// Фабрика животных - игрушек
class ToyFactory : public AbstractFactory {
public:
virtual Duck *CreateDuck() const override {
return new ToyDuck();
}
virtual Dog *CreateDog() const override {
return new ToyDog();
}
};
Использование сформированных программных объектов обеспечивает полиморфное и независимое формирование различных семейств альтернативных животных.
Абстрактная фабрика как отношения между данными и функциями
Если сравнить Абстрактную фабрику и Фабричный метод, то можно увидеть много общего. В частности альтернативы Фабричного метода напоминают альтернативы семейств. Отличие проявляется в том, что интерфейсные классы ОО программы, используемые в качестве создателей отличаются количеством методов порождающих конкретные объекты. Это говорит об увеличении числа обобщений, с каждым из которых связан одинаковый по количеству набор альтернатив. Отношение между функциями и данными, определяющие соответствующие отношения для животных приведены на рисунке.
Графическое представление ПП аналога Абстрактной фабрики
Процедурно-параметрическая имитация Абстрактной фабрики
Если опираться на приведенную схему, то ПП парадигма позволяет сформировать как абстрактные фабрики, так и семейства продуктов с использованием обобщений и специализаций. При этом по своей сути абстрактные фабрики, как и создатели, реализуемые в ПП аналоге паттерна Фабричный метод, могут состоять из специализаций, реализованных в как эволюционно расширяемый перечислимый тип. Каждое вид животных содержит свой набор специализаций, эквивалентный набору других семейств. Обобщения выступают в роли клея для альтернатив по одним и тем же животным. Создатели являются внешними параметрическими функциями, ориентированными на создание конкретных животных, принадлежащих к конкретным категориям. Отдельные категории задаются обобщающими аргументами, определяющими используемую фабрику.
В рассматриваемом примере каждое из семейств может иметь свои варианты животных (домашних и игрушечных), которые в дальнейшем могут использоваться в качестве основ специализаций для обобщений видов.
//-------------------------------------------------------
// Семейство домашних животных
// Домашняя утка
typedef struct DomesticDuck {} DomesticDuck;
void DomesticDuckMessage() {printf("It is Domestic duck.\n");}
// Домашняя собака
typedef struct DomesticDog {} DomesticDog;
void DomesticDogMessage() {printf("It is Domestic dog.\n");}
//-------------------------------------------------
// Семейство животных - игрушек
// Игрушечная утка
typedef struct ToyDuck {} ToyDuck;
void ToyDuckMessage() {printf("It is Toy duck.\n");}
// Игрушечная собака
typedef struct ToyDog {} ToyDog;
void ToyDogMessage() {printf("It is Toy dog.\n");}
Обобщения могут использовать эти основы, образуя соответствующие специализации:
// Абстрактная утка, обобщающая разные семейства.
typedef struct Duck {}<> Duck;
void DuckMessage() {printf("Duck created! ");}
// Специализации уток
Duck + <DomesticDuck;>;
Duck + <ToyDuck;>;
// Абстрактная собака, обобщающая разные семейства
typedef struct Dog {}<> Dog;
void DogMessage() {printf("Dog created! ");}
// Специализации собак
Dog + <DomesticDog;>;
Dog + <ToyDog;>;
Абстрактная фабрика определяется через обобщение, имитирующее эволюционно расширяемыей перечислимый тип. Для каждой из сформированных специализаций создается обработчик, осуществляющий создание различных животных реализованных к текущему моменту времени.
// Обобщающая функция, определяющая создание уток.
Duck* CreateDuck<AbstractFactory* f>() {return NULL;} //= 0;
//Обработчик, обеспечивающий создание домашней утки.
Duck* CreateDuck<AbstractFactory.ForDomestic* f>() {
struct Duck.DomesticDuck* d = create_spec(Duck.DomesticDuck);
DuckMessage();
DomesticDuckMessage();
return (Duck*)d;
}
//Обработчик, обеспечивающий создание игрушечной утки.
Duck* CreateDuck<AbstractFactory.ForToy* f>() {
struct Duck.ToyDuck* d = create_spec(Duck.ToyDuck);
DuckMessage();
ToyDuckMessage();
return (Duck*)d;
}
// Обобщающая функция, определяющая создание собак.
Dog* CreateDog<AbstractFactory* f>() {return NULL;} //= 0;
//Обработчик, обеспечивающий создание домашней собаки.
Dog* CreateDog<AbstractFactory.ForDomestic* f>() {
struct Dog.DomesticDog* d = create_spec(Dog.DomesticDog);
DogMessage();
DomesticDogMessage();
return (Dog*)d;
}
//Обработчик, обеспечивающий создание игрушечной собаки.
Dog* CreateDog<AbstractFactory.ForToy* f>() {
struct Dog.ToyDog* d = create_spec(Dog.ToyDog);
DogMessage();
ToyDogMessage();
return (Dog*)d;
}
Абстрактная фабрика и ППП
Моделирование абстрактной фабрики с использованием ПП подхода позволяет практически полностью повторить ОО решение. Помимо этого можно отметить более гибкие возможности, связанные с расширением номенклатуры продуктов. В частности, при добавлении нового продукта в объектно--ориентированной программе необходимо изменить интерфейсы базовой и производных Абстрактных фабрик. Предлагаемый GoF [gof-patternsГамма Э., Хелм Р., Джонсон Р., Влиссидес Дж. Паттерны объектно-ориентированного проектирования. - СПб.: Питер, 2020. - 448 с.] вариант, связанный с передачей параметра, идентифицирующего добавляемую фабрику, вряд ли можно считать надежным. Использование ПП подхода позволяет безболезненно изменять как количество фабрик, добавляя новый эволюционно расширяемый перечислимый тип, так и добавлять под этот тип новый обработчик специализации.
Содержание