抽象工厂模式实战:告别多工厂混乱!一键切换产品族,扩展不修改老代码

目录

引言:工厂方法不够用了?多产品族场景的创建噩梦

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

1.1 核心思想:“一个工厂,搞定一整套产品”

1.2 关键概念:产品族 vs 产品等级结构(必懂!)

1.3 抽象工厂的 4 个核心角色(和工厂方法对比)

1.4 和工厂方法模式的核心区别(避免混淆!)

二、C++ 反例实战:工厂方法模式处理多产品族的混乱

2.1 需求背景

2.2 工厂方法模式的混乱实现

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

三、C++ 重构实战:用抽象工厂模式打造 “一键切换产品族” 的支付系统

3.1 重构后的完整代码

3.1.1 抽象产品(产品等级结构)

3.1.2 具体产品(微信产品族)

3.1.3 具体产品(支付宝产品族)

3.1.4 抽象工厂与具体工厂

3.1.5 业务模块(订单系统)

3.1.6 新增银联产品族(无需修改老代码)

3.1.7 主函数测试

3.2 编译与运行说明

3.2.1 编译命令(GCC)

3.2.2 运行结果

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

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

4.1 场景 1:UI 组件库(跨平台界面切换)

需求:开发跨平台应用,支持 Windows 和 Mac 两套 UI 组件(按钮、输入框、下拉框),可一键切换平台。

抽象工厂实现:

4.2 场景 2:数据库访问层(多数据库切换)

需求:支持 MySQL 和 PostgreSQL 两种数据库,每种数据库包含连接、查询、事务 3 个配套操作,可动态切换数据库。

抽象工厂实现:

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
 
 
# 实例化一个我
我 = 卑微码农()

引言:工厂方法不够用了?多产品族场景的创建噩梦

作为 C++ 开发者,你一定用工厂方法模式解决过对象创建问题 —— 比如支付系统中,微信支付由WechatPayFactory创建,支付宝由AlipayFactory创建,业务逻辑只需切换工厂即可切换支付方式。

但如果需求升级:支付系统不仅需要 “支付” 功能,还需要配套的 “退款”“对账” 功能 —— 微信支付对应WechatRefund“WechatReconciliation”,支付宝对应AlipayRefund“AlipayReconciliation”。此时用工厂方法模式,你会发现:

// 工厂方法模式的尴尬:每个产品对应一个工厂,工厂泛滥成灾
WechatPayFactory payFactory;       // 微信支付工厂
WechatRefundFactory refundFactory; // 微信退款工厂
WechatReconciliationFactory reconFactory; // 微信对账工厂

// 业务调用:需要管理多个工厂,切换产品族(比如从微信换支付宝)要改3处
auto pay = payFactory.create();
auto refund = refundFactory.create();
auto recon = reconFactory.create();

这种场景下,工厂方法模式的弊端暴露无遗:

  • 工厂泛滥:一个产品族(如微信支付相关)包含 N 个产品,就需要 N 个工厂,管理成本指数级增长;
  • 切换繁琐:切换产品族(如从微信换成支付宝),需要修改所有工厂的创建逻辑,违反开放封闭原则;
  • 业务耦合:业务代码需要同时依赖多个工厂,一旦工厂数量增加,调用逻辑变得臃肿混乱。

抽象工厂模式(Abstract Factory Pattern),正是为解决 “多产品族创建” 而生的设计模式:通过定义 “抽象工厂”,让一个工厂负责创建同一产品族的多个相关产品(如微信工厂同时创建支付、退款、对账),业务逻辑只需依赖一个抽象工厂,切换产品族时只需换一个工厂实例,无需修改任何业务代码。

这篇文章不会堆砌复杂的 UML 图,而是用 C++ 实战场景 + 可运行代码 + 真实踩坑经验,带你彻底吃透抽象工厂模式。不管你是刚接触设计模式的新手,还是想解决多产品族创建混乱的开发者,读完这篇,你都能写出 “产品族一键切换,扩展不碰老代码” 的优雅架构。

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

1.1 核心思想:“一个工厂,搞定一整套产品”

抽象工厂模式的核心是:定义一个抽象工厂接口,该接口负责创建一系列相关或相互依赖的产品(产品族),具体工厂实现这个接口,创建对应产品族的所有产品,业务逻辑通过依赖抽象工厂,实现产品族的一键切换

用一句大白话解释:抽象工厂是 “工厂的工厂”,它不只是创建一个产品,而是创建一整套配套产品。比如:

  • 手机品牌(抽象工厂):苹果工厂创建 iPhone(手机)+ AirPods(耳机)+ MagSafe(充电器)(同一产品族);华为工厂创建 Mate 系列(手机)+ FreeBuds(耳机)+ 华为充电器(同一产品族);
  • 业务逻辑(用户)只需选择 “苹果工厂” 或 “华为工厂”,就能获得一整套配套产品,不用分别找手机工厂、耳机工厂。

1.2 关键概念:产品族 vs 产品等级结构(必懂!)

理解抽象工厂模式的关键,是区分 “产品族” 和 “产品等级结构”,这也是它和工厂方法模式的核心区别:

  • 产品族:同一品牌 / 场景下的配套产品,比如 “微信支付 + 微信退款 + 微信对账”“Windows 按钮 + Windows 输入框 + Windows 下拉框”;
  • 产品等级结构:不同品牌 / 场景下的同一类产品,比如 “微信支付 + 支付宝支付 + 银联支付”“Windows 按钮 + Mac 按钮 + Linux 按钮”。

用表格更直观:

维度产品族(抽象工厂负责)产品等级结构(工厂方法负责)
核心关系配套使用(如支付 + 退款 + 对账)相互替代(如微信支付替代支付宝支付)
创建主体一个抽象工厂创建一整套产品一个具体工厂创建一个产品
适用场景需要切换整套配套产品需要切换单个替代产品

