抽象工厂模式实战:用C++打造家具生产系统(附UML图与完整代码)

抽象工厂模式实战:用C++打造家具生产系统(附UML图与完整代码)

大家好,今天我们深入探讨设计模式中的"抽象工厂模式"。在面向对象设计中,"封装对象创建逻辑"是降低耦合、提升扩展性的核心思路,而抽象工厂模式正是针对"一系列相关或依赖对象创建"场景的经典解决方案。本文将以"家具生产"为生活案例,结合完整C++代码与UML图,带你从理论到实践,吃透抽象工厂模式的设计逻辑与落地方法。

一、抽象工厂模式的核心定义与生活案例

抽象工厂模式的核心思想是:提供一个接口,用于创建一系列相互关联或依赖的对象,而无需指定它们具体的类

生活中最典型的场景就是"家具套装选购":我们装修时,通常会选择统一风格的家具------比如"现代风格套装"包含现代沙发、现代椅子,"古典风格套装"包含古典沙发、古典椅子。很少会把现代沙发和古典椅子混搭,因为这会破坏风格统一性。

这里的"现代风格""古典风格"就是产品族 (一系列相关对象),"沙发""椅子"则是产品等级结构(同一类产品的不同实现)。抽象工厂模式的本质,就是将同一产品族的创建逻辑封装到专属工厂中,让客户端能快速获取风格一致的产品,同时彻底屏蔽具体产品的创建细节。

二、抽象工厂模式的核心角色(附UML类图)

抽象工厂模式包含5个核心角色,各角色职责清晰、分工明确。我们结合"家具生产"场景与代码,先通过UML图理清角色间的协作关系:

1. 抽象工厂模式UML类图(家具生产场景)

2. 核心角色解析(对应代码与场景)

角色名称 代码中的类/对象 核心职责
抽象工厂(Abstract Factory) Factory 定义创建同一产品族所有产品的接口,如createSofa()用于创建沙发,createChair()用于创建椅子,约束具体工厂的实现规范
具体工厂(Concrete Factory) ModernFactory/ClassicalFactory 实现抽象工厂接口,负责创建某一产品族的具体产品------比如ModernFactory专门生产现代风格的沙发和椅子,ClassicalFactory专门生产古典风格的家具
抽象产品(Abstract Product) Sofa/Chair 定义某一产品等级结构的统一接口,是所有具体产品的基类------Sofa是所有沙发的抽象,Chair是所有椅子的抽象
具体产品(Concrete Product) ModernSofa/ClassicalChair等类 实现抽象产品接口,是抽象工厂最终创建的实例,如ModernSofa是现代沙发的具体实现,ClassicalChair是古典椅子的具体实现
客户端(Client) main函数 通过抽象工厂接口获取产品,无需关心具体工厂和产品的实现细节------只需输入"modern"或"classical",就能获取对应风格的家具套装

三、不使用抽象工厂模式的问题:传统实现与工厂方法的局限

在处理多产品族、多产品等级的场景时,不使用抽象工厂模式会面临明显缺陷。我们通过对比传统硬编码和工厂方法模式,来明确抽象工厂模式的必要性。

1. 传统硬编码实现的痛点

直接在客户端创建具体产品,是最直观但最不可取的方式:

cpp 复制代码
int main() {
    vector<unique_ptr<Sofa>> sofas;
    vector<unique_ptr<Chair>> chairs;
    
    // 客户端需手动判断风格,逐一创建产品
    string style = "modern";
    if (style == "modern") {
        sofas.push_back(make_unique<ModernSofa>());
        chairs.push_back(make_unique<ModernChair>());
    } else {
        sofas.push_back(make_unique<ClassicalSofa>());
        chairs.push_back(make_unique<ClassicalChair>());
    }
    return 0;
}

这种方式的问题显而易见:

  • 风格一致性无法保证 :客户端可能误将ModernSofaClassicalChair搭配;
  • 耦合度极高:客户端直接依赖所有具体产品类,产品类的任何修改都会影响客户端;
  • 扩展性为零:新增风格或产品类型时,需大面积修改客户端代码。

2. 工厂方法模式的局限

工厂方法模式通过"一个工厂对应一个产品"的方式解耦了客户端与具体产品,但在处理产品族时仍有局限:

cpp 复制代码
// 工厂方法模式:为每个产品创建专属工厂
class SofaFactory {
public:
    virtual Sofa* createSofa() = 0;
};
class ModernSofaFactory : public SofaFactory {
public:
    Sofa* createSofa() override { return new ModernSofa; }
};
class ClassicalSofaFactory : public SofaFactory {
public:
    Sofa* createSofa() override { return new ClassicalSofa; }
};

// 椅子工厂类似,此处省略...

// 客户端使用
int main() {
    // 创建现代风格家具需同时操作两个工厂
    SofaFactory* sofaFactory = new ModernSofaFactory;
    ChairFactory* chairFactory = new ModernChairFactory;
    sofaFactory->createSofa();
    chairFactory->createChair();
    // ...
}

工厂方法模式的问题在于:

  • 无法保证产品族一致性 :客户端需手动协调SofaFactoryChairFactory的风格,仍可能出现混搭;
  • 工厂类数量爆炸:每增加一个产品类型或风格,需新增大量工厂类(如4种产品×2种风格=8个工厂);
  • 客户端逻辑复杂:创建一套家具需操作多个工厂,增加使用成本。

3. 抽象工厂模式的优势对比

抽象工厂模式通过"一个工厂对应一个产品族"的设计,完美解决了上述问题:

  • 天然保证风格一致ModernFactory只生产现代风格产品,避免混搭;
  • 工厂数量精简:N种风格只需N个工厂,而非N×M个(M为产品类型);
  • 客户端逻辑简化:获取一套家具只需操作一个工厂,屏蔽多产品创建的复杂性。

四、代码实战:抽象工厂模式实现家具生产系统

你的代码完美落地了抽象工厂模式的核心逻辑,我们逐部分解析,看它如何解决传统实现的痛点:

1. 步骤1:定义抽象产品与具体产品(构建产品等级结构)

首先创建SofaChair两个抽象产品类,再实现不同风格的具体产品,明确产品等级结构的接口与实现:

cpp 复制代码
#include<iostream>
#include<vector>
#include<memory>
using namespace std;

// 抽象产品1:沙发(定义沙发的统一接口)
class Sofa { };

// 具体产品1-1:现代风格沙发(实现抽象沙发接口)
class ModernSofa:public Sofa {
public:
    ModernSofa() { cout<<"modern sofa"<<endl; }
};

// 具体产品1-2:古典风格沙发(实现抽象沙发接口)
class ClassicalSofa:public Sofa {
public:
    ClassicalSofa() { cout<<"classical sofa"<<endl; }
};

// 抽象产品2:椅子(定义椅子的统一接口)
class Chair { };

// 具体产品2-1:现代风格椅子(实现抽象椅子接口)
class ModernChair:public Chair {
public:
    ModernChair() { cout<<"modern chair"<<endl; }
};

// 具体产品2-2:古典风格椅子(实现抽象椅子接口)
class ClassicalChair:public Chair {
public:
    ClassicalChair() { cout<<"classical chair"<<endl; }
};

2. 步骤2:定义抽象工厂与具体工厂(封装产品族创建逻辑)

抽象工厂Factory定义创建"沙发+椅子"产品族的接口,具体工厂则实现对应风格的产品创建逻辑,确保同一工厂产出的产品风格统一:

cpp 复制代码
// 抽象工厂:定义创建家具产品族的接口
class Factory {
public:
    // 创建沙发(对应抽象产品1)
    virtual Sofa* createSofa() = 0;
    // 创建椅子(对应抽象产品2)
    virtual Chair* createChair() = 0;
    // 虚析构:确保具体工厂对象销毁时能正确调用自身析构函数
    virtual ~Factory() = default;
};

// 具体工厂1:现代风格家具工厂(生产现代产品族)
class ModernFactory:public Factory {
public:
    Sofa* createSofa() override {
        return new ModernSofa; // 生产现代沙发
    }
    Chair* createChair() override {
        return new ModernChair; // 生产现代椅子
    }
};

// 具体工厂2:古典风格家具工厂(生产古典产品族)
class ClassicalFactory:public Factory {
public:
    Sofa* createSofa() override {
        return new ClassicalSofa; // 生产古典沙发
    }
    Chair* createChair() override {
        return new ClassicalChair; // 生产古典椅子
    }
};

3. 步骤3:客户端调用(简化产品获取逻辑)

客户端只需根据需求选择具体工厂,通过抽象工厂接口获取产品,无需关心具体产品的创建细节,同时保证风格一致性:

