# C++ 工厂方法模式实战指南

C++ 工厂方法模式实战指南

以"咖啡店多地区饮品制作系统"为例,从简单工厂出发,讲解为什么需要工厂方法模式以及如何实现。


一、从简单工厂到工厂方法

简单工厂的局限

在简单工厂模式中,我们用一个 CoffeeSimpleFactory 根据字符串参数创建不同的咖啡:

cpp 复制代码
Coffee* latte = CoffeeSimpleFactory::createCoffee("latte");

现在需求变了------咖啡店开到了中国和美国,同一种咖啡在不同地区价格不同。如果继续用简单工厂,就要这样改:

cpp 复制代码
// 简单工厂的做法:参数越来越多,if/else 越来越长
static Coffee* createCoffee(string type, string region){
    if(region=="china" && type=="latte") return new ChinaLatte();
    if(region=="usa" && type=="latte") return new USALatte();
    // ... 每加一个地区 x 每种咖啡 = 爆炸增长
}

问题 :每新增一个地区或一种咖啡,都要修改工厂的 if/else------违反开闭原则

工厂方法的解决思路

用多态替代 if/else:定义一个抽象工厂基类,每个地区各自实现一个工厂子类。

复制代码
简单工厂:1 个工厂 + if/else 选产品
工厂方法:N 个工厂子类,各自负责自己地区的产品

二、类图

复制代码
         Coffee (抽象产品)
        /      |       \
ChinaAmericano ChinaLatte ChinaCappuccino   (中国区产品)
USAAmericano   USALatte   USACappuccino     (美国区产品)

    CoffeeAbstractFactory (抽象工厂)
       /              \
ChinaCoffeeFactory   USACoffeeFactory       (具体工厂)
  创建中国区产品       创建美国区产品

关键关系

  • CoffeeAbstractFactory 定义接口 createCoffee()
  • ChinaCoffeeFactoryUSACoffeeFactory 各自实现,创建自己地区的产品
  • 客户端通过基类指针 CoffeeAbstractFactory* 调用,不关心具体是哪个工厂

三、项目结构

复制代码
include/
├── Coffee.h                    # 抽象产品基类
├── ChinaAmericano.h             # 中国区美式(声明)
├── ChinaLatte.h                 # 中国区拿铁(声明)
├── ChinaCappuccino.h            # 中国区卡布奇诺(声明)
├── USAAmericano.h               # 美国区美式(声明)
├── USALatte.h                   # 美国区拿铁(声明)
├── USACappuccino.h              # 美国区卡布奇诺(声明)
├── CoffeeAbstractFactory.h      # 抽象工厂基类(声明)
├── ChinaCoffeeFactory.h         # 中国区工厂(声明)
└── USACoffeeFactory.h           # 美国区工厂(声明)

src/
├── main.cpp                     # 主函数
├── ChinaAmericano.cpp           # 中国区产品实现
├── ChinaLatte.cpp
├── ChinaCappuccino.cpp
├── USAAmericano.cpp             # 美国区产品实现
├── USALatte.cpp
├── USACappuccino.cpp
├── ChinaCoffeeFactory.cpp       # 工厂实现
└── USACoffeeFactory.cpp

四、逐步实现

第 1 步:抽象产品基类(复用简单工厂的 Coffee)

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

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

#endif

第 2 步:定义各地区的具体产品

以中国区拿铁为例,其他产品结构完全一样:

cpp 复制代码
// include/ChinaLatte.h --- 声明
#ifndef CHINA_LATTE_H
#define CHINA_LATTE_H
#include"Coffee.h"

class ChinaLatte:public Coffee{
    public:
    string getName() override;
    double getPrice() override;
};

#endif
cpp 复制代码
// src/ChinaLatte.cpp --- 实现
#include"ChinaLatte.h"

string ChinaLatte::getName(){
    return "China Latte";
}

double ChinaLatte::getPrice(){
    return 30;    // 中国区价格:30元
}

所有产品的价格对照:

产品 中国区 美国区
Americano 25 元 $4
Latte 30 元 $5
Cappuccino 32 元 $5.5

第 3 步:定义抽象工厂基类

cpp 复制代码
// include/CoffeeAbstractFactory.h
#ifndef COFFEE_ABSTRACT_FACTORY_H
#define COFFEE_ABSTRACT_FACTORY_H
#include"Coffee.h"

class CoffeeAbstractFactory{
    public:
    virtual Coffee* createCoffee(string type)=0;  // 纯虚函数,子工厂必须实现
    virtual ~CoffeeAbstractFactory(){}
};

#endif

注意 :抽象工厂不包含任何 if/else,它只定义"工厂应该有什么能力"。

第 4 步:实现具体工厂

中国区工厂
cpp 复制代码
// include/ChinaCoffeeFactory.h --- 声明
#ifndef CHINA_COFFEE_FACTORY_H
#define CHINA_COFFEE_FACTORY_H
#include"CoffeeAbstractFactory.h"

class ChinaCoffeeFactory:public CoffeeAbstractFactory{
    public:
    Coffee* createCoffee(string type) override;
};

#endif
cpp 复制代码
// src/ChinaCoffeeFactory.cpp --- 实现
#include"ChinaCoffeeFactory.h"
#include"ChinaAmericano.h"
#include"ChinaLatte.h"
#include"ChinaCappuccino.h"

Coffee* ChinaCoffeeFactory::createCoffee(string type){
    if(type=="americano") return new ChinaAmericano();
    if(type=="latte") return new ChinaLatte();
    if(type=="cappuccino") return new ChinaCappuccino();
    return nullptr;
}
美国区工厂
cpp 复制代码
// include/USACoffeeFactory.h --- 声明
#ifndef USA_COFFEE_FACTORY_H
#define USA_COFFEE_FACTORY_H
#include"CoffeeAbstractFactory.h"