举个生活化例子:

  • 产品族:星巴克的 “咖啡 + 蛋糕 + 饮品”(配套购买);
  • 产品等级结构:不同品牌的咖啡(星巴克咖啡、瑞幸咖啡,相互替代)。

抽象工厂模式的目标,就是让 “切换产品族” 像换一套星巴克套餐一样简单,而不是分别换咖啡、蛋糕、饮品。

1.3 抽象工厂的 4 个核心角色(和工厂方法对比)

抽象工厂模式的角色和工厂方法类似,但职责扩展到了 “产品族”:

  • 抽象产品(Abstract Product):定义某一类产品的接口(如IPayIRefund),每个产品等级结构对应一个抽象产品;
  • 具体产品(Concrete Product):实现抽象产品的具体类(如WechatPayAlipayRefund),属于某个产品族;
  • 抽象工厂(Abstract Factory):定义创建同一产品族所有产品的接口(如IPayFactory包含createPay()createRefund()createReconciliation());
  • 具体工厂(Concrete Factory):实现抽象工厂接口,创建对应产品族的所有具体产品(如WechatFactory创建微信支付、退款、对账)。

它们的关系:抽象工厂→创建→同一产品族的多个抽象产品,具体工厂→创建→同一产品族的多个具体产品

1.4 和工厂方法模式的核心区别(避免混淆!)

很多开发者分不清抽象工厂和工厂方法,用一句话总结:

  • 工厂方法模式:“一厂一品”,解决 “单个产品的替代问题”(如支付方式替代);
  • 抽象工厂模式:“一厂多品(产品族)”,解决 “整套产品的切换问题”(如支付 + 退款 + 对账的整套切换)。

用表格对比更清晰:

特性工厂方法模式抽象工厂模式
工厂职责创建单个产品创建同一产品族的多个配套产品
产品关系相互替代(产品等级结构)相互配套(产品族)
扩展方式新增产品→新增具体工厂新增产品族→新增具体工厂;新增产品等级→修改抽象工厂(不推荐)
适用场景产品类型少,需灵活替代产品族明确,需整体切换整套产品
复杂度低(单个产品创建)中(多个产品创建,接口更复杂)

简单说:如果你的场景是 “换单个零件”,用工厂方法;如果是 “换一整套装备”,用抽象工厂。

二、C++ 反例实战:工厂方法模式处理多产品族的混乱

为了让你直观感受抽象工厂的价值,我们先用工厂方法模式实现一个 “支付 + 退款 + 对账” 的多产品族场景,看看它有多混乱。

2.1 需求背景

实现一个电商支付系统,核心需求:

  1. 支持微信、支付宝两个产品族,每个产品族包含 3 个产品:支付(Pay)、退款(Refund)、对账(Reconciliation);
  2. 业务逻辑需要同时使用同一产品族的 3 个产品(比如创建订单后,支付成功→后续可能退款→每日对账);
  3. 未来可能新增 “银联” 产品族,不新增产品类型(仍为支付、退款、对账)。

2.2 工厂方法模式的混乱实现

#include <iostream>
#include <string>
#include <memory>

// ======================== 抽象产品(3个产品等级结构)========================
// 抽象产品1:支付
class IPay {
public:
    virtual ~IPay() = default;
    virtual void pay(double amount) = 0;
};

// 抽象产品2:退款
class IRefund {
public:
    virtual ~IRefund() = default;
    virtual void refund(double amount) = 0;
};

// 抽象产品3:对账
class IReconciliation {
public:
    virtual ~IReconciliation() = default;
    virtual void reconcile() = 0;
};

// ======================== 具体产品(微信产品族)========================
// 微信支付
class WechatPay : public IPay {
public:
    void pay(double amount) override {
        std::cout << "[微信] 发起支付:" << amount << "元" << std::endl;
    }
};

// 微信退款
class WechatRefund : public IRefund {
public:
    void refund(double amount) override {
        std::cout << "[微信] 发起退款:" << amount << "元" << std::endl;
    }
};

// 微信对账
class WechatReconciliation : public IReconciliation {
public:
    void reconcile() override {
        std::cout << "[微信] 每日对账,核对交易流水" << std::endl;
    }
};

// ======================== 具体产品(支付宝产品族)========================
// 支付宝支付
class Alipay : public IPay {
public:
    void pay(double amount) override {
        std::cout << "[支付宝] 发起支付:" << amount << "元" << std::endl;
    }
};

// 支付宝退款
class AlipayRefund : public IRefund {
public:
    void refund(double amount) override {
        std::cout << "[支付宝] 发起退款:" << amount << "元" << std::endl;
    }
};

// 支付宝对账
class AlipayReconciliation : public IReconciliation {
public:
    void reconcile() override {
        std::cout << "[支付宝] 每日对账,核对交易流水" << std::endl;
    }
};

// ======================== 工厂方法(6个工厂,每个产品对应一个)========================
// 微信支付工厂
class WechatPayFactory {
public:
    std::unique_ptr<IPay> create() { return std::make_unique<WechatPay>(); }
};

// 微信退款工厂
class WechatRefundFactory {
public:
    std::unique_ptr<IRefund> create() { return std::make_unique<WechatRefund>(); }
};

// 微信对账工厂
class WechatReconciliationFactory {
public:
    std::unique_ptr<IReconciliation> create() { return std::make_unique<WechatReconciliation>(); }
};

// 支付宝支付工厂
class AlipayPayFactory {
public:
    std::unique_ptr<IPay> create() { return std::make_unique<Alipay>(); }
};

// 支付宝退款工厂
class AlipayRefundFactory {
public:
    std::unique_ptr<IRefund> create() { return std::make_unique<AlipayRefund>(); }
};

// 支付宝对账工厂
class AlipayReconciliationFactory {
public:
    std::unique_ptr<IReconciliation> create() { return std::make_unique<AlipayReconciliation>(); }
};

// ======================== 业务模块(订单系统)========================
class OrderSystem {
public:
    // 业务逻辑:支付→退款→对账(需要3个工厂)
    void processOrder(bool useWechat, double amount) {
        // 1. 创建对应产品族的3个工厂(混乱点1:工厂数量多,管理复杂)
        std::unique_ptr<IPayFactory> payFactory;
        std::unique_ptr<IRefundFactory> refundFactory;
        std::unique_ptr<IReconciliationFactory> reconFactory;

        if (useWechat) {
            payFactory = std::make_unique<WechatPayFactory>();
            refundFactory = std::make_unique<WechatRefundFactory>();
            reconFactory = std::make_unique<WechatReconciliationFactory>();
        } else {
            payFactory = std::make_unique<AlipayPayFactory>();
            refundFactory = std::make_unique<AlipayRefundFactory>();
            reconFactory = std::make_unique<AlipayReconciliationFactory>();
        }

        // 2. 创建产品(混乱点2:需要调用3个工厂的create方法)
        auto pay = payFactory->create();
        auto refund = refundFactory->create();
        auto recon = reconFactory->create();

        // 3. 业务逻辑
        pay->pay(amount);
        refund->refund(amount * 0.5); // 假设退一半
        recon->reconcile();
    }
};

// 主函数测试
int main() {
    OrderSystem orderSystem;

    // 微信产品族
    std::cout << "=== 测试微信产品族 ===" << std::endl;
    orderSystem.processOrder(true, 199.9);

    // 支付宝产品族
    std::cout << "\n=== 测试支付宝产品族 ===" << std::endl;
    orderSystem.processOrder(false, 299.9);

    return 0;
}

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

这段代码能实现功能,但在实际开发中会让维护者崩溃:

  1. 工厂泛滥:2 个产品族 + 3 个产品 = 6 个工厂,新增 “银联” 产品族需要再加 3 个工厂,工厂数量爆炸式增长;
  2. 切换繁琐:业务模块切换产品族时,需要修改 3 个工厂的创建逻辑(if-else里新增 3 行),违反开放封闭原则;
  3. 业务耦合:业务模块需要管理多个工厂,还要手动匹配 “同一产品族的工厂”,一旦配错(比如微信支付 + 支付宝退款),会导致逻辑错误;
  4. 扩展成本高:如果新增产品类型(如 “对账明细导出”),需要新增 1 个抽象产品 + 2 个具体产品 + 2 个工厂,所有业务模块都要同步修改,牵一发而动全身。

这就是多产品族场景下工厂方法模式的瓶颈 —— 它只能处理 “单个产品的替代”,无法应对 “整套产品的切换”。而抽象工厂模式,正是为解决这个问题而生。

三、C++ 重构实战:用抽象工厂模式打造 “一键切换产品族” 的支付系统

针对反例的混乱,我们用抽象工厂模式重构,核心思路是 **“抽象工厂负责创建同一产品族的所有产品,业务模块只依赖一个抽象工厂”**:

  1. 定义抽象工厂IPaymentFactory,包含创建支付、退款、对账 3 个产品的方法;
  2. 每个具体工厂(WechatFactoryAlipayFactory)实现抽象工厂,创建对应产品族的 3 个具体产品;
  3. 业务模块OrderSystem只依赖IPaymentFactory,切换产品族只需传入不同的具体工厂,无需修改业务代码。

3.1 重构后的完整代码

3.1.1 抽象产品(产品等级结构)
// IPay.h(抽象产品1:支付)
#ifndef I_PAY_H
#define I_PAY_H

#include <string>

class IPay {
public:
    virtual ~IPay() = default;
    virtual void pay(double amount) = 0;
    virtual std::string getPayName() const = 0;
};

#endif // I_PAY_H
// IRefund.h(抽象产品2:退款)
#ifndef I_REFUND_H
#define I_REFUND_H

#include <string>

class IRefund {
public:
    virtual ~IRefund() = default;
    virtual void refund(double amount) = 0;
    virtual std::string getRefundName() const = 0;
};

#endif // I_REFUND_H
// IReconciliation.h(抽象产品3:对账)
#ifndef I_RECONCILIATION_H
#define I_RECONCILIATION_H

#include <string>

class IReconciliation {
public:
    virtual ~IReconciliation() = default;
    virtual void reconcile() = 0;
    virtual std::string getReconName() const = 0;
};

#endif // I_RECONCILIATION_H
3.1.2 具体产品(微信产品族)
// WechatPay.h(微信支付)
#ifndef WECHAT_PAY_H
#define WECHAT_PAY_H

#include "IPay.h"