cpp 复制代码
int main() {
    int number;
    cin >> number;
    vector<unique_ptr<Sofa>> sofas;
    vector<unique_ptr<Chair>> chairs;
    unique_ptr<Factory> factory; // 依赖抽象工厂,不直接依赖具体工厂
    
    for (int i = 0; i < number; i++) {
        string furnitureType;
        cin >> furnitureType;
        
        // 根据风格选择具体工厂,确保产品族统一
        if (furnitureType == "modern") {
            factory = make_unique<ModernFactory>();
        } else {
            factory = make_unique<ClassicalFactory>();
        }
        
        // 通过抽象工厂接口获取产品,屏蔽具体产品创建细节
        unique_ptr<Chair> it1(factory->createChair());
        unique_ptr<Sofa> it2(factory->createSofa());
        
        chairs.push_back(move(it1));
        sofas.push_back(move(it2));
    }
    return 0;
}

4. 代码运行示例

输入(生产2套家具,1套现代风格、1套古典风格):

复制代码
2
modern
classical

输出(产品族风格统一,创建逻辑清晰):

复制代码
modern chair
modern sofa
classical chair
classical sofa

五、抽象工厂模式的优缺点

1. 优点

  • 保证产品族一致性:同一工厂创建的产品天然属于同一风格,避免客户端误搭配,无需手动校验风格;
  • 降低耦合度:客户端仅与抽象工厂、抽象产品交互,不依赖具体工厂和具体产品,代码灵活性更高;
  • 扩展性强(产品族维度) :新增产品族(如北欧风格)时,只需新增NordicFactory和对应具体产品(NordicSofaNordicChair),无需修改现有代码;
  • 便于产品族切换 :切换风格只需更换具体工厂(如从ModernFactory改为ClassicalFactory),客户端核心逻辑无需改动。

2. 缺点

  • 扩展产品等级结构复杂 :若新增产品类型(如"桌子"),需修改所有工厂类(抽象工厂Factory、具体工厂ModernFactory等),违反"开闭原则";
  • 类数量激增 :每增加一个产品族或一个产品等级,都需新增多个类------比如新增北欧风格,需额外创建NordicFactoryNordicSofaNordicChair三个类,增加系统理解成本。

六、抽象工厂模式的应用场景

以下场景特别适合使用抽象工厂模式:

  1. 需要统一产品族风格:如家具套装、电子设备套装(手机+耳机+平板同品牌)、UI组件库(按钮+输入框+下拉框同主题);
  2. 客户端不关心产品创建细节:如框架开发中,为用户提供"一键获取整套组件"的接口,无需用户手动创建每个组件;
  3. 产品族稳定,产品等级结构变化少:如家电行业(冰箱、洗衣机、空调同品牌),产品类型相对固定,新增品牌(产品族)的需求更频繁。

七、总结

抽象工厂模式的核心是"抽象工厂定规范,具体工厂产同族"。通过家具生产系统的案例,我们能清晰看到:它将产品族的创建逻辑封装成独立工厂,既解决了传统硬编码中"风格混乱""耦合度高"的问题,又弥补了工厂方法模式在处理多产品协同创建时的不足。

需要注意的是,抽象工厂模式并非万能------它更适合产品族稳定的场景。若产品等级结构频繁变化(如频繁新增家具类型),可结合"工厂方法模式"进一步优化。希望本文能帮你掌握抽象工厂模式的精髓,在实际项目中灵活运用。

本文部分设计思想与术语参考自《大话设计模式》。

相关推荐
是店小二呀2 小时前
DanceGRPO+FLUX:多模态生成强化学习模型的高效
设计模式
梁下轻语的秋缘2 小时前
用 LoRa + W5500 做一个无线呼叫器
c语言·c++
YoungHong19922 小时前
C++ 硬核基础:为什么函数重载不能只看返回值?
开发语言·c++
明洞日记2 小时前
【设计模式手册022】抽象工厂模式 - 创建产品家族
java·设计模式·抽象工厂模式
我不会插花弄玉2 小时前
vector【由浅入深-C++】
c++
兵哥工控2 小时前
mfc静态文本控件背景及字体颜色设置实例
c++·mfc
hqzing2 小时前
C语言程序调用syscall的几种方式
linux·c++
迟熙2 小时前
你的return,真的return对了吗?
c++
superman超哥2 小时前
仓颉内存分配优化深度解析
c语言·开发语言·c++·python·仓颉