工厂方法模式实战:别再用 new 硬编码了!从对象创建混乱到优雅扩展

目录

引言:为什么你总在 "new 对象" 时栽跟头?

一、先搞懂:工厂方法模式到底是什么?

1.1 核心思想:"谁用谁创建" 不如 "专门工厂来创建"

1.2 解决什么问题?3 个核心痛点

1.3 工厂方法的 4 个核心角色

1.4 和简单工厂的区别:别搞混了!

二、C++ 反例实战:没有工厂的 "支付系统" 有多坑?

2.1 需求背景

2.2 反例代码:直接 new+switch 的混乱实现

2.3 反例代码的 3 大致命问题

三、C++ 重构实战:用工厂方法模式打造 "可插拔" 支付系统

3.1 重构后的完整代码

3.1.1 抽象产品与具体产品(支付方式)

3.1.2 抽象工厂与具体工厂(创建支付对象)

3.1.3 业务模块:订单系统(只依赖抽象)

3.1.4 新增银联支付(无需修改老代码)

3.1.5 主函数:测试工厂方法的灵活性

3.2 编译与运行说明

3.2.1 编译命令(GCC)

3.2.2 运行结果

3.3 重构后的核心优势(对比反例)

四、扩展实战:工厂方法模式的 3 个经典应用场景

4.1 场景 1:日志系统(多输出目标)

需求:支持控制台日志、文件日志、数据库日志,可动态切换

工厂方法实现:

4.2 场景 2:文档解析器(多格式支持)

需求:支持解析 TXT、PDF、Word 文档,未来可能加 Markdown

工厂方法实现:

4.3 场景 3:游戏角色创建(多职业系统)

需求:游戏支持战士、法师、牧师三种职业,每种职业有不同技能和属性

工厂方法实现:

五、C++ 实战避坑指南:工厂方法模式的 5 个常见误区

误区 1:工厂接口设计过粗,一个工厂创建多种产品

误区 2:工厂方法返回具体产品类型,而非抽象产品

误区 3:滥用工厂模式,简单对象也用工厂创建

误区 4:具体工厂不对外暴露,在业务中硬编码工厂创建

误区 5:忽视 C++ 特性,工厂方法参数传递不当

六、总结:工厂方法模式的核心与实战建议

6.1 核心要点提炼

6.2 C++ 实战建议

6.3 最后一句话


class 卑微码农:
    def __init__(self):
        self.技能 = ['能读懂十年前祖传代码', '擅长用Ctrl+C/V搭建世界', '信奉"能跑就别动"的玄学']
        self.发量 = 100  # 初始发量
        self.咖啡因耐受度 = '极限'
        
    def 修Bug(self, bug):
        try:
            # 试图用玄学解决问题
            if bug.严重程度 == '离谱':
                print("这一定是环境问题!")
            else:
                print("让我看看是谁又没写注释...哦,是我自己。")
        except Exception as e:
            # 如果try块都救不了,那就...
            print("重启一下试试?")
            self.发量 -= 1  # 每解决一个bug,头发-1
 
 
# 实例化一个我
我 = 卑微码农()

引言:为什么你总在 "new 对象" 时栽跟头?

作为 C++ 开发者,你一定写过这样的代码:

// 支付功能:根据类型创建不同支付对象
IPay* createPay(int type) {
    if (type == 1) {
        return new WechatPay();  // 硬编码微信支付
    } else if (type == 2) {
        return new Alipay();     // 硬编码支付宝
    } else if (type == 3) {
        return new UnionPay();   // 新增银联支付,又改这里
    }
    return nullptr;
}

这段代码看似简单,却藏着 3 个致命问题:

  • 新增支付方式必改代码:产品要加 ApplePay?你得在createPay里加else if (type == 4),违反开放封闭原则;
  • 业务逻辑与创建逻辑混杂:订单服务调用createPay时,既要关心 "如何支付",还要知道 "怎么创建支付对象",职责混乱;
  • 测试成本高:改了createPay后,所有依赖它的模块(订单、退款、对账)都得回归测试,生怕漏改if-else导致创建错误对象。

这些问题的根源,在于对象创建逻辑没有被妥善管理—— 直接用new硬编码具体类型,就像把 "零件生产" 和 "产品组装" 混在一个车间,效率低还容易出错。