class WechatPay : public IPay {
public:
    void pay(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、签名校验等逻辑(简化)
}

std::string WechatPay::getPayName() const {
    return "微信支付";
}
// WechatRefund.h(微信退款)
#ifndef WECHAT_REFUND_H
#define WECHAT_REFUND_H

#include "IRefund.h"

class WechatRefund : public IRefund {
public:
    void refund(double amount) override;
    std::string getRefundName() const override;
};

#endif // WECHAT_REFUND_H
// WechatRefund.cpp
#include "WechatRefund.h"
#include <iostream>

void WechatRefund::refund(double amount) {
    std::cout << "[微信退款] 发起退款请求,金额:" << amount << "元" << std::endl;
    // 实际调用微信退款SDK逻辑(简化)
}

std::string WechatRefund::getRefundName() const {
    return "微信退款";
}
// WechatReconciliation.h(微信对账)
#ifndef WECHAT_RECONCILIATION_H
#define WECHAT_RECONCILIATION_H

#include "IReconciliation.h"

class WechatReconciliation : public IReconciliation {
public:
    void reconcile() override;
    std::string getReconName() const override;
};

#endif // WECHAT_RECONCILIATION_H
// WechatReconciliation.cpp
#include "WechatReconciliation.h"
#include <iostream>

void WechatReconciliation::reconcile() {
    std::cout << "[微信对账] 对接微信商户平台,核对今日交易流水" << std::endl;
}

std::string WechatReconciliation::getReconName() const {
    return "微信对账";
}
3.1.3 具体产品(支付宝产品族)
// Alipay.h(支付宝支付)
#ifndef ALIPAY_H
#define ALIPAY_H

#include "IPay.h"

class Alipay : public IPay {
public:
    void pay(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逻辑(简化)
}

std::string Alipay::getPayName() const {
    return "支付宝支付";
}
// AlipayRefund.h(支付宝退款)
#ifndef ALIPAY_REFUND_H
#define ALIPAY_REFUND_H

#include "IRefund.h"

class AlipayRefund : public IRefund {
public:
    void refund(double amount) override;
    std::string getRefundName() const override;
};

#endif // ALIPAY_REFUND_H
// AlipayRefund.cpp
#include "AlipayRefund.h"
#include <iostream>

void AlipayRefund::refund(double amount) {
    std::cout << "[支付宝退款] 发起退款请求,金额:" << amount << "元" << std::endl;
    // 实际调用支付宝退款SDK逻辑(简化)
}

std::string AlipayRefund::getRefundName() const {
    return "支付宝退款";
}
// AlipayReconciliation.h(支付宝对账)
#ifndef ALIPAY_RECONCILIATION_H
#define ALIPAY_RECONCILIATION_H

#include "IReconciliation.h"

class AlipayReconciliation : public IReconciliation {
public:
    void reconcile() override;
    std::string getReconName() const override;
};

#endif // ALIPAY_RECONCILIATION_H
// AlipayReconciliation.cpp
#include "AlipayReconciliation.h"
#include <iostream>

void AlipayReconciliation::reconcile() {
    std::cout << "[支付宝对账] 对接支付宝商户平台,核对今日交易流水" << std::endl;
}

std::string AlipayReconciliation::getReconName() const {
    return "支付宝对账";
}
3.1.4 抽象工厂与具体工厂
// IPaymentFactory.h(抽象工厂:创建支付产品族的3个产品)
#ifndef I_PAYMENT_FACTORY_H
#define I_PAYMENT_FACTORY_H

#include "IPay.h"
#include "IRefund.h"
#include "IReconciliation.h"
#include <memory>

// 抽象工厂:负责创建同一产品族的3个产品(支付、退款、对账)
class IPaymentFactory {
public:
    virtual ~IPaymentFactory() = default;
    virtual std::unique_ptr<IPay> createPay() = 0;
    virtual std::unique_ptr<IRefund> createRefund() = 0;
    virtual std::unique_ptr<IReconciliation> createReconciliation() = 0;
};

#endif // I_PAYMENT_FACTORY_H
// WechatFactory.h(具体工厂:微信产品族)
#ifndef WECHAT_FACTORY_H
#define WECHAT_FACTORY_H

#include "IPaymentFactory.h"
#include "WechatPay.h"
#include "WechatRefund.h"
#include "WechatReconciliation.h"

class WechatFactory : public IPaymentFactory {
public:
    std::unique_ptr<IPay> createPay() override {
        return std::make_unique<WechatPay>();
    }

    std::unique_ptr<IRefund> createRefund() override {
        return std::make_unique<WechatRefund>();
    }

    std::unique_ptr<IReconciliation> createReconciliation() override {
        return std::make_unique<WechatReconciliation>();
    }
};

#endif // WECHAT_FACTORY_H
// AlipayFactory.h(具体工厂:支付宝产品族)
#ifndef ALIPAY_FACTORY_H
#define ALIPAY_FACTORY_H

#include "IPaymentFactory.h"
#include "Alipay.h"
#include "AlipayRefund.h"
#include "AlipayReconciliation.h"

class AlipayFactory : public IPaymentFactory {
public:
    std::unique_ptr<IPay> createPay() override {
        return std::make_unique<Alipay>();
    }

    std::unique_ptr<IRefund> createRefund() override {
        return std::make_unique<AlipayRefund>();
    }