class USACoffeeFactory:public CoffeeAbstractFactory{
    public:
    Coffee* createCoffee(string type) override;
};

#endif
cpp 复制代码
// src/USACoffeeFactory.cpp --- 实现
#include"USACoffeeFactory.h"
#include"USAAmericano.h"
#include"USALatte.h"
#include"USACappuccino.h"

Coffee* USACoffeeFactory::createCoffee(string type){
    if(type=="americano") return new USAAmericano();
    if(type=="latte") return new USALatte();
    if(type=="cappuccino") return new USACappuccino();
    return nullptr;
}

第 5 步:客户端使用

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

int main()
{
    // 创建中国区工厂
    CoffeeAbstractFactory* chinaFactory=new ChinaCoffeeFactory();
    Coffee* chinaLatte=chinaFactory->createCoffee("latte");

    cout<<"=== China Factory ==="<<endl;
    cout<<chinaLatte->getName()<<" - "<<chinaLatte->getPrice()<<"yuan"<<endl;

    // 创建美国区工厂
    CoffeeAbstractFactory* usaFactory=new USACoffeeFactory();
    Coffee* usaLatte=usaFactory->createCoffee("latte");

    cout<<"=== USA Factory ==="<<endl;
    cout<<usaLatte->getName()<<" - $"<<usaLatte->getPrice()<<endl;

    delete chinaLatte; delete usaLatte;
    delete chinaFactory; delete usaFactory;

    system("pause");
    return 0;
}

五、关键理解

为什么 CoffeeAbstractFactory 不根据参数创建不同的工厂?

你可能会想这样写:

cpp 复制代码
// 错误理解:在抽象工厂里选择创建哪个工厂
class CoffeeAbstractFactory{
    static CoffeeAbstractFactory* createFactory(string region){
        if(region=="china") return new ChinaCoffeeFactory();
        if(region=="usa") return new USACoffeeFactory();
    }
};

这又回到了简单工厂的思路------用 if/else 选择。新增地区还是要改这个 if/else。

工厂方法模式的做法是:客户端直接选择用哪个工厂,用多态来处理差异:

cpp 复制代码
// 客户端决定用哪个工厂(这个选择是不可避免的)
CoffeeAbstractFactory* factory = new ChinaCoffeeFactory();

// 之后的代码完全一样,不关心具体是哪个工厂
Coffee* latte = factory->createCoffee("latte");
cout << latte->getName() << endl;

简单工厂 vs 工厂方法 对比

简单工厂 工厂方法
工厂数量 1 个 每个变体 1 个
创建机制 static 方法 + if/else 继承 + virtual 多态
新增产品 修改工厂代码 新增工厂子类,不改旧代码
开闭原则 违反(每次都改工厂) 遵守(只增不改)
适用场景 产品少、变化少 产品族多、经常扩展

新增一个"日本区"需要做什么?

只需要新增文件,不修改任何现有代码:

  1. 新增 JapanAmericano.h/.cppJapanLatte.h/.cppJapanCappuccino.h/.cpp
  2. 新增 JapanCoffeeFactory.h/.cpp
  3. main.cpp 中使用 new JapanCoffeeFactory()

没有任何现有文件被修改------这就是开闭原则。


六、常见错误

错误 1:头文件保护宏重复

cpp 复制代码
// ChinaCoffeeFactory.h 中写了:
#ifndef COFFEE_SIMPLE_FACTORY_H    // 错误!和 SimpleFactory 的宏重名

修复 :每个头文件的宏名必须唯一,用文件名命名:CHINA_COFFEE_FACTORY_H

错误 2:把工厂子类写成产品类

cpp 复制代码
class ChinaCoffeeFactory{
    string getName(){ return "China Coffee Factory"; }  // 错误!工厂不是产品
    double getPrice(){ return 0; }
};

修复 :工厂类应该继承 CoffeeAbstractFactory,实现 createCoffee() 方法来创建产品。

错误 3:抽象工厂写了具体实现

cpp 复制代码
class CoffeeAbstractFactory{
    virtual Coffee* createCoffee(string type){
        // 不应该在抽象基类里写 if/else
        if(type=="latte") return new Latte();
    }
};

修复 :抽象工厂用 =0 纯虚函数,实现留给子类。


七、模式角色总结

角色 本例中 职责
抽象产品 Coffee 定义产品的公共接口
具体产品 ChinaLatte, USALatte 各地区的具体咖啡实现
抽象工厂 CoffeeAbstractFactory 定义创建产品的接口
具体工厂 ChinaCoffeeFactory, USACoffeeFactory 各地区的工厂,创建本地区产品
客户端 main() 选择工厂,通过基类指针使用产品

八、下一步

当咖啡店不仅卖咖啡,还卖甜点(多种产品族)时,就需要抽象工厂模式------每个工厂子类同时创建一组相关产品(咖啡 + 甜点),而不只是单一产品。

相关推荐
Scott9999HH8 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
2401_841495649 小时前
【操作系统】进程同步与互斥实验报告
c++·算法·操作系统·进程·并发·同步·互斥
fqbqrr9 小时前
2607C++,soui与安卓
c++·soui
码智社9 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海9 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖10 小时前
JDK 26 新特性详解
java·开发语言
马优晨10 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
fqbqrr12 小时前
2607C++,使用微软detours勾挂工具
c++
人邮异步社区12 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大13 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf