以"咖啡店多地区套餐系统"为例,从工厂方法出发,讲解为什么需要抽象工厂模式以及如何实现。
一、从工厂方法到抽象工厂
工厂方法的局限
在工厂方法模式中,每个地区工厂只创建一种产品(咖啡):
CoffeeAbstractFactory*factory=newChinaCoffeeFactory();Coffee*latte=factory->createCoffee("latte");// 只能创建咖啡现在需求又变了——咖啡店不仅卖咖啡,还卖甜点,而且不同地区的套餐内容不同:
| 地区 | 咖啡 | 甜点 |
|---|---|---|
| 中国区 | 拿铁 30元 | 蛋黄酥 15元 |
| 美国区 | 拿铁 $5 | 提拉米苏 $6 |
如果用工厂方法,就需要两套独立的工厂(咖啡工厂 + 甜点工厂),彼此之间没有关联,无法保证"中国区工厂一定搭配中国区甜点"。
抽象工厂的解决思路
一个工厂同时创建一组相关产品。每个地区工厂既能创建咖啡,又能创建甜点,保证产品之间的搭配关系。
工厂方法:一个工厂 → 一种产品 抽象工厂:一个工厂 → 一组相关产品(咖啡 + 甜点)二、类图
Coffee (抽象产品A) Dessert (抽象产品B) | | ChinaLatte USALatte EggYolkPastry Tiramisu \ / \ / \ / \ / AbstractFactory (抽象工厂) ├── createCoffee() └── createDessert() / \ ChinaFactory USAFactory 创建: 创建: ChinaLatte USALatte EggYolkPastry Tiramisu核心区别:抽象工厂定义了两个创建方法(createCoffee+createDessert),而工厂方法只有一个。
三、项目结构
include/ ├── Coffee.h # 抽象产品A:咖啡基类 ├── ChinaLatte.h # 中国区拿铁(复用工厂方法的类) ├── USALatte.h # 美国区拿铁(复用工厂方法的类) ├── Dessert.h # 抽象产品B:甜点基类(新增) ├── EggYolkPastry.h # 蛋黄酥(新增) ├── Tiramisu.h # 提拉米苏(新增) ├── AbstractFactory.h # 抽象工厂基类(新增) ├── ChinaFactory.h # 中国区工厂(新增) └── USAFactory.h # 美国区工厂(新增) src/ ├── main.cpp ├── ChinaLatte.cpp # 复用 ├── USALatte.cpp # 复用 ├── EggYolkPastry.cpp # 新增 ├── Tiramisu.cpp # 新增 ├── ChinaFactory.cpp # 新增 └── USAFactory.cpp # 新增四、逐步实现
第 1 步:定义第二种抽象产品 —Dessert.h
咖啡基类Coffee已经有了,现在新增甜点基类,结构完全对称:
// include/Dessert.h#ifndefDESSERT_H#defineDESSERT_H#include<string>usingnamespacestd;classDessert{public:virtualstringgetName()=0;virtualdoublegetPrice()=0;virtual~Dessert(){}};#endif踩坑记录:文件名拼写错误(
Deesert.h多了个 e),但#include写的是"Dessert.h",编译时报找不到文件。文件名一定要和 include 里的一致。
第 2 步:实现具体甜点
EggYolkPastry.h— 蛋黄酥(声明)
#ifndefEGG_YOLK_PASTRY_H#defineEGG_YOLK_PASTRY_H#include"Dessert.h"classEggYolkPastry:publicDessert{public:stringgetName()override;doublegetPrice()override;};#endifEggYolkPastry.cpp— 蛋黄酥(实现)
#include"EggYolkPastry.h"stringEggYolkPastry::getName(){return"EggYolkPastry";}doubleEggYolkPastry::getPrice(){return15;// 中国区甜点:15元}Tiramisu.h— 提拉米苏(声明)
#ifndefTIRAMISU_H#defineTIRAMISU_H#include"Dessert.h"classTiramisu:publicDessert{public:stringgetName()override;doublegetPrice()override;};#endifTiramisu.cpp— 提拉米苏(实现)
#include"Tiramisu.h"stringTiramisu::getName(){return"Tiramisu";}doubleTiramisu::getPrice(){return6;// 美国区甜点:$6}第 3 步:定义抽象工厂基类 —AbstractFactory.h
这是抽象工厂模式的核心——工厂接口中定义多个创建方法:
// include/AbstractFactory.h#ifndefABSTRACT_FACTORY_H#defineABSTRACT_FACTORY_H#include"Coffee.h"#include"Dessert.h"classAbstractFactory{public:virtualCoffee*createCoffee(string type)=0;// 创建咖啡virtualDessert*createDessert(string type)=0;// 创建甜点virtual~AbstractFactory(){}};#endif对比工厂方法:
工厂方法的CoffeeAbstractFactory | 抽象工厂的AbstractFactory |
|---|---|
createCoffee()一个方法 | createCoffee()+createDessert()两个方法 |
| 只能创建咖啡 | 同时创建咖啡和甜点 |
第 4 步:实现具体工厂
ChinaFactory.h— 中国区工厂(声明)
#ifndefCHINA_FACTORY_H#defineCHINA_FACTORY_H#include"AbstractFactory.h"classChinaFactory:publicAbstractFactory{public:Coffee*createCoffee(string type)override;Dessert*createDessert(string type)override;};#endifChinaFactory.cpp— 中国区工厂(实现)
#include"ChinaFactory.h"#include"ChinaLatte.h"#include"EggYolkPastry.h"Coffee*ChinaFactory::createCoffee(string type){if(type=="latte")returnnewChinaLatte();returnnullptr;}Dessert*ChinaFactory::createDessert(string type){if(type=="eggyolkpastry")returnnewEggYolkPastry();returnnullptr;}USAFactory.h— 美国区工厂(声明)
#ifndefUSA_FACTORY_H#defineUSA_FACTORY_H#include"AbstractFactory.h"classUSAFactory:publicAbstractFactory{public:Coffee*createCoffee(string type)override;Dessert*createDessert(string type)override;};#endifUSAFactory.cpp— 美国区工厂(实现)
#include"USAFactory.h"#include"USALatte.h"#include"Tiramisu.h"Coffee*USAFactory::createCoffee(string type){if(type=="latte")returnnewUSALatte();returnnullptr;}Dessert*USAFactory::createDessert(string type){if(type=="tiramisu")returnnewTiramisu();returnnullptr;}第 5 步:客户端使用
// src/main.cpp#include<iostream>#include<cstdlib>#include"ChinaFactory.h"#include"USAFactory.h"usingnamespacestd;intmain(){// 中国区工厂 → 同时创建咖啡和甜点AbstractFactory*chinaFactory=newChinaFactory();Coffee*chinaCoffee=chinaFactory->createCoffee("latte");Dessert*chinaDessert=chinaFactory->createDessert("eggyolkpastry");cout<<"=== China Combo ==="<<endl;cout<<"Coffee: "<<chinaCoffee->getName()<<" - "<<chinaCoffee->getPrice()<<"yuan"<<endl;cout<<"Dessert: "<<chinaDessert->getName()<<" - "<<chinaDessert->getPrice()<<"yuan"<<endl;cout<<"Total: "<<chinaCoffee->getPrice()+chinaDessert->getPrice()<<"yuan"<<endl;// 美国区工厂 → 同时创建咖啡和甜点AbstractFactory*usaFactory=newUSAFactory();Coffee*usaCoffee=usaFactory->createCoffee("latte");Dessert*usaDessert=usaFactory->createDessert("tiramisu");cout<<"=== USA Combo ==="<<endl;cout<<"Coffee: "<<usaCoffee->getName()<<" - $"<<usaCoffee->getPrice()<<endl;cout<<"Dessert: "<<usaDessert->getName()<<" - $"<<usaDessert->getPrice()<<endl;cout<<"Total: $"<<usaCoffee->getPrice()+usaDessert->getPrice()<<endl;deletechinaCoffee;deletechinaDessert;deletechinaFactory;deleteusaCoffee;deleteusaDessert;deleteusaFactory;system("pause");return0;}运行结果:
=== China Combo === Coffee: China Latte - 30yuan Dessert: EggYolkPastry - 15yuan Total: 45yuan === USA Combo === Coffee: USA Latte - $5 Dessert: Tiramisu - $6 Total: $11五、三种工厂模式完整对比
| 简单工厂 | 工厂方法 | 抽象工厂 | |
|---|---|---|---|
| 工厂数量 | 1 个 | 每个变体 1 个 | 每个变体 1 个 |
| 产品种类 | 1 种 | 1 种 | 多种(一组) |
| 创建方式 | static+ if/else | 继承 + 多态 | 继承 + 多态 |
| 新增产品变体 | 改工厂 if/else | 加工厂子类 | 加工厂子类 |
| 新增产品种类 | — | — | 改抽象工厂接口(所有子工厂都要改) |
| 核心价值 | 集中创建逻辑 | 对扩展开放 | 保证产品族搭配一致 |
什么时候用哪个?
产品少、不怎么变 → 简单工厂 产品有多种变体、经常扩展 → 工厂方法 多种产品需要配套使用 → 抽象工厂六、抽象工厂的优缺点
优点
- 保证产品族一致性:中国区工厂一定创建中国区咖啡 + 中国区甜点,不会出现混搭
- 新增地区方便:加一个
JapanFactory,不改现有代码 - 客户端只依赖抽象接口:
AbstractFactory*、Coffee*、Dessert*
缺点
- 新增产品种类困难:如果要加"小食(Snack)",需要改
AbstractFactory接口加createSnack(),所有已有工厂子类都要跟着改——违反开闭原则 - 类的数量多:每个地区 × 每种产品 = 大量类文件
取舍原则
- 如果**经常新增地区(产品族变体)**→ 用抽象工厂,因为只需加工厂子类
- 如果经常新增产品种类→ 不适合抽象工厂,考虑其他模式
七、角色总结
| 角色 | 本例中 | 职责 |
|---|---|---|
| 抽象产品 A | Coffee | 咖啡的公共接口 |
| 抽象产品 B | Dessert | 甜点的公共接口 |
| 具体产品 | ChinaLatte,EggYolkPastry,USALatte,Tiramisu | 各地区的具体产品 |
| 抽象工厂 | AbstractFactory | 定义创建咖啡+甜点的接口 |
| 具体工厂 | ChinaFactory,USAFactory | 各地区工厂,创建本地区的产品组合 |
| 客户端 | main() | 选择工厂,获取配套产品 |
八、学习路径回顾
简单工厂 → 工厂方法 → 抽象工厂 | | | 1个工厂 N个工厂 N个工厂 1种产品 1种产品 多种产品 if/else 多态 多态 集中管理 易扩展变体 保证搭配一致三种模式是递进关系,每一种都是为了解决前一种的局限而出现的。理解了这个演进过程,就能在实际项目中根据需求选择合适的模式。