    std::unique_ptr<IReconciliation> createReconciliation() override {
        return std::make_unique<AlipayReconciliation>();
    }
};

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

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

class OrderSystem {
public:
    // 业务逻辑:只接收抽象工厂,不关心具体产品族
    void processOrder(IPaymentFactory& factory, double amount, const std::string& orderId) {
        std::cout << "=== 处理订单:" << orderId << " ===" << std::endl;

        // 1. 通过抽象工厂创建同一产品族的3个产品(无需关心具体是哪个产品族)
        auto pay = factory.createPay();
        auto refund = factory.createRefund();
        auto recon = factory.createReconciliation();

        // 2. 业务逻辑:支付→退款→对账
        std::cout << "步骤1:" << pay->getPayName() << std::endl;
        pay->pay(amount);

        std::cout << "步骤2:" << refund->getRefundName() << "(退50%)" << std::endl;
        refund->refund(amount * 0.5);

        std::cout << "步骤3:" << recon->getReconName() << std::endl;
        recon->reconcile();
    }
};

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

#include "IPay.h"

class UnionPay : public IPay {
public:
    void pay(double amount) override {
        std::cout << "[银联支付] 发起支付请求,金额:" << amount << "元" << std::endl;
    }
    std::string getPayName() const override { return "银联支付"; }
};

#endif // UNION_PAY_H
// UnionRefund.h(银联退款)
#ifndef UNION_REFUND_H
#define UNION_REFUND_H

#include "IRefund.h"

class UnionRefund : public IRefund {
public:
    void refund(double amount) override {
        std::cout << "[银联退款] 发起退款请求,金额:" << amount << "元" << std::endl;
    }
    std::string getRefundName() const override { return "银联退款"; }
};

#endif // UNION_REFUND_H
// UnionReconciliation.h(银联对账)
#ifndef UNION_RECONCILIATION_H
#define UNION_RECONCILIATION_H

#include "IReconciliation.h"

class UnionReconciliation : public IReconciliation {
public:
    void reconcile() override {
        std::cout << "[银联对账] 对接银联商户平台,核对今日交易流水" << std::endl;
    }
    std::string getReconName() const override { return "银联对账"; }
};

#endif // UNION_RECONCILIATION_H
// UnionFactory.h(银联工厂:新增产品族,无需修改老代码)
#ifndef UNION_FACTORY_H
#define UNION_FACTORY_H

#include "IPaymentFactory.h"
#include "UnionPay.h"
#include "UnionRefund.h"
#include "UnionReconciliation.h"

class UnionFactory : public IPaymentFactory {
public:
    std::unique_ptr<IPay> createPay() override { return std::make_unique<UnionPay>(); }
    std::unique_ptr<IRefund> createRefund() override { return std::make_unique<UnionRefund>(); }
    std::unique_ptr<IReconciliation> createReconciliation() override { return std::make_unique<UnionReconciliation>(); }
};

#endif // UNION_FACTORY_H
3.1.7 主函数测试
// main.cpp
#include <iostream>
#include "OrderSystem.h"
#include "WechatFactory.h"
#include "AlipayFactory.h"
#include "UnionFactory.h"

int main() {
    OrderSystem orderSystem;

    // 1. 测试微信产品族
    WechatFactory wechatFactory;
    orderSystem.processOrder(wechatFactory, 199.9, "ORDER001");

    // 2. 测试支付宝产品族(切换产品族只需换工厂)
    std::cout << "\n" << std::endl;
    AlipayFactory alipayFactory;
    orderSystem.processOrder(alipayFactory, 299.9, "ORDER002");

    // 3. 测试新增银联产品族(无需修改OrderSystem)
    std::cout << "\n" << std::endl;
    UnionFactory unionFactory;
    orderSystem.processOrder(unionFactory, 399.9, "ORDER003");

    return 0;
}

3.2 编译与运行说明

3.2.1 编译命令(GCC)
g++ -std=c++11 main.cpp WechatPay.cpp WechatRefund.cpp WechatReconciliation.cpp \
Alipay.cpp AlipayRefund.cpp AlipayReconciliation.cpp \
UnionPay.cpp UnionRefund.cpp UnionReconciliation.cpp -o payment_system
  • 依赖:C++11 及以上标准(支持std::unique_ptroverride);
  • 编译环境:Linux/GCC、Windows/MinGW、Mac/Clang 均可。
3.2.2 运行结果
=== 处理订单:ORDER001 ===
步骤1:微信支付
[微信支付] 发起支付请求,金额:199.9元
步骤2:微信退款(退50%)
[微信退款] 发起退款请求,金额:99.95元
步骤3:微信对账
[微信对账] 对接微信商户平台,核对今日交易流水


=== 处理订单:ORDER002 ===
步骤1:支付宝支付
[支付宝支付] 发起支付请求,金额:299.9元
步骤2:支付宝退款(退50%)
[支付宝退款] 发起退款请求,金额:149.95元
步骤3:支付宝对账
[支付宝对账] 对接支付宝商户平台,核对今日交易流水


=== 处理订单:ORDER003 ===
步骤1:银联支付
[银联支付] 发起支付请求,金额:399.9元
步骤2:银联退款(退50%)
[银联退款] 发起退款请求,金额:199.95元
步骤3:银联对账
[银联对账] 对接银联商户平台,核对今日交易流水

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

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