工厂方法模式(Factory Method Pattern),就是专门解决对象创建混乱的 "生产车间管理方案":通过定义 "抽象工厂" 和 "具体工厂",让对象创建逻辑与业务逻辑分离,新增类型时只需加新工厂,无需修改老代码。

这篇文章不会讲干巴巴的 UML 图,而是用 C++ 实战场景 + 可运行代码 + 真实踩坑经验,带你彻底吃透工厂方法模式。不管你是刚接触设计模式的新手,还是想摆脱 "new 对象泥潭" 的开发者,读完这篇,你都能写出 "创建逻辑灵活扩展,业务代码稳如泰山" 的优雅代码。

一、先搞懂:工厂方法模式到底是什么?

1.1 核心思想:"谁用谁创建" 不如 "专门工厂来创建"

工厂方法模式的核心是:定义一个创建对象的接口(抽象工厂),让子类(具体工厂)决定实例化哪个类(产品),将对象创建延迟到子类

用一句大白话解释:别在业务代码里直接 new 对象了,交给专门的 "工厂" 去创建。比如支付系统中,微信支付由WechatPayFactory创建,支付宝由AlipayFactory创建,业务逻辑只需要调用工厂的 "创建方法",不用关心具体怎么 new。

举个生活化的例子:

  • 传统做法(直接 new):你想吃披萨,自己动手做(自己 new),换个口味(新增类型)就得重新学做法;
  • 工厂方法:你去披萨店(抽象工厂),点不同口味的披萨(具体产品),店员(具体工厂)帮你做,新增口味只需雇新店员,你不用学新做法。

这里的 "披萨店" 就是抽象工厂,"不同口味的店员" 是具体工厂,"披萨" 是产品 —— 业务逻辑(你)只和抽象工厂交互,具体创建交给子类。

1.2 解决什么问题?3 个核心痛点

工厂方法模式专门解决对象创建的 3 大痛点:

  1. 创建逻辑分散:到处都是newif-else,新增类型要改多个地方;
  2. 业务与创建耦合:业务代码依赖具体产品类型,换产品就得改业务;
  3. 扩展困难:新增产品需要修改创建逻辑,违反开放封闭原则。

1.3 工厂方法的 4 个核心角色

理解工厂方法模式,只需记住 4 个角色:

  • 抽象产品(Product):定义产品的接口(如IPay),是所有具体产品的父类;
  • 具体产品(ConcreteProduct):实现抽象产品的具体类(如WechatPayAlipay);
  • 抽象工厂(Factory):定义创建产品的接口(如IPayFactory),包含一个创建产品的纯虚方法;
  • 具体工厂(ConcreteFactory):实现抽象工厂的方法,负责创建具体产品(如WechatPayFactory)。

它们的关系就像:抽象工厂→生产→抽象产品,具体工厂→生产→具体产品,业务逻辑只依赖 "抽象工厂" 和 "抽象产品",不碰具体实现。

1.4 和简单工厂的区别:别搞混了!

很多人分不清 "工厂方法" 和 "简单工厂",这里一句话说清:

  • 简单工厂:一个工厂类负责所有产品的创建(用switch判断),新增产品要改工厂类,违反开放封闭原则;
  • 工厂方法:每个具体产品对应一个具体工厂,新增产品只需加新工厂,不用改老代码,符合开放封闭原则。

简单说,简单工厂是 "一厂多品",工厂方法是 "一厂一品"—— 前者适合产品少且稳定的场景,后者适合产品多变的场景。

二、C++ 反例实战:没有工厂的 "支付系统" 有多坑?

为了让你直观感受工厂方法的价值,我们先实现一个 "没有工厂" 的支付系统,看看它在需求变更时的狼狈。

2.1 需求背景

实现一个电商支付系统,核心功能:

  1. 支持微信、支付宝两种支付方式,每种支付有pay(发起支付)和refund(退款)方法;
  2. 订单系统根据用户选择的支付方式,创建对应支付对象并调用支付方法;
  3. 未来可能新增银联、ApplePay 等支付方式。

2.2 反例代码:直接 new+switch 的混乱实现

#include <iostream>
#include <string>

// 抽象产品:支付接口
class IPay {
public:
    virtual ~IPay() = default;
    virtual void pay(double amount) = 0;    // 发起支付
    virtual void refund(double amount) = 0; // 退款
};

