认识设计模式——工厂模式

目录

🔍为什么需要工厂模式?

1️⃣第一种:简单工厂模式(最常用)

[1. 核心定义](#1. 核心定义)

[2. 通俗类比](#2. 通俗类比)

[3. 完整代码实现](#3. 完整代码实现)

[4. 打印结果](#4. 打印结果)

[5. 优缺点](#5. 优缺点)

优点

缺点

[6. 适用场景](#6. 适用场景)

2️⃣第二种:工厂方法模式(遵循开闭原则)

[1. 核心定义](#1. 核心定义)

[2. 通俗类比](#2. 通俗类比)

[3. 完整代码实现](#3. 完整代码实现)

[4. 运行结果](#4. 运行结果)

[5. 核心亮点:遵循开闭原则](#5. 核心亮点:遵循开闭原则)

[6. 优点与缺点](#6. 优点与缺点)

优点

缺点

[7. 适用场景](#7. 适用场景)

3️⃣第三种:抽象工厂模式(高级版)

[1. 核心定义](#1. 核心定义)

[2. 关键概念理解](#2. 关键概念理解)

[3. 通俗类比](#3. 通俗类比)

[4. 完整C++代码实现](#4. 完整C++代码实现)

[5. 运行结果](#5. 运行结果)

[6. 优点与缺点](#6. 优点与缺点)

优点

缺点

[7. 适用场景](#7. 适用场景)

📝三种工厂模式的对比总结

⚠️工厂模式的常见误区与注意事项

[1. 误区1:过度使用工厂模式](#1. 误区1:过度使用工厂模式)

[2. 误区2:混淆"工厂方法"与"抽象工厂"](#2. 误区2:混淆“工厂方法”与“抽象工厂”)

[3. 误区3:忽视内存泄漏](#3. 误区3:忽视内存泄漏)

[4. 注意:工厂模式不是万能的](#4. 注意:工厂模式不是万能的)

📚总结


工厂模式是创建型设计模式的核心,它的核心思想是封装对象的创建过程,将对象创建与使用分离。想象一下:你去餐厅点餐(使用对象),肯定是不需要自己动手做菜的(创建对象),餐厅里的厨师(工厂)会根据你的需求做出对应的菜品(实例)------这就是工厂模式的本质。

在C++中,工厂模式主要分为三种形式(从简单到复杂,逐步进阶):简单工厂模式 (又称静态工厂模式)、工厂方法模式抽象工厂模式


🔍为什么需要工厂模式?

在直接使用new创建对象的场景中,会存在几个明显问题:

  1. 耦合度高:使用方必须知道具体类名,一旦类名修改或替换,所有使用处都要修改
  2. 创建逻辑复杂:若对象创建需要一系列初始化步骤(如配置参数、依赖注入),这些逻辑会散落各处,难以维护
  3. 难以扩展:新增一种对象类型时,需要修改所有创建对象的代码,违反"开闭原则"(对扩展开放,对修改关闭)

而工厂模式的核心价值,就是解决以上问题:把对象创建的"黑盒"交给工厂,使用方只需要关心"要什么",不需要关心"怎么造"。


1️⃣第一种:简单工厂模式(最常用)

1. 核心定义

定义一个工厂类,通过静态方法(或普通方法)根据传入的参数,创建并返回不同类型的产品实例。产品需继承自同一抽象基类(或接口)

2. 通俗类比

你去奶茶店(工厂)点单,告诉店员"我要珍珠奶茶"(参数),店员不需要你动手,直接给你做好的珍珠奶茶(产品实例);你点"椰果奶茶",就给你椰果奶茶------所有奶茶都属于"奶茶"这个统一品类(抽象基类)。

3. 完整代码实现

我们以"图形绘制"为场景:支持圆形、矩形两种图形,通过工厂创建对应图形并绘制。

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

// 步骤1:定义产品抽象基类(所有产品的统一接口)
class Shape {
public:
    // 纯虚函数:统一的产品行为(绘制)
    virtual void draw() const = 0;
    // 虚析构函数:确保派生类对象正确析构
    virtual ~Shape() = default;
};

// 步骤2:实现具体产品类(继承抽象基类,实现具体行为)
// 圆形产品
class Circle : public Shape {
public:
    void draw() const override {
        cout << "绘制:圆形" << endl;
    }
};

// 矩形产品
class Rectangle : public Shape {
public:
    void draw() const override {
        cout << "绘制:矩形" << endl;
    }
};

// 步骤3:定义简单工厂类(负责创建所有产品实例)
class ShapeFactory {
public:
    // 静态工厂方法:根据参数创建对应产品
    static Shape* createShape(const string& shapeType) {
        if (shapeType == "circle") {
            return new Circle();
        } else if (shapeType == "rectangle") {
            return new Rectangle();
        } else {
            // 未知类型,返回空指针
            return nullptr;
        }
    }
};

// 步骤4:客户端使用(只和工厂、抽象基类交互,不依赖具体产品)
int main() {
    // 需求1:创建圆形并绘制
    Shape* circle = ShapeFactory::createShape("circle");
    if (circle != nullptr) {
        circle->draw();
        delete circle; // 手动释放内存,避免泄漏
    }

    // 需求2:创建矩形并绘制
    Shape* rectangle = ShapeFactory::createShape("rectangle");
    if (rectangle != nullptr) {
        rectangle->draw();
        delete rectangle;
    }

    // 需求3:创建未知图形(返回空指针)
    Shape* unknown = ShapeFactory::createShape("triangle");
    if (unknown == nullptr) {
        cout << "错误:不支持该图形类型" << endl;
    }

    return 0;
}

4. 打印结果

cpp 复制代码
绘制:圆形
绘制:矩形
错误:不支持该图形类型

5. 优缺点

优点
  • 实现简单:代码量少,容易理解和上手,适合简单的场景。
  • 解耦:客户端只依赖抽象基类和工厂,不依赖具体产品类,可以降低耦合度。
  • 封装创建逻辑:所有对象的创建逻辑集中在工厂类中,便于统一维护(如修改图形初始化参数,只需改工厂)。
缺点
  • 违反开闭原则 :新增产品(如三角形**Triangle** )时,必须修改工厂类的 createShape 方法(添加 **else if**分支),对原有代码有侵入性。
  • 工厂类职责过重:所有产品的创建都由一个工厂负责,一旦工厂出问题,所有产品创建都会受影响。
  • 难以支持复杂产品:若产品创建需要大量不同的初始化参数,工厂方法的参数列表会变得冗长,难以维护。

6. 适用场景

  • 产品类型较少且固定(不会频繁新增产品)。
  • 客户端不需要关心产品的创建细节,只需要根据参数获取产品。

2️⃣第二种:工厂方法模式(遵循开闭原则)

1. 核心定义

将简单工厂的"单一工厂类"拆分为**"工厂抽象基类+具体工厂子类"**,每个具体工厂只负责创建一种具体产品。新增产品时,只需新增对应的产品类和工厂子类,无需修改原有代码。

2. 通俗类比

奶茶店升级了:不再是一个店员做所有奶茶,而是设置"珍珠奶茶车间"(具体工厂1)、"椰果奶茶车间"(具体工厂2),每个车间只生产对应类型的奶茶。新增"水果奶茶"时,只需新增"水果奶茶车间",无需修改原有车间的流程------这就遵循了开闭原则。

3. 完整代码实现

还是以"图形绘制"为场景,升级为工厂方法模式,后续新增三角形无需修改原有代码。

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

// 步骤1:定义产品抽象基类(和简单工厂一致)
class Shape {
public:
    virtual void draw() const = 0;
    virtual ~Shape() = default;
};

// 步骤2:实现具体产品类(和简单工厂一致)
class Circle : public Shape {
public:
    void draw() const override {
        cout << "绘制:圆形" << endl;
    }
};

class Rectangle : public Shape {
public:
    void draw() const override {
        cout << "绘制:矩形" << endl;
    }
};

// 新增:三角形产品(后续扩展,无需修改原有代码)
class Triangle : public Shape {
public:
    void draw() const override {
        cout << "绘制:三角形" << endl;
    }
};

// 步骤3:定义工厂抽象基类(所有具体工厂的统一接口)
class ShapeFactory {
public:
    // 纯虚函数:创建产品的接口(每个具体工厂实现自己的创建逻辑)
    virtual Shape* createShape() const = 0;
    virtual ~ShapeFactory() = default;
};

// 步骤4:实现具体工厂类(每个工厂对应一种产品)
// 圆形工厂:只创建圆形
class CircleFactory : public ShapeFactory {
public:
    Shape* createShape() const override {
        return new Circle();
    }
};

// 矩形工厂:只创建矩形
class RectangleFactory : public ShapeFactory {
public:
    Shape* createShape() const override {
        return new Rectangle();
    }
};

// 新增:三角形工厂(扩展时新增,无需修改原有工厂)
class TriangleFactory : public ShapeFactory {
public:
    Shape* createShape() const override {
        return new Triangle();
    }
};

// 步骤5:客户端使用(依赖抽象工厂和抽象产品,不依赖具体实现)
int main() {
    // 需求1:创建圆形(使用圆形工厂)
    ShapeFactory* circleFactory = new CircleFactory();
    Shape* circle = circleFactory->createShape();
    circle->draw();
    delete circle;
    delete circleFactory;

    // 需求2:创建矩形(使用矩形工厂)
    ShapeFactory* rectangleFactory = new RectangleFactory();
    Shape* rectangle = rectangleFactory->createShape();
    rectangle->draw();
    delete rectangle;
    delete rectangleFactory;

    // 需求3:创建三角形(新增功能,无需修改原有代码)
    ShapeFactory* triangleFactory = new TriangleFactory();
    Shape* triangle = triangleFactory->createShape();
    triangle->draw();
    delete triangle;
    delete triangleFactory;

    return 0;
}

4. 运行结果

cpp 复制代码
绘制:圆形
绘制:矩形
绘制:三角形

5. 核心亮点:遵循开闭原则

当我们需要新增"三角形"产品时,只需要做两件事:

  1. 新增**Triangle** 类(继承 Shape ),实现 **draw**方法。
  2. 新增 TriangleFactory 类(继承 ShapeFactory ),实现 **createShape**方法。

整个过程不需要修改任何原有代码 (既不修改抽象类,也不修改已有的圆形、矩形工厂/产品),完美符合"对扩展开放,对修改关闭"的开闭原则。

6. 优点与缺点

优点
  • 遵循开闭原则:新增产品只需新增对应的产品类和工厂类,无侵入性修改。
  • 工厂职责单一:每个具体工厂只负责创建一种产品,符合"单一职责原则",便于维护。
  • 灵活性更高:可以通过子类化工厂,定制产品的创建逻辑(如给圆形设置不同的半径)。
缺点
  • 类数量爆炸:每新增一种产品,就需要新增两个类(产品类+工厂类),当产品类型较多时,类的数量会大幅增加,增加项目复杂度。
  • 实现复杂:相比简单工厂,需要定义更多的抽象类和子类,上手难度略高。
  • 客户端需要了解更多类 :客户端需要知道具体工厂类(如 CircleFactory),才能创建对应的产品,相比简单工厂的"传参创建",使用成本更高。

7. 适用场景

  • 产品类型较多,且需要频繁新增产品(如电商平台的商品类型、游戏中的道具类型)。
  • 希望遵循开闭原则,降低代码维护成本。
  • 每个产品的创建逻辑相对独立,需要单独定制。

3️⃣第三种:抽象工厂模式(高级版)

1. 核心定义

抽象工厂模式用于创建一系列相关或相互依赖的产品(产品族),它定义了一个抽象工厂接口,每个具体工厂实现该接口,负责创建一个产品族中的所有产品。

2. 关键概念理解

  • 产品族:一组相关的产品,属于同一"品牌"或"系列",例如:"中式家具"(产品族1)包含中式桌子、中式椅子;"欧式家具"(产品族2)包含欧式桌子、欧式椅子------桌子和椅子是"产品等级",中式和欧式是"产品族"。
  • 核心价值 :确保创建的产品族内的产品是相互匹配的(不会出现"中式桌子+欧式椅子"的组合)。

3. 通俗类比

家具厂(抽象工厂)有两个分厂:中式家具厂(具体工厂1)、欧式家具厂(具体工厂2)。中式家具厂只生产中式桌子、中式椅子(产品族1),欧式家具厂只生产欧式桌子、欧式椅子(产品族2)。客户只需要选择"中式"或"欧式",就能得到一套配套的家具,无需担心搭配错乱。

4. 完整C++代码实现

以"家具生产"为场景,实现抽象工厂模式,确保家具风格统一。

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

// 步骤1:定义第一个产品等级的抽象基类------桌子
class Table {
public:
    virtual void showStyle() const = 0;
    virtual ~Table() = default;
};

// 步骤2:定义第二个产品等级的抽象基类------椅子
class Chair {
public:
    virtual void showStyle() const = 0;
    virtual ~Chair() = default;
};

// 步骤3:实现具体产品(产品族1:中式家具)
// 中式桌子
class ChineseTable : public Table {
public:
    void showStyle() const override {
        cout << "家具:中式桌子(红木材质、雕花工艺)" << endl;
    }
};

// 中式椅子
class ChineseChair : public Chair {
public:
    void showStyle() const override {
        cout << "家具:中式椅子(实木框架、软垫坐垫)" << endl;
    }
};

// 步骤4:实现具体产品(产品族2:欧式家具)
// 欧式桌子
class EuropeanTable : public Table {
public:
    void showStyle() const override {
        cout << "家具:欧式桌子(大理石桌面、镀金雕花)" << endl;
    }
};

// 欧式椅子
class EuropeanChair : public Chair {
public:
    void showStyle() const override {
        cout << "家具:欧式椅子(皮质坐垫、铁艺框架)" << endl;
    }
};

// 步骤5:定义抽象工厂(负责创建产品族,包含所有产品等级的创建接口)
class FurnitureFactory {
public:
    // 创建桌子的接口
    virtual Table* createTable() const = 0;
    // 创建椅子的接口
    virtual Chair* createChair() const = 0;
    virtual ~FurnitureFactory() = default;
};

// 步骤6:实现具体工厂(每个工厂对应一个产品族)
// 中式家具工厂:创建中式桌子+中式椅子
class ChineseFurnitureFactory : public FurnitureFactory {
public:
    Table* createTable() const override {
        return new ChineseTable();
    }

    Chair* createChair() const override {
        return new ChineseChair();
    }
};

// 欧式家具工厂:创建欧式桌子+欧式椅子
class EuropeanFurnitureFactory : public FurnitureFactory {
public:
    Table* createTable() const override {
        return new EuropeanTable();
    }

    Chair* createChair() const override {
        return new EuropeanChair();
    }
};

// 步骤7:客户端使用(选择产品族,获取配套产品)
void buyFurniture(const FurnitureFactory& factory) {
    // 从工厂获取配套的桌子和椅子
    Table* table = factory.createTable();
    Chair* chair = factory.createChair();

    // 展示家具风格
    table->showStyle();
    chair->showStyle();

    // 释放内存
    delete table;
    delete chair;
}

int main() {
    cout << "=== 购买中式家具 ===" << endl;
    ChineseFurnitureFactory chineseFactory;
    buyFurniture(chineseFactory);

    cout << "\n=== 购买欧式家具 ===" << endl;
    EuropeanFurnitureFactory europeanFactory;
    buyFurniture(europeanFactory);

    return 0;
}

5. 运行结果

cpp 复制代码
=== 购买中式家具 ===
家具:中式桌子(红木材质、雕花工艺)
家具:中式椅子(实木框架、软垫坐垫)

=== 购买欧式家具 ===
家具:欧式桌子(大理石桌面、镀金雕花)
家具:欧式椅子(皮质坐垫、铁艺框架)

6. 优点与缺点

优点
  • 保证产品配套性:创建的产品属于同一产品族,不会出现不匹配的产品组合,简化客户端的使用。
  • 遵循开闭原则:新增产品族(如美式家具)时,只需新增对应的产品类和工厂类,无需修改原有代码。
  • 高度封装:客户端只需依赖抽象工厂和抽象产品,完全隔离具体产品的实现细节,耦合度极低。
缺点
  • 扩展产品等级困难 :若需要新增产品等级(如在家具中新增"沙发"),需要修改抽象工厂接口(添加 **createSofa**方法),所有具体工厂都需要同步修改,违反开闭原则。
  • 类数量急剧增加:每个产品族包含多个产品等级,新增产品族或产品等级都会导致类数量大幅增加,项目复杂度高。
  • 实现复杂:抽象层次多,需要理解产品族和产品等级的概念,上手难度最高。

7. 适用场景

  • 系统需要创建一组相关的产品(产品族),且需要保证产品的配套性。
  • 产品族相对固定,产品等级不会频繁新增(如家电品牌的产品系列、汽车品牌的车型系列)。
  • 希望隔离具体产品的实现,提高系统的可移植性(如切换不同的数据库驱动、不同的UI风格)。

📝三种工厂模式的对比总结

|--------|-----------------------|--------------------|---------------------|------------------|
| 模式类型 | 核心思想 | 优点 | 缺点 | 适用场景 |
| 简单工厂模式 | 单一工厂类,根据参数创建不同产品 | 实现简单、解耦、封装创建逻辑 | 违反开闭原则、工厂职责过重 | 产品类型少且固定,简单场景 |
| 工厂方法模式 | 工厂抽象+具体工厂,一个工厂对应一个产品 | 遵循开闭原则、工厂职责单一、灵活性高 | 类数量爆炸、实现复杂、客户端使用成本高 | 产品类型多,需要频繁新增产品 |
| 抽象工厂模式 | 工厂抽象+具体工厂,一个工厂对应一个产品族 | 保证产品配套、遵循开闭原则、高度封装 | 扩展产品等级困难、类数量暴增、实现复杂 | 需创建配套产品族,产品族相对固定 |


⚠️工厂模式的常见误区与注意事项

1. 误区1:过度使用工厂模式

简单场景(如只有1-2种产品,且永不扩展)直接使用 new 创建对象即可,无需强行引入工厂模式------过度设计会增加代码复杂度,得不偿失。(不要为了使用设计模式而使用)

2. 误区2:混淆"工厂方法"与"抽象工厂"

  • 工厂方法模式:关注单个产品的创建,解决"新增产品"的扩展问题。
  • 抽象工厂模式:关注产品族的创建,解决"新增配套产品系列"的扩展问题。

3. 误区3:忽视内存泄漏

C++中通过工厂创建的对象大多是堆上分配的(new 创建),客户端使用完毕后必须手动 delete ,或使用智能指针(std::unique_ptr)管理内存,避免内存泄漏。

cpp 复制代码
// 优化:使用智能指针,自动释放内存
#include <memory>

Shape* createShape() {
    return new Circle();
}

// 客户端使用unique_ptr
std::unique_ptr<Shape> circle(createShape());
circle->draw(); // 无需手动delete,智能指针自动释放

4. 注意:工厂模式不是万能的

工厂模式解决的是"对象创建"的问题,无法解决对象的使用、维护等问题。在实际开发中,需结合其他设计模式(如单例模式、策略模式)使用。


📚总结

工厂模式的核心价值是封装创建,解耦使用,三种模式从简单到复杂,分别对应不同的场景需求。在实际开发中,无需盲目追求"高级模式",而是要根据项目的实际情况选择合适的模式:

  • 简单场景选简单工厂
  • 需频繁新增单个产品选工厂方法
  • 需创建配套产品族选抽象工厂

掌握工厂模式,不仅能提高代码的可维护性和扩展性,更能理解"面向抽象编程"的核心思想------这也是设计模式的精髓所在。

相关推荐
星轨初途2 小时前
牛客小白月赛126
开发语言·c++·经验分享·笔记·算法
fqbqrr2 小时前
2601,链式调用
c++
yuuki2332332 小时前
【C++】掌握list:C++链表容器的核心奥秘
c++·后端·链表·list
崎岖Qiu2 小时前
【设计模式笔记24】:JDK源码分析-Comparator中的「策略模式」
java·笔记·设计模式·jdk·策略模式
崎岖Qiu3 小时前
【设计模式笔记23】:长文解析-深刻理解「装饰器模式」
java·笔记·设计模式·装饰器模式
坐怀不乱杯魂3 小时前
C++ 11
c++
yuuki2332333 小时前
【C++】揭秘STL:stack与queue的底层实现
java·c++·windows
草莓熊Lotso4 小时前
Python 进阶核心:字典 / 文件操作 + 上下文管理器实战指南
数据结构·c++·人工智能·经验分享·笔记·git·python
Thera77712 小时前
状态机(State Machine)详解:原理、优缺点与 C++ 实战示例
开发语言·c++