  1. 工厂数量精简:1 个产品族对应 1 个工厂,2 个产品族 + 新增银联 = 3 个工厂,而非 6 个,管理成本大幅降低;
  2. 一键切换产品族:业务模块切换产品族只需换工厂实例(如WechatFactoryAlipayFactory),无需修改任何业务代码,符合开放封闭原则;
  3. 产品族自动匹配:抽象工厂确保创建的产品属于同一产品族(如微信工厂只创建微信相关产品),避免业务模块配错产品的风险;
  4. 扩展成本低:新增产品族(如银联)只需新增 1 个具体工厂 + 对应产品,老代码(抽象工厂、业务模块)一行不动;
  5. 职责清晰:抽象工厂负责 “创建同一产品族的多个产品”,业务模块负责 “使用产品”,创建逻辑与业务逻辑彻底分离,代码可读性和维护性大幅提升。

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

抽象工厂模式的核心价值是 “切换产品族”,在 C++ 开发中,凡是需要 “整套配套产品切换” 的场景,都能发挥其优势。下面通过 3 个真实场景,展示模式的灵活性。

4.1 场景 1:UI 组件库(跨平台界面切换)

需求:开发跨平台应用,支持 Windows 和 Mac 两套 UI 组件(按钮、输入框、下拉框),可一键切换平台。
抽象工厂实现:
// ======================== 抽象产品(UI组件族)========================
// 抽象产品1:按钮
class IButton {
public:
    virtual ~IButton() = default;
    virtual void render() = 0; // 渲染按钮
};

// 抽象产品2:输入框
class IInput {
public:
    virtual ~IInput() = default;
    virtual void render() = 0; // 渲染输入框
};

// 抽象产品3:下拉框
class IDropdown {
public:
    virtual ~IDropdown() = default;
    virtual void render() = 0; // 渲染下拉框
};

// ======================== 具体产品(Windows UI族)========================
class WindowsButton : public IButton {
public:
    void render() override { std::cout << "渲染Windows风格按钮(灰色边框,方形)" << std::endl; }
};

class WindowsInput : public IInput {
public:
    void render() override { std::cout << "渲染Windows风格输入框(白色背景,黑色文字)" << std::endl; }
};

class WindowsDropdown : public IDropdown {
public:
    void render() override { std::cout << "渲染Windows风格下拉框(点击展开,蓝色选中)" << std::endl; }
};

// ======================== 具体产品(Mac UI族)========================
class MacButton : public IButton {
public:
    void render() override { std::cout << "渲染Mac风格按钮(无边框,圆角)" << std::endl; }
};

class MacInput : public IInput {
public:
    void render() override { std::cout << "渲染Mac风格输入框(浅灰色背景,黑色文字)" << std::endl; }
};

class MacDropdown : public IDropdown {
public:
    void render() override { std::cout << "渲染Mac风格下拉框(hover展开,灰色选中)" << std::endl; }
};

// ======================== 抽象工厂与具体工厂 ========================
// 抽象工厂:UI组件工厂
class IUIFactory {
public:
    virtual ~IUIFactory() = default;
    virtual std::unique_ptr<IButton> createButton() = 0;
    virtual std::unique_ptr<IInput> createInput() = 0;
    virtual std::unique_ptr<IDropdown> createDropdown() = 0;
};

// Windows UI工厂
class WindowsUIFactory : public IUIFactory {
public:
    std::unique_ptr<IButton> createButton() override { return std::make_unique<WindowsButton>(); }
    std::unique_ptr<IInput> createInput() override { return std::make_unique<WindowsInput>(); }
    std::unique_ptr<IDropdown> createDropdown() override { return std::make_unique<WindowsDropdown>(); }
};

// Mac UI工厂
class MacUIFactory : public IUIFactory {
public:
    std::unique_ptr<IButton> createButton() override { return std::make_unique<MacButton>(); }
    std::unique_ptr<IInput> createInput() override { return std::make_unique<MacInput>(); }
    std::unique_ptr<IDropdown> createDropdown() override { return std::make_unique<MacDropdown>(); }
};

// ======================== 业务模块:应用渲染 ========================
class App {
public:
    void renderUI(IUIFactory& factory) {
        std::cout << "=== 渲染应用UI ===" << std::endl;
        auto button = factory.createButton();
        auto input = factory.createInput();
        auto dropdown = factory.createDropdown();

        button->render();
        input->render();
        dropdown->render();
    }
};

// 测试代码
int main() {
    App app;

    // 渲染Windows风格UI
    std::cout << "【Windows平台】" << std::endl;
    WindowsUIFactory windowsFactory;
    app.renderUI(windowsFactory);

    // 一键切换到Mac风格UI
    std::cout << "\n【Mac平台】" << std::endl;
    MacUIFactory macFactory;
    app.renderUI(macFactory);

    return 0;
}

运行结果

【Windows平台】
=== 渲染应用UI ===
渲染Windows风格按钮(灰色边框,方形)
渲染Windows风格输入框(白色背景,黑色文字)
渲染Windows风格下拉框(点击展开,蓝色选中)

【Mac平台】
=== 渲染应用UI ===
渲染Mac风格按钮(无边框,圆角)
渲染Mac风格输入框(浅灰色背景,黑色文字)
渲染Mac风格下拉框(hover展开,灰色选中)

优势:新增 Linux 平台 UI 时,只需新增LinuxUIFactory和对应的 3 个组件,应用渲染逻辑App完全不用改,实现 “一键切换平台 UI”。

4.2 场景 2:数据库访问层(多数据库切换)

需求:支持 MySQL 和 PostgreSQL 两种数据库,每种数据库包含连接、查询、事务 3 个配套操作,可动态切换数据库。
抽象工厂实现:
// ======================== 抽象产品(数据库操作族)========================
// 抽象产品1:数据库连接
class IDbConnection {
public:
    virtual ~IDbConnection() = default;
    virtual void connect() = 0; // 建立连接
};

// 抽象产品2:数据库查询
class IDbQuery {
public:
    virtual ~IDbQuery() = default;
    virtual void execute(const std::string& sql) = 0; // 执行查询
};

// 抽象产品3:数据库事务
class IDbTransaction {
public:
    virtual ~IDbTransaction() = default;
    virtual void begin() = 0;  // 开始事务
    virtual void commit() = 0; // 提交事务
    virtual void rollback() = 0; // 回滚事务
};

// ======================== 具体产品(MySQL族)========================
class MysqlConnection : public IDbConnection {
public:
    void connect() override { std::cout << "MySQL:建立数据库连接(mysql_real_connect)" << std::endl; }
};

class MysqlQuery : public IDbQuery {
public:
    void execute(const std::string& sql) override {
        std::cout << "MySQL:执行SQL:" << sql << "(mysql_query)" << std::endl;
    }
};

class MysqlTransaction : public IDbTransaction {
public:
    void begin() override { std::cout << "MySQL:开始事务(SET AUTOCOMMIT=0)" << std::endl; }
    void commit() override { std::cout << "MySQL:提交事务(COMMIT)" << std::endl; }
    void rollback() override { std::cout << "MySQL:回滚事务(ROLLBACK)" << std::endl; }
};

// ======================== 具体产品(PostgreSQL族)========================
class PgConnection : public IDbConnection {
public:
    void connect() override { std::cout << "PostgreSQL:建立数据库连接(PQconnectdb)" << std::endl; }
};

class PgQuery : public IDbQuery {
public:
    void execute(const std::string& sql) override {
        std::cout << "PostgreSQL:执行SQL:" << sql << "(PQexec)" << std::endl;
    }
};

class PgTransaction : public IDbTransaction {
public:
    void begin() override { std::cout << "PostgreSQL:开始事务(BEGIN)" << std::endl; }
    void commit() override { std::cout << "PostgreSQL:提交事务(COMMIT)" << std::endl; }
    void rollback() override { std::cout << "PostgreSQL:回滚事务(ROLLBACK)" << std::endl; }
};

// ======================== 抽象工厂与具体工厂 ========================
class IDbFactory {
public:
    virtual ~IDbFactory() = default;
    virtual std::unique_ptr<IDbConnection> createConnection() = 0;
    virtual std::unique_ptr<IDbQuery> createQuery() = 0;
    virtual std::unique_ptr<IDbTransaction> createTransaction() = 0;
};

class MysqlFactory : public IDbFactory {
public:
    std::unique_ptr<IDbConnection> createConnection() override { return std::make_unique<MysqlConnection>(); }
    std::unique_ptr<IDbQuery> createQuery() override { return std::make_unique<MysqlQuery>(); }
    std::unique_ptr<IDbTransaction> createTransaction() override { return std::make_unique<MysqlTransaction>(); }
};

class PgFactory : public IDbFactory {
public:
    std::unique_ptr<IDbConnection> createConnection() override { return std::make_unique<PgConnection>(); }
    std::unique_ptr<IDbQuery> createQuery() override { return std::make_unique<PgQuery>(); }
    std::unique_ptr<IDbTransaction> createTransaction() override { return std::make_unique<PgTransaction>(); }
};

// ======================== 业务模块:数据访问服务 ========================
class DataService {
public:
    void processData(IDbFactory& factory) {
        std::cout << "=== 处理数据 ===" << std::endl;
        // 1. 创建数据库操作族
        auto conn = factory.createConnection();
        auto query = factory.createQuery();
        auto tx = factory.createTransaction();

        // 2. 业务逻辑:连接→事务→查询→提交
        conn->connect();
        tx->begin();
        query->execute("SELECT * FROM users WHERE id=1001");
        tx->commit();
    }
};

// 测试代码
int main() {
    DataService dataService;

    // 使用MySQL
    std::cout << "【使用MySQL】" << std::endl;
    MysqlFactory mysqlFactory;
    dataService.processData(mysqlFactory);

    // 切换到PostgreSQL
    std::cout << "\n【使用PostgreSQL】" << std::endl;
    PgFactory pgFactory;
    dataService.processData(pgFactory);

    return 0;
}

运行结果

【使用MySQL】
=== 处理数据 ===
MySQL:建立数据库连接(mysql_real_connect)
MySQL:开始事务(SET AUTOCOMMIT=0)
MySQL:执行SQL:SELECT * FROM users WHERE id=1001(mysql_query)
MySQL:提交事务(COMMIT)

【使用PostgreSQL】
=== 处理数据 ===
PostgreSQL:建立数据库连接(PQconnectdb)
PostgreSQL:开始事务(BEGIN)
PostgreSQL:执行SQL:SELECT * FROM users WHERE id=1001(PQexec)
PostgreSQL:提交事务(COMMIT)

优势:业务模块无需关心数据库的具体实现(如 MySQL 的mysql_query和 PostgreSQL 的PQexec),切换数据库只需换工厂,实现 “数据库无关” 的设计。

4.3 场景 3:游戏装备系统(职业装备切换)

需求:游戏支持战士和法师两种职业,每种职业有配套的武器、防具、技能,切换职业时自动切换整套装备。
抽象工厂实现:
// ======================== 抽象产品(装备族)========================
// 抽象产品1:武器
class IWeapon {
public:
    virtual ~IWeapon() = default;
    virtual void attack() = 0; // 武器攻击
};

// 抽象产品2:防具
class IArmor {
public:
    virtual ~IArmor() = default;
    virtual void defend() = 0; // 防具防御
};

// 抽象产品3:技能
class ISkill {
public:
    virtual ~ISkill() = default;
    virtual void cast() = 0; // 释放技能
};

// ======================== 具体产品(战士装备族)========================
class WarriorSword : public IWeapon {
public:
    void attack() override { std::cout << "战士武器:青铜剑,造成100点物理伤害" << std::endl; }
};

class WarriorShield : public IArmor {
public:
    void defend() override { std::cout << "战士防具:铁盾,减免50点物理伤害" << std::endl; }
};

class WarriorSlash : public ISkill {
public:
    void cast() override { std::cout << "战士技能:横斩,对前方敌人造成150点物理伤害" << std::endl; }
};

// ======================== 具体产品(法师装备族)========================
class MageWand : public IWeapon {
public:
    void attack() override { std::cout << "法师武器:魔法杖,造成80点魔法伤害" << std::endl; }
};

class MageRobe : public IArmor {
public:
    void defend() override { std::cout << "法师防具:魔法袍,减免30点魔法伤害" << std::endl; }
};

class MageFireball : public ISkill {
public:
    void cast() override { std::cout << "法师技能:火球术,对单个敌人造成200点魔法伤害" << std::endl; }
};

// ======================== 抽象工厂与具体工厂 ========================
class IEquipmentFactory {
public:
    virtual ~IEquipmentFactory() = default;
    virtual std::unique_ptr<IWeapon> createWeapon() = 0;
    virtual std::unique_ptr<IArmor> createArmor() = 0;
    virtual std::unique_ptr<ISkill> createSkill() = 0;
};

class WarriorFactory : public IEquipmentFactory {
public:
    std::unique_ptr<IWeapon> createWeapon() override { return std::make_unique<WarriorSword>(); }
    std::unique_ptr<IArmor> createArmor() override { return std::make_unique<WarriorShield>(); }
    std::unique_ptr<ISkill> createSkill() override { return std::make_unique<WarriorSlash>(); }
};

class MageFactory : public IEquipmentFactory {
public:
    std::unique_ptr<IWeapon> createWeapon() override { return std::make_unique<MageWand>(); }
    std::unique_ptr<IArmor> createArmor() override { return std::make_unique<MageRobe>(); }
    std::unique_ptr<ISkill> createSkill() override { return std::make_unique<MageFireball>(); }
};

// ======================== 业务模块:游戏角色 ========================
class GameCharacter {
public:
    void equip(IEquipmentFactory& factory, const std::string& roleName) {
        std::cout << "=== " << roleName << "装备整套装备 ===" << std::endl;
        auto weapon = factory.createWeapon();
        auto armor = factory.createArmor();
        auto skill = factory.createSkill();

        weapon->attack();
        armor->defend();
        skill->cast();
    }
};

// 测试代码
int main() {
    GameCharacter character;

    // 战士装备
    WarriorFactory warriorFactory;
    character.equip(warriorFactory, "战士");

    // 切换法师装备
    std::cout << "\n" << std::endl;
    MageFactory mageFactory;
    character.equip(mageFactory, "法师");

    return 0;
}

运行结果

=== 战士装备整套装备 ===
战士武器:青铜剑,造成100点物理伤害
战士防具:铁盾,减免50点物理伤害
战士技能:横斩,对前方敌人造成150点物理伤害


=== 法师装备整套装备 ===
法师武器:魔法杖,造成80点魔法伤害
法师防具:魔法袍,减免30点魔法伤害
法师技能:火球术,对单个敌人造成200点魔法伤害

优势:新增 “牧师” 职业时,只需新增PriestFactory和对应的武器、防具、技能,游戏角色逻辑完全不变,实现 “职业一键切换装备”。

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

抽象工厂模式虽然强大,但在实际应用中容易因理解偏差导致 “过度设计” 或 “使用不当”。下面列出 5 个典型误区及避坑方案。

误区 1:混淆 “产品族” 和 “产品等级结构”

比如把 “微信支付” 和 “支付宝支付” 当成产品族(实际是产品等级结构),把 “支付” 和 “退款” 当成产品等级结构(实际是产品族)—— 导致抽象工厂接口设计错误,无法实现产品族切换。

避坑方案