// 具体产品:微信支付
class WechatPay : public IPay {
public:
    void pay(double amount) override {
        std::cout << "微信支付:" << amount << "元" << std::endl;
    }
    void refund(double amount) override {
        std::cout << "微信退款:" << amount << "元" << std::endl;
    }
};

// 具体产品:支付宝
class Alipay : public IPay {
public:
    void pay(double amount) override {
        std::cout << "支付宝支付:" << amount << "元" << std::endl;
    }
    void refund(double amount) override {
        std::cout << "支付宝退款:" << amount << "元" << std::endl;
    }
};

// 业务模块:订单系统(直接创建支付对象,耦合具体类型)
class OrderSystem {
public:
    // 根据类型创建支付对象(硬编码,违反开放封闭)
    IPay* createPay(int payType) {
        switch (payType) {
            case 1: return new WechatPay();  // 直接new具体类型
            case 2: return new Alipay();     // 直接new具体类型
            default: return nullptr;
        }
    }

    // 处理支付(业务逻辑与创建逻辑混杂)
    void processPay(int payType, double amount) {
        IPay* pay = createPay(payType);
        if (pay) {
            pay->pay(amount);
            delete pay; // 手动释放,容易漏
        }
    }
};

// 主函数测试
int main() {
    OrderSystem orderSystem;
    orderSystem.processPay(1, 199.9);  // 微信支付
    orderSystem.processPay(2, 299.9);  // 支付宝支付
    return 0;
}

2.3 反例代码的 3 大致命问题

这段代码能跑,但当产品要求 "新增银联支付" 时,问题集中爆发:

  1. 必须修改业务代码:要在OrderSystemcreatePay里加case 3: return new UnionPay(),违反 "对修改关闭" 原则,改完还得重新测试整个订单系统;
  2. 创建逻辑与业务耦合OrderSystem既要管订单业务,又要负责支付对象创建,违背单一职责原则,代码越来越臃肿;
  3. 扩展成本高:如果后续还要加 "支付方式配置中心"(比如从数据库读支付类型),createPay里的switch会越来越长,变成 "if-else 地狱"。

更隐蔽的问题是:如果WechatPay的构造函数需要新增参数(比如appId),所有new WechatPay()的地方都得改 —— 这就是直接new具体类型的 "牵一发而动全身"。

三、C++ 重构实战:用工厂方法模式打造 "可插拔" 支付系统

针对反例的问题,我们用工厂方法模式重构,核心思路是 **"创建逻辑抽离到工厂,业务只认抽象工厂"**:

  1. 定义抽象工厂IPayFactory,包含创建支付对象的纯虚方法;
  2. 为每个具体支付方式创建对应工厂(WechatPayFactoryAlipayFactory);
  3. 业务模块OrderSystem只依赖IPayFactory,通过工厂获取支付对象,不碰具体类型。

3.1 重构后的完整代码

3.1.1 抽象产品与具体产品(支付方式)
// IPay.h(抽象产品)
#ifndef I_PAY_H
#define I_PAY_H

#include <string>

// 支付接口:所有支付方式的抽象
class IPay {
public:
    virtual ~IPay() = default;
    virtual void pay(double amount) = 0;    // 发起支付
    virtual void refund(double amount) = 0; // 退款
    virtual std::string getPayName() const = 0; // 获取支付名称
};

#endif // I_PAY_H
// WechatPay.h(具体产品:微信支付)
#ifndef WECHAT_PAY_H
#define WECHAT_PAY_H

#include "IPay.h"

class WechatPay : public IPay {
public:
    void pay(double amount) override;
    void refund(double amount) override;
    std::string getPayName() const override;
};

#endif // WECHAT_PAY_H
// WechatPay.cpp
#include "WechatPay.h"
#include <iostream>

void WechatPay::pay(double amount) {
    std::cout << "[微信支付] 发起支付:" << amount << "元" << std::endl;
    // 实际调用微信支付SDK的逻辑(简化)
}

void WechatPay::refund(double amount) {
    std::cout << "[微信支付] 发起退款:" << amount << "元" << std::endl;
}

std::string WechatPay::getPayName() const {
    return "微信支付";
}
// Alipay.h(具体产品:支付宝)
#ifndef ALIPAY_H
#define ALIPAY_H

