C++ 抽象工厂模式实战指南

以"咖啡店多地区套餐系统"为例,从工厂方法出发,讲解为什么需要抽象工厂模式以及如何实现。


一、从工厂方法到抽象工厂

工厂方法的局限

在工厂方法模式中,每个地区工厂只创建一种产品(咖啡):

cpp 复制代码
CoffeeAbstractFactory* factory = new ChinaCoffeeFactory();
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 已经有了,现在新增甜点基类,结构完全对称:

cpp 复制代码
// include/Dessert.h
#ifndef DESSERT_H
#define DESSERT_H
#include<string>
using namespace std;

class Dessert
{
    public:
    virtual string getName()=0;
    virtual double getPrice()=0;
    virtual ~Dessert(){}
};

#endif

踩坑记录 :文件名拼写错误(Deesert.h 多了个 e),但 #include 写的是 "Dessert.h",编译时报找不到文件。文件名一定要和 include 里的一致。

第 2 步:实现具体甜点

EggYolkPastry.h --- 蛋黄酥(声明)
cpp 复制代码
#ifndef EGG_YOLK_PASTRY_H
#define EGG_YOLK_PASTRY_H
#include"Dessert.h"

class EggYolkPastry:public Dessert{
    public:
    string getName() override;
    double getPrice() override;
};

#endif
EggYolkPastry.cpp --- 蛋黄酥(实现)
cpp 复制代码
#include"EggYolkPastry.h"

string EggYolkPastry::getName(){
    return "EggYolkPastry";
}

double EggYolkPastry::getPrice(){
    return 15;    // 中国区甜点:15元
}
Tiramisu.h --- 提拉米苏(声明)
cpp 复制代码
#ifndef TIRAMISU_H
#define TIRAMISU_H
#include"Dessert.h"

class Tiramisu:public Dessert{
    public:
    string getName() override;
    double getPrice() override;
};

#endif
Tiramisu.cpp --- 提拉米苏(实现)
cpp 复制代码
#include"Tiramisu.h"

string Tiramisu::getName(){
    return "Tiramisu";
}

double Tiramisu::getPrice(){
    return 6;     // 美国区甜点:$6
}

第 3 步:定义抽象工厂基类 --- AbstractFactory.h

这是抽象工厂模式的核心------工厂接口中定义多个创建方法:

cpp 复制代码
// include/AbstractFactory.h
#ifndef ABSTRACT_FACTORY_H
#define ABSTRACT_FACTORY_H
#include"Coffee.h"
#include"Dessert.h"

class AbstractFactory{
    public:
    virtual Coffee* createCoffee(string type)=0;    // 创建咖啡
    virtual Dessert* createDessert(string type)=0;   // 创建甜点
    virtual ~AbstractFactory(){}
};

#endif

对比工厂方法

工厂方法的 CoffeeAbstractFactory 抽象工厂的 AbstractFactory
createCoffee() 一个方法 createCoffee() + createDessert() 两个方法
只能创建咖啡 同时创建咖啡和甜点

第 4 步:实现具体工厂

ChinaFactory.h --- 中国区工厂(声明)
cpp 复制代码
#ifndef CHINA_FACTORY_H
#define CHINA_FACTORY_H
#include"AbstractFactory.h"

class ChinaFactory:public AbstractFactory{
    public:
    Coffee* createCoffee(string type) override;
    Dessert* createDessert(string type) override;
};

#endif
ChinaFactory.cpp --- 中国区工厂(实现)
cpp 复制代码
#include"ChinaFactory.h"
#include"ChinaLatte.h"
#include"EggYolkPastry.h"

Coffee* ChinaFactory::createCoffee(string type){
    if(type=="latte") return new ChinaLatte();
    return nullptr;
}

Dessert* ChinaFactory::createDessert(string type){
    if(type=="eggyolkpastry") return new EggYolkPastry();
    return nullptr;
}
USAFactory.h --- 美国区工厂(声明)
cpp 复制代码
#ifndef USA_FACTORY_H
#define USA_FACTORY_H
#include"AbstractFactory.h"

class USAFactory:public AbstractFactory{
    public:
    Coffee* createCoffee(string type) override;
    Dessert* createDessert(string type) override;
};

#endif
USAFactory.cpp --- 美国区工厂(实现)
cpp 复制代码
#include"USAFactory.h"
#include"USALatte.h"
#include"Tiramisu.h"

Coffee* USAFactory::createCoffee(string type){
    if(type=="latte") return new USALatte();
    return nullptr;
}

Dessert* USAFactory::createDessert(string type){
    if(type=="tiramisu") return new Tiramisu();
    return nullptr;
}

第 5 步:客户端使用

cpp 复制代码
// src/main.cpp
#include<iostream>
#include<cstdlib>
#include"ChinaFactory.h"
#include"USAFactory.h"
using namespace std;

int main()
{
    // 中国区工厂 → 同时创建咖啡和甜点
    AbstractFactory* chinaFactory=new ChinaFactory();
    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=new USAFactory();
    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;

    delete chinaCoffee; delete chinaDessert; delete chinaFactory;
    delete usaCoffee; delete usaDessert; delete usaFactory;

    system("pause");
    return 0;
}

运行结果:

复制代码
=== 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     多态          多态
 集中管理    易扩展变体    保证搭配一致

三种模式是递进关系,每一种都是为了解决前一种的局限而出现的。理解了这个演进过程,就能在实际项目中根据需求选择合适的模式。

相关推荐
小书房1 小时前
Kotlin使用体验及理解1
android·开发语言·kotlin
CoderMeijun1 小时前
C++ 智能指针:auto_ptr
c++·内存管理·智能指针·raii·auto_ptr
勤劳的进取家1 小时前
传输层基础
运维·开发语言·学习·php
wuminyu1 小时前
专家视角看Lambda表达式的原理解析
java·linux·c语言·jvm·c++
wangbing11251 小时前
Java处理csv文件总是丢数据
java·开发语言·python
Rust语言中文社区1 小时前
【Rust日报】2026-04-28 Pacquet:pnpm 的 Rust 重写版本
开发语言·后端·rust
modelmd2 小时前
研究C语言的hello world输出
c语言·开发语言·chrome
ximu_polaris2 小时前
设计模式(C++)-行为型模式-命令模式
c++·设计模式·命令模式
6Hzlia2 小时前
【Hot 100 刷题计划】 LeetCode 189. 轮转数组 | C++ 三次反转经典魔法 (O(1) 空间)
c++·算法·leetcode