  • 产品族:同一场景下的 “配套产品”(如支付 + 退款 + 对账),必须一起使用;
  • 产品等级结构:不同场景下的 “替代产品”(如微信支付 + 支付宝支付),只能选一个使用;
  • 定义抽象工厂前,先明确 “哪些产品是配套的”,再设计抽象工厂的创建方法。

误区 2:抽象工厂接口过于臃肿

比如抽象工厂包含createPay()createRefund()createReconciliation()createLog()createConfig()等十几个方法,涵盖不相关的产品,导致具体工厂被迫实现不需要的方法。

避坑方案

  • 遵循 “接口单一职责”:一个抽象工厂只负责一个产品族的创建,不包含无关产品;
  • 若有多个不相关的产品族,拆分多个抽象工厂(如IPaymentFactory负责支付族,ILogFactory负责日志族),避免 “万能工厂”。

误区 3:滥用抽象工厂模式,简单场景过度设计

比如只是创建单个产品(如仅支付功能),没有产品族,却强行用抽象工厂模式,导致代码复杂度增加(抽象工厂 + 具体工厂 + 抽象产品 + 具体产品)。

避坑方案

  • 单个产品替代场景:用工厂方法模式;
  • 多产品族切换场景:用抽象工厂模式;
  • 简单对象(无复杂创建逻辑):直接new,无需设计模式。

误区 4:新增产品等级结构时修改抽象工厂

比如抽象工厂IPaymentFactory包含createPay()createRefund(),新增产品等级结构 “对账明细导出” 时,直接在抽象工厂中加createReconExport()—— 导致所有具体工厂都要同步修改,违反开放封闭原则。

避坑方案