#include "IPay.h"

class Alipay : public IPay {
public:
    void pay(double amount) override;
    void refund(double amount) override;
    std::string getPayName() const override;
};

#endif // ALIPAY_H
// Alipay.cpp
#include "Alipay.h"
#include <iostream>

void Alipay::pay(double amount) {
    std::cout << "[支付宝] 发起支付:" << amount << "元" << std::endl;
    // 实际调用支付宝SDK的逻辑(简化)
}

void Alipay::refund(double amount) {
    std::cout << "[支付宝] 发起退款:" << amount << "元" << std::endl;
}

std::string Alipay::getPayName() const {
    return "支付宝";
}
3.1.2 抽象工厂与具体工厂(创建支付对象)
// IPayFactory.h(抽象工厂)
#ifndef I_PAY_FACTORY_H
#define I_PAY_FACTORY_H

#include "IPay.h"
#include <memory>  // 用智能指针管理对象生命周期

// 支付工厂接口:定义创建支付对象的契约
class IPayFactory {
public:
    virtual ~IPayFactory() = default;
    // 纯虚方法:创建支付对象(返回智能指针,避免内存泄漏)
    virtual std::unique_ptr<IPay> createPay() = 0;
};

#endif // I_PAY_FACTORY_H
// WechatPayFactory.h(具体工厂:创建微信支付)
#ifndef WECHAT_PAY_FACTORY_H
#define WECHAT_PAY_FACTORY_H

#include "IPayFactory.h"
#include "WechatPay.h"

class WechatPayFactory : public IPayFactory {
public:
    // 实现创建方法:返回微信支付对象
    std::unique_ptr<IPay> createPay() override {
        // 这里可以添加微信支付的初始化逻辑(如配置appId、密钥)
        return std::make_unique<WechatPay>();
    }
};

#endif // WECHAT_PAY_FACTORY_H
// AlipayFactory.h(具体工厂:创建支付宝)
#ifndef ALIPAY_FACTORY_H
#define ALIPAY_FACTORY_H

#include "IPayFactory.h"
#include "Alipay.h"

class AlipayFactory : public IPayFactory {
public:
    // 实现创建方法:返回支付宝对象
    std::unique_ptr<IPay> createPay() override {
        // 这里可以添加支付宝的初始化逻辑(如配置appId、私钥)
        return std::make_unique<Alipay>();
    }
};

#endif // ALIPAY_FACTORY_H
3.1.3 业务模块:订单系统(只依赖抽象)
// OrderSystem.h(业务模块)
#ifndef ORDER_SYSTEM_H
#define ORDER_SYSTEM_H

#include "IPayFactory.h"
#include <string>

// 订单系统:只依赖抽象工厂和抽象产品,不碰具体实现
class OrderSystem {
public:
    // 接收抽象工厂作为参数,不关心具体是哪个工厂
    void processPay(IPayFactory& factory, double amount) {
        // 通过工厂创建支付对象(多态)
        std::unique_ptr<IPay> pay = factory.createPay();
        if (pay) {
            std::cout << "订单支付开始,支付方式:" << pay->getPayName() << std::endl;
            pay->pay(amount);  // 调用支付方法(多态)
        }
    }

    void processRefund(IPayFactory& factory, double amount) {
        std::unique_ptr<IPay> pay = factory.createPay();
        if (pay) {
            std::cout << "订单退款开始,支付方式:" << pay->getPayName() << std::endl;
            pay->refund(amount);  // 调用退款方法(多态)
        }
    }
};

#endif // ORDER_SYSTEM_H
3.1.4 新增银联支付(无需修改老代码)
// UnionPay.h(新增具体产品:银联支付)
#ifndef UNION_PAY_H
#define UNION_PAY_H

#include "IPay.h"

class UnionPay : public IPay {
public:
    void pay(double amount) override;
    void refund(double amount) override;
    std::string getPayName() const override;
};

#endif // UNION_PAY_H
// UnionPay.cpp
#include "UnionPay.h"
#include <iostream>

void UnionPay::pay(double amount) {
    std::cout << "[银联支付] 发起支付:" << amount << "元" << std::endl;
}

