06. 抽象工厂模式
一、抽象工厂模式
工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问题。但由于工厂方法模式中的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,势必会增加系统的开销。此时,可以考虑将一些相关的产品组成一个 “产品族”,由同一个工厂来统一生产,这就是本章将要学习的抽象工厂模式的基本思想。
为了更好地理解抽象工厂模式,这里先引入如下两个概念:
- 产品等级结构。产品等级结构即 产品的继承结构,例如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL 电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。
- 产品族。在抽象工厂模式中,产品族是指由 同一个工厂生产的,位于不同产品等级结构中的一组产品。例如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中,海尔电视机、海尔电冰箱构成了一个产品族。
抽象工厂模式(Abstract Factory Pattern)是一种创建型设计模式,它提供了一种方式来封装一系列具有共同主题的单个工厂,而不需要指定它们的具体类。抽象工厂模式主要针对的是一系列产品的创建,通过提供一个抽象工厂接口来声明创建产品的方法,然后具体工厂实现该接口,实现具体产品的创建。
在抽象工厂模式中,抽象工厂定义了一系列产品的生产方法,而具体的工厂则负责实现这些方法来生产具体的产品。客户端通过抽象工厂接口来与工厂交互,无需关心具体产品的创建细节。这样,客户端就可以从具体的产品类解耦出来,只需要知道相应的抽象工厂和产品族,就可以通过统一的接口来使用这些产品。
抽象工厂模式的主要角色:
- 抽象工厂(Factory):它声明了一组用于创建一族产品的方法,每个方法对应一种产品。
- 具体工厂(Concrete Factory):它实现了抽象共产中声明的创建产品的方法,生成一组具体产品,这些产品构成了一个产品族,每种产品都位于某个产品等级结构中。
- 抽象产品(Product):定义了产品的规范,描述了产品的主要特征和功能,抽象工厂模式中有多个抽象产品。
- 具体产品(Concrete Product):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂是多对一关系。
如果工厂生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构、属于不同类型的具体产品时,就可以使用抽象工厂模式。抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建。
二、C++实现抽象工厂模式
将所有产品类公共的代码移至抽象产品类,并在抽象产品类中声明一些抽象方法,以供不同的具体产品类来实现。
// 抽象披萨类
class Pizza
{
private:
std::string name;
public:
Pizza(void);
Pizza(std::string name);
virtual void prepare(void) = 0; // 准备原材料,这里不同披萨准备原材料不同
void bake(void); // 烘烤,这里不同披萨烘烤方式相同
void cut(void); // 切块,这里不同披萨切块方式相同
void box(void); // 装盒,这里不同披萨装盒方式相同
std::string getName(void);
void setName(std::string name);
};
Pizza::Pizza(void) {}
Pizza::Pizza(std::string name) : name(name){}
void Pizza::bake(void)
{
std::cout << name + "烘烤" << std::endl;
}
void Pizza::cut(void)
{
std::cout << name + "切块" << std::endl;
}
void Pizza::box(void)
{
std::cout << name + "装盒" << std::endl;
}
std::string Pizza::getName(void)
{
return name;
}
void Pizza::setName(std::string name)
{
this->name = name;
}
在具体产品类中实现了抽象产品类中声明的抽象业务方法,不同的具体产品类可以提供不同的实现。
// 奶酪披萨
class CheesePizza : public Pizza
{
public:
using Pizza::Pizza;
void prepare(void) override;
};
void CheesePizza::prepare(void)
{
std::cout << "准备" + getName() + "的原材料" << std::endl;
std::cout << "准备奶酪" << std::endl;
}
// 水果披萨
class FruitPizza : public Pizza
{
public:
using Pizza::Pizza;
void prepare(void) override;
};
void FruitPizza::prepare(void)
{
std::cout << "准备" + getName() + "的原材料" << std::endl;
std::cout << "准备水果" << std::endl;
}
抽象甜品产品:
// 抽象甜品类
class Dessert
{
private:
std::string name;
public:
Dessert(void);
Dessert(std::string name);
virtual void complimentaryDessert(void) = 0; // 赠送甜品
std::string getName(void);
void setName(std::string name);
};
Dessert::Dessert(void) {}
Dessert::Dessert(std::string name) :name(name){}
std::string Dessert::getName(void)
{
return name;
}
void Dessert::setName(std::string name)
{
this->name = name;
}
具体甜品产品:
// 奶酪甜品
class CheeseDessert : public Dessert
{
public:
using Dessert::Dessert;
void complimentaryDessert(void) override;
};
void CheeseDessert::complimentaryDessert(void)
{
std::cout << "赠送" + getName() << std::endl;
}
// 水果甜品
class FruitDessert : public Dessert
{
public:
using Dessert::Dessert;
void complimentaryDessert(void) override;
};
void FruitDessert::complimentaryDessert(void)
{
std::cout << "赠送" + getName() << std::endl;
}
在抽象工厂中声明了工厂方法但并未实现工厂方法,具体产品对象的创建由其子类负责。客户端针对抽象工厂编程,可在运行时再指定具体工厂类。
// 披萨工厂,根据顾客的订单,来生产不同的披萨
class PizzaFactory
{
public:
virtual Pizza * ceratePizza(void) = 0; // 生产披萨
virtual Dessert * cerateDessert(void) = 0; // 生产甜品
};
具体工厂类实现了抽象工厂,每个具体的工厂方法可以返回一个特定的产品对象,而同一个具体工厂所创建的产品对象构成了一个产品族。
// 具体的奶酪披萨工厂
class CheesePizzaFactory : public PizzaFactory
{
public:
Pizza * ceratePizza(void) override;
Dessert * cerateDessert(void) override;
};
Pizza * CheesePizzaFactory::ceratePizza(void)
{
return new CheesePizza("奶酪披萨");
}
Dessert * CheesePizzaFactory::cerateDessert(void)
{
return new CheeseDessert("奶酪甜品");
}
// 具体的水果披萨工厂
class FruitPizzaFactory : public PizzaFactory
{
public:
Pizza * ceratePizza(void) override;
Dessert * cerateDessert(void) override;
};
Pizza * FruitPizzaFactory::ceratePizza(void)
{
return new FruitPizza("水果披萨");
}
Dessert * FruitPizzaFactory::cerateDessert(void)
{
return new FruitDessert("水果甜品");
}
在客户端代码中,只需关心工厂类即可。不同的具体工厂可以创建不同的产品。
// 披萨店,根据顾客的订单,来选择不同的披萨
class PizzaStore
{
private:
PizzaFactory * pizzaFactory;
public:
Pizza * order(void); // 顾客订购披萨
void setPizzaFactory(PizzaFactory * pizzaFactory);
};
void PizzaStore::setPizzaFactory(PizzaFactory * pizzaFactory)
{
this->pizzaFactory = pizzaFactory;
}
Pizza * PizzaStore::order(void)
{
Pizza * pizza = pizzaFactory->ceratePizza(); // 根据顾客的订单,来选择不同的披萨
pizza->prepare();
pizza->bake();
pizza->cut();
pizza->box();
Dessert * dessert = pizzaFactory->cerateDessert(); // 根据顾客的订单,选择赠送的不同的甜品
dessert->complimentaryDessert();
delete dessert;
return pizza;
}
main() 函数:
#include <iostream>
int main(void)
{
PizzaStore pizzaStore;
// 顾客订购奶酪披萨
PizzaFactory * pizzaCheessFactory = new CheesePizzaFactory();
pizzaStore.setPizzaFactory(pizzaCheessFactory);
Pizza * cheesePizza = pizzaStore.order();
std::cout << std::endl;
// 顾客订购水果披萨
FruitPizzaFactory * pizzaFruitFactory = new FruitPizzaFactory();
pizzaStore.setPizzaFactory(pizzaFruitFactory);
Pizza * fruitPizza = pizzaStore.order();
delete pizzaCheessFactory;
delete pizzaFruitFactory;
delete cheesePizza;
delete fruitPizza;
return 0;
}
三、抽象工厂模式的总结
在抽象工厂模式中,增加新的产品族很方便,但是增加新的产品等级结构很麻烦,抽象工厂模式的这种性质称为开闭原则的倾斜性。
- 增加产品族。对于增加新的产品族,抽象工厂模式很好地支持了开闭原则,只需要增加具体产品并对应增加一个新的具体工厂,对已有代码无须做任何修改。
- 增加新的产品等级结构。对于增加新的产品等级结构,需要修改所有的工厂角色,包括抽象工厂类,在所有的工厂类中都需要增加生产新产品的方法,违背了开闭原则。
3.1、抽象工厂模式的优点
- 抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易,所有的具体工厂都实现了在抽象工厂中声明的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。
- 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。
- 增加新的产品族很方便,无须修改已有系统,符合开闭原则。
3.2、抽象工厂模式的缺点
抽象工厂模式的主要缺点是:增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不便,违背了开闭原则。
3.3、抽象工厂模式的适用场景
- 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是很重要的,用户无须关心对象的创建过程,将对象的创建和使用解耦。
- 系统中有多于一个的产品族,而每次只使用其中某一个产品族。可以通过配置文件等方式来使得用户可以动态改变产品族,也可以很方便地增加新的产品族。
- 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。同一个产品族中的产品可以是没有任何关系的对象,但是它们都具有一些共同的约束。
- 产品等级结构稳定,设计完成之后,不会向系统中增加新的产品等级结构或者删除已有的产品等级结构。