  • 抽象工厂模式适合 “产品族稳定,产品等级结构不新增” 的场景;
  • 若必须新增产品等级结构,可通过 “扩展抽象工厂” 实现(如IPaymentFactoryV2继承IPaymentFactory,新增createReconExport()),避免修改原有抽象工厂。

误区 5:忽视 C++ 的智能指针和依赖注入

比如抽象工厂的创建方法返回原始指针,导致内存泄漏;业务模块直接new具体工厂,硬编码产品族,无法动态切换。

避坑方案

  • std::unique_ptr/std::shared_ptr管理产品和工厂的生命周期,避免内存泄漏;
  • 业务模块通过 “依赖注入” 获取抽象工厂(如构造函数传入),不硬编码具体工厂,支持动态切换产品族。

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

6.1 核心要点提炼

  1. 核心思想:抽象工厂负责创建同一产品族的多个配套产品,业务模块依赖抽象工厂,实现产品族一键切换;
  2. 关键概念:产品族(配套使用)vs 产品等级结构(相互替代),这是与工厂方法模式的核心区别;
  3. 4 个角色:抽象产品(产品等级结构)、具体产品(产品族成员)、抽象工厂(产品族创建接口)、具体工厂(产品族创建实现);
  4. 核心价值:解耦产品族创建与业务逻辑,支持整套产品切换,符合开放封闭原则;
  5. 适用场景:跨平台 UI、多数据库切换、游戏职业装备、配套工具集等需要 “整套产品切换” 的场景。

6.2 C++ 实战建议

  1. 抽象设计

    • 抽象产品用纯虚类定义,包含核心业务方法,不包含具体实现;
    • 抽象工厂只定义同一产品族的创建方法,方法名统一(如createXxx()),返回抽象产品的智能指针;
    • 具体工厂实现抽象工厂,确保创建的产品属于同一产品族,不混合其他产品族的产品。
  2. 具体实现

    • 产品需要初始化参数时,通过具体工厂的构造函数传递(如FileLoggerFactory接收日志路径);
    • 产品族的创建逻辑封装在具体工厂中,业务模块无需关心产品的创建细节(如 SDK 调用、配置初始化)。
  3. 业务调用

    • 业务模块只依赖抽象工厂,不依赖具体工厂和具体产品;
    • 通过依赖注入获取抽象工厂,支持动态切换产品族(如从配置文件读取产品族类型,创建对应工厂)。
  4. 与其他模式结合

    • 结合单例模式:无状态的具体工厂设计为单例(如WechatFactory),避免重复创建;
    • 结合依赖注入框架:大型项目中,用依赖注入框架(如 Boost.DI)管理工厂的创建和注入;
    • 结合原型模式:产品创建成本高时,工厂先创建原型,再通过克隆生成产品,提升效率。

6.3 最后一句话

抽象工厂模式的本质,是 “用抽象隔离产品族的创建变化”。它不只是工厂方法模式的 “升级版”,而是针对 “多产品族切换” 的专属解决方案 —— 当你发现工厂方法模式导致工厂泛滥、产品族管理混乱时,抽象工厂模式就是最优解。

记住:设计模式的价值不是 “炫技”,而是 “解决实际问题”。选择设计模式时,先明确场景(是单个产品替代,还是多产品族切换),再选择合适的模式,避免为了模式而模式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值