void UnionPay::refund(double amount) {
    std::cout << "[银联支付] 发起退款:" << amount << "元" << std::endl;
}

std::string UnionPay::getPayName() const {
    return "银联支付";
}
// UnionPayFactory.h(新增具体工厂:创建银联支付)
#ifndef UNION_PAY_FACTORY_H
#define UNION_PAY_FACTORY_H

#include "IPayFactory.h"
#include "UnionPay.h"

class UnionPayFactory : public IPayFactory {
public:
    std::unique_ptr<IPay> createPay() override {
        // 银联支付的初始化逻辑(如配置商户号)
        return std::make_unique<UnionPay>();
    }
};

#endif // UNION_PAY_FACTORY_H
3.1.5 主函数:测试工厂方法的灵活性
// main.cpp
#include <iostream>
#include "OrderSystem.h"
#include "WechatPayFactory.h"
#include "AlipayFactory.h"
#include "UnionPayFactory.h"

int main() {
    OrderSystem orderSystem;

    // 1. 微信支付
    std::cout << "=== 测试微信支付 ===" << std::endl;
    WechatPayFactory wechatFactory;
    orderSystem.processPay(wechatFactory, 199.9);  // 传入微信工厂
    orderSystem.processRefund(wechatFactory, 199.9);

    // 2. 支付宝支付
    std::cout << "\n=== 测试支付宝支付 ===" << std::endl;
    AlipayFactory alipayFactory;
    orderSystem.processPay(alipayFactory, 299.9);   // 传入支付宝工厂
    orderSystem.processRefund(alipayFactory, 299.9);

    // 3. 新增银联支付(无需修改OrderSystem)
    std::cout << "\n=== 测试银联支付 ===" << std::endl;
    UnionPayFactory unionFactory;
    orderSystem.processPay(unionFactory, 399.9);    // 传入银联工厂
    orderSystem.processRefund(unionFactory, 399.9);

    return 0;
}

3.2 编译与运行说明

3.2.1 编译命令(GCC)
g++ -std=c++11 main.cpp WechatPay.cpp Alipay.cpp UnionPay.cpp -o pay_system
  • 依赖:C++11 及以上标准(支持std::unique_ptroverride);
  • 编译环境:Linux/GCC、Windows/MinGW、Mac/Clang 均可。
3.2.2 运行结果
=== 测试微信支付 ===
订单支付开始,支付方式:微信支付
[微信支付] 发起支付:199.9元
订单退款开始,支付方式:微信支付
[微信支付] 发起退款:199.9元

=== 测试支付宝支付 ===
订单支付开始,支付方式:支付宝
[支付宝] 发起支付:299.9元
订单退款开始,支付方式:支付宝
[支付宝] 发起退款:299.9元

=== 测试银联支付 ===
订单支付开始,支付方式:银联支付
[银联支付] 发起支付:399.9元
订单退款开始,支付方式:银联支付
[银联支付] 发起退款:399.9元

3.3 重构后的核心优势(对比反例)

重构后的代码完全符合工厂方法模式,解决了反例的所有问题:

  1. 新增支付方式无需修改老代码:加银联支付时,只需新增UnionPay(具体产品)和UnionPayFactory(具体工厂),OrderSystem和原有工厂代码一行不动,符合开放封闭原则;
  2. 创建逻辑与业务彻底分离OrderSystem只负责订单业务,支付对象的创建交给工厂,职责清晰,代码可读性大幅提升;
  3. 灵活切换支付方式:业务模块通过 "传入不同工厂" 实现支付方式切换,比如从微信换成支付宝,只需改一行代码(换工厂实例);
  4. 避免内存泄漏:用std::unique_ptr管理支付对象生命周期,无需手动delete,比反例的原始指针更安全;
  5. 初始化逻辑集中管理:每个工厂的createPay方法可以封装产品的初始化细节(如配置密钥、连接 SDK),避免创建逻辑散落在业务代码中。

四、扩展实战:工厂方法模式的 3 个经典应用场景

工厂方法模式的应用远不止支付系统,在 C++ 开发中,凡是需要 "灵活创建对象" 的场景,都能用上。下面通过 3 个真实场景,展示模式的灵活性。

4.1 场景 1:日志系统(多输出目标)

