# 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() 选择工厂,通过基类指针使用产品

八、下一步

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

相关推荐
忡黑梨1 小时前
eNSP_DHCP配置
c语言·网络·c++·python·算法·网络安全·智能路由器
skywalk81632 小时前
CodeArts碰到问题:CodeArts 智能体使用失败,显示:会话创建失败,请稍后重试
开发语言·python
a里啊里啊2 小时前
软考-软件评测师:知识点整理(七)——软件工程
设计模式·软件工程·软考·uml·结构化开发·软件评测师·软件模型
白露与泡影2 小时前
从区间锁到行锁:一次高并发写入死锁治理实战
java·开发语言
小短腿的代码世界2 小时前
VLC-Qt深度解析:Qt应用中的专业视频播放方案
开发语言·qt
丑八怪大丑2 小时前
Java范型
java·开发语言
加藤不太惠2 小时前
Nacos简单实用集群创建
java·开发语言·nacos
我能坚持多久2 小时前
C++的Vector学习:从功能探索到底层实现
开发语言·c++·学习
她说彩礼65万2 小时前
C语言 动态内存管理
c语言·开发语言·算法