需求:支持控制台日志、文件日志、数据库日志,可动态切换
工厂方法实现:
// 1. 抽象产品:日志接口
class ILogger {
public:
    virtual ~ILogger() = default;
    virtual void log(const std::string& msg) = 0;
};

// 2. 具体产品:控制台日志
class ConsoleLogger : public ILogger {
public:
    void log(const std::string& msg) override {
        std::cout << "[控制台] " << msg << std::endl;
    }
};

// 3. 具体产品:文件日志
class FileLogger : public ILogger {
private:
    std::string filePath_;
public:
    explicit FileLogger(std::string path) : filePath_(std::move(path)) {}
    void log(const std::string& msg) override {
        // 实际写入文件的逻辑(简化)
        std::cout << "[文件][" << filePath_ << "] " << msg << std::endl;
    }
};

// 4. 抽象工厂:日志工厂
class ILoggerFactory {
public:
    virtual ~ILoggerFactory() = default;
    virtual std::unique_ptr<ILogger> createLogger() = 0;
};

// 5. 具体工厂:控制台日志工厂
class ConsoleLoggerFactory : public ILoggerFactory {
public:
    std::unique_ptr<ILogger> createLogger() override {
        return std::make_unique<ConsoleLogger>();
    }
};

// 6. 具体工厂:文件日志工厂(带参数)
class FileLoggerFactory : public ILoggerFactory {
private:
    std::string filePath_;
public:
    explicit FileLoggerFactory(std::string path) : filePath_(std::move(path)) {}
    std::unique_ptr<ILogger> createLogger() override {
        // 传递参数给具体产品
        return std::make_unique<FileLogger>(filePath_);
    }
};

// 7. 业务模块:日志服务
class LogService {
public:
    // 依赖抽象工厂
    void writeLog(ILoggerFactory& factory, const std::string& msg) {
        auto logger = factory.createLogger();
        logger->log(msg);
    }
};

// 测试代码
int main() {
    LogService logService;

    // 控制台日志
    ConsoleLoggerFactory consoleFactory;
    logService.writeLog(consoleFactory, "用户登录成功");

    // 文件日志(带路径参数)
    FileLoggerFactory fileFactory("app.log");
    logService.writeLog(fileFactory, "订单创建成功");

    return 0;
}

运行结果

[控制台] 用户登录成功
[文件][app.log] 订单创建成功

优势:新增 "数据库日志" 时,只需加DatabaseLoggerDatabaseLoggerFactoryLogService完全不用改,甚至可以在运行时根据配置文件选择日志工厂。

4.2 场景 2:文档解析器(多格式支持)

需求:支持解析 TXT、PDF、Word 文档,未来可能加 Markdown
工厂方法实现:
// 1. 抽象产品:文档解析器
class IDocumentParser {
public:
    virtual ~IDocumentParser() = default;
    virtual void parse(const std::string& filePath) = 0; // 解析文档
    virtual std::string getFormat() const = 0; // 获取格式名称
};

// 2. 具体产品:TXT解析器
class TxtParser : public IDocumentParser {
public:
    void parse(const std::string& filePath) override {
        std::cout << "解析TXT文件:" << filePath << std::endl;
    }
    std::string getFormat() const override { return "TXT"; }
};

// 3. 具体产品:PDF解析器
class PdfParser : public IDocumentParser {
public:
    void parse(const std::string& filePath) override {
        std::cout << "解析PDF文件:" << filePath << std::endl;
    }
    std::string getFormat() const override { return "PDF"; }
};

// 4. 抽象工厂:解析器工厂
class IDocumentParserFactory {
public:
    virtual ~IDocumentParserFactory() = default;
    virtual std::unique_ptr<IDocumentParser> createParser() = 0;
};

// 5. 具体工厂:TXT解析器工厂
class TxtParserFactory : public IDocumentParserFactory {
public:
    std::unique_ptr<IDocumentParser> createParser() override {
        return std::make_unique<TxtParser>();
    }
};

// 6. 具体工厂:PDF解析器工厂
class PdfParserFactory : public IDocumentParserFactory {
public:
    std::unique_ptr<IDocumentParser> createParser() override {
        return std::make_unique<PdfParser>();
    }
};

// 7. 业务模块:文档处理服务
class DocumentService {
public:
    void processDocument(IDocumentParserFactory& factory, const std::string& filePath) {
        auto parser = factory.createParser();
        std::cout << "开始处理" << parser->getFormat() << "文档..." << std::endl;
        parser->parse(filePath);
    }
};

// 测试代码
int main() {
    DocumentService docService;

    // 解析TXT
    TxtParserFactory txtFactory;
    docService.processDocument(txtFactory, "readme.txt");

    // 解析PDF
    PdfParserFactory pdfFactory;
    docService.processDocument(pdfFactory, "report.pdf");

    return 0;
}

运行结果

开始处理TXT文档...
解析TXT文件:readme.txt
开始处理PDF文档...
解析PDF文件:report.pdf

优势:每个解析器的创建逻辑封装在对应工厂,业务模块不用关心 "PDF 解析需要依赖哪个库",只需调用工厂即可,符合依赖倒置原则。

4.3 场景 3:游戏角色创建(多职业系统)

需求:游戏支持战士、法师、牧师三种职业,每种职业有不同技能和属性
工厂方法实现:
// 1. 抽象产品:游戏角色
class ICharacter {
public:
    virtual ~ICharacter() = default;
    virtual void attack() = 0;    // 攻击技能
    virtual void defend() = 0;    // 防御技能
    virtual std::string get职业() const = 0;
};

// 2. 具体产品:战士
class Warrior : public ICharacter {
public:
    void attack() override { std::cout << "战士使用劈砍!" << std::endl; }
    void defend() override { std::cout << "战士举起盾牌防御!" << std::endl; }
    std::string get职业() const override { return "战士"; }
};

// 3. 具体产品:法师
class Mage : public ICharacter {
public:
    void attack() override { std::cout << "法师释放火球术!" << std::endl; }
    void defend() override { std::cout << "法师开启魔法护盾!" << std::endl; }
    std::string get职业() const override { return "法师"; }
};

// 4. 抽象工厂:角色工厂
class ICharacterFactory {
public:
    virtual ~ICharacterFactory() = default;
    virtual std::unique_ptr<ICharacter> createCharacter() = 0;
};

// 5. 具体工厂:战士工厂
class WarriorFactory : public ICharacterFactory {
public:
    std::unique_ptr<ICharacter> createCharacter() override {
        return std::make_unique<Warrior>();
    }
};

// 6. 具体工厂:法师工厂
class MageFactory : public ICharacterFactory {
public:
    std::unique_ptr<ICharacter> createCharacter() override {
        return std::make_unique<Mage>();
    }
};

// 7. 业务模块:游戏系统
class GameSystem {
public:
    void createAndPlay(ICharacterFactory& factory) {
        auto character = factory.createCharacter();
        std::cout << "创建" << character->get职业() << "成功!" << std::endl;
        character->attack();
        character->defend();
    }
};

// 测试代码
int main() {
    GameSystem game;

    // 创建战士
    WarriorFactory warriorFactory;
    game.createAndPlay(warriorFactory);

    // 创建法师
    MageFactory mageFactory;
    game.createAndPlay(mageFactory);

    return 0;
}

运行结果

创建战士成功!
战士使用劈砍!
战士举起盾牌防御!
创建法师成功!
法师释放火球术!
法师开启魔法护盾!

优势:新增 "牧师" 职业时,只需加PriestPriestFactory,游戏核心逻辑GameSystem完全不变,甚至可以在游戏运行时让玩家选择工厂创建不同角色。

五、C++ 实战避坑指南:工厂方法模式的 5 个常见误区

工厂方法模式看似简单,但在实际应用中,很多开发者会因理解偏差写出 "反模式" 代码。下面列出 5 个典型误区及避坑方案。

误区 1:工厂接口设计过粗,一个工厂创建多种产品

比如定义IAllFactory,包含createPay()createLogger()createParser()等方法,让一个工厂负责所有产品的创建 —— 这违背了 "单一职责原则",工厂会变得臃肿,新增产品还得改工厂接口。

避坑方案:一个抽象工厂只负责一类产品的创建(如IPayFactory只创建支付相关产品),不同类产品对应不同工厂,避免 "万能工厂"。

误区 2:工厂方法返回具体产品类型,而非抽象产品

比如WechatPayFactorycreatePay()返回WechatPay*而非IPay*,导致业务模块依赖具体产品,失去多态优势。

避坑方案:工厂方法的返回值必须是抽象产品类型(如std::unique_ptr<IPay>),确保业务模块只认抽象,不认具体。

误区 3:滥用工厂模式,简单对象也用工厂创建

比如创建UserOrder这类简单 POJO 对象(只有属性和 getter/setter),也非要整个UserFactory—— 这会增加代码复杂度,完全没必要。

避坑方案:工厂方法适合创建 "复杂对象"(如需要初始化配置、依赖外部资源、有复杂构造逻辑的对象),简单对象直接new即可。

误区 4:具体工厂不对外暴露,在业务中硬编码工厂创建

比如业务代码里写IPayFactory* factory = new WechatPayFactory(),虽然避免了直接new产品,但又硬编码了工厂类型,换工厂还得改业务代码。

避坑方案:用 "工厂的工厂"(抽象工厂的工厂)或配置文件动态创建工厂,比如:

// 工厂的工厂:根据类型字符串创建具体工厂
std::unique_ptr<IPayFactory> createPayFactory(const std::string& type) {
    if (type == "wechat") {
        return std::make_unique<WechatPayFactory>();
    } else if (type == "alipay") {
        return std::make_unique<AlipayFactory>();
    }
    return nullptr;
}

// 业务中通过配置字符串获取工厂,避免硬编码
std::string payType = getConfig("pay_type"); // 从配置文件读
auto factory = createPayFactory(payType);

误区 5:忽视 C++ 特性,工厂方法参数传递不当

比如具体工厂需要参数(如文件日志的路径),却在工厂构造函数中硬编码,导致工厂灵活性不足。

避坑方案:通过工厂的构造函数传递参数,让工厂在创建时配置初始化信息,如:

// 文件日志工厂:通过构造函数接收路径参数
class FileLoggerFactory : public ILoggerFactory {
private:
    std::string path_;
public:
    explicit FileLoggerFactory(std::string path) : path_(std::move(path)) {}
    std::unique_ptr<ILogger> createLogger() override {
        return std::make_unique<FileLogger>(path_); // 传递参数给产品
    }
};

六、总结:工厂方法模式的核心与实战建议

6.1 核心要点提炼

  1. 核心思想:将对象创建延迟到具体工厂,业务逻辑只依赖抽象工厂和抽象产品;
  2. 4 个角色:抽象产品(Product)、具体产品(ConcreteProduct)、抽象工厂(Factory)、具体工厂(ConcreteFactory);
  3. 核心价值:解耦对象创建与业务逻辑,支持灵活扩展,符合开放封闭原则;
  4. 适用场景:产品类型多变、创建逻辑复杂、需要统一管理对象创建的场景。

6.2 C++ 实战建议

  1. 抽象设计

    • 抽象产品用纯虚类定义,包含必要的业务方法;
    • 抽象工厂只定义一个create方法(单一职责),返回抽象产品的智能指针;
    • 优先用std::unique_ptr管理产品生命周期,避免内存泄漏。
  2. 具体实现

    • 具体工厂的create方法负责产品的完整创建(包括初始化、配置);
    • 产品需要参数时,通过工厂的构造函数传递,再由工厂传递给产品;
    • 新增产品时,同步新增对应的具体工厂,确保 "一厂一品"。
  3. 业务调用

    • 业务模块通过 "注入工厂" 而非 "直接创建工厂" 来获取产品;
    • 避免在业务中硬编码具体工厂类型,可通过配置或工厂的工厂动态获取。
  4. 与其他模式结合

    • 结合依赖注入:在大型项目中,用依赖注入框架管理工厂的创建和注入;
    • 结合原型模式:如果产品创建成本高,工厂可以先创建原型,再通过克隆生成产品;
    • 结合单例模式:如果工厂无状态,可将具体工厂设计为单例,避免重复创建。

6.3 最后一句话

工厂方法模式的本质,是 "用抽象隔离对象创建的变化"。它不会让你的代码行数减少,但会让代码的 "变化部分" 和 "稳定部分" 泾渭分明 —— 当新增产品时,你只需关注新工厂和新产品的实现,不用再在老代码中 "缝缝补补"。

记住:好的代码不是 "一次写对",而是 "在需求变更时,能以最小代价修改"。工厂方法模式,就是帮你实现这一点的利器。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值