C++设计模式:抽象工厂模式(风格切换案例)

抽象工厂模式(Abstract Factory)是一种创建型设计模式 ,其核心思想是:为一组相关或相互依赖的对象提供一个创建接口,而无需指定它们具体的类。简单来说,就是一个工厂可以生产一系列相关的对象。

我们接下来通过逐步拆解、举例说明和详细代码注释来理解这个模式。


1. 什么是抽象工厂模式?

举个简单例子:

想象你是个游戏开发者,需要为游戏开发不同风格的用户界面(UI)。

  • 如果玩家在 Windows 上玩,游戏需要提供 Windows 风格的按钮、文本框。
  • 如果玩家在 Mac 上玩,则需要提供 Mac 风格的按钮、文本框。

这里的问题是:

  • 我们需要根据不同的操作系统 来生成一套成对的 UI 组件(按钮 + 文本框)。
  • 不同的 UI 组件风格相互独立,代码却不能依赖具体的组件实现。

解决这个问题的方案就是使用抽象工厂模式。它允许我们将"创建对象"的逻辑与对象的具体实现分离开。


2. 模式的核心要素

抽象工厂模式包含以下几部分:

  1. 抽象工厂:定义创建一系列对象的接口。
  2. 具体工厂:实现创建特定风格对象的工厂。
  3. 抽象产品:定义产品的公共接口(如按钮和文本框的接口)。
  4. 具体产品:特定风格的产品实现。
  5. 客户端:通过抽象工厂创建产品,而不关心具体产品的实现。

下面用代码一步步实现这个设计模式。


3. 详细代码实现与讲解

Step 1: 定义抽象产品

按钮和文本框是两种产品。我们需要定义它们的接口,让不同风格的具体产品实现这些接口。

cpp 复制代码
#include <iostream>
#include <memory> // 用于智能指针

// 抽象产品 A: 按钮
class Button {
public:
    virtual void render() const = 0; // 渲染按钮
    virtual ~Button() = default;     // 虚析构,避免内存泄漏
};

// 抽象产品 B: 文本框
class TextBox {
public:
    virtual void render() const = 0; // 渲染文本框
    virtual ~TextBox() = default;
};

解释:

  • ButtonTextBox 是两个抽象接口,定义了按钮和文本框的行为(render()方法)。
  • 使用抽象类的好处是,客户端代码可以依赖于接口,而不是具体的实现。

Step 2: 定义具体产品

实现两种风格(Windows 和 Mac)的具体按钮和文本框。

cpp 复制代码
// 具体产品 A1: Windows 风格按钮
class WindowsButton : public Button {
public:
    void render() const override {
        std::cout << "Rendering Windows-style Button\n";
    }
};

// 具体产品 B1: Windows 风格文本框
class WindowsTextBox : public TextBox {
public:
    void render() const override {
        std::cout << "Rendering Windows-style TextBox\n";
    }
};

// 具体产品 A2: Mac 风格按钮
class MacButton : public Button {
public:
    void render() const override {
        std::cout << "Rendering Mac-style Button\n";
    }
};

// 具体产品 B2: Mac 风格文本框
class MacTextBox : public TextBox {
public:
    void render() const override {
        std::cout << "Rendering Mac-style TextBox\n";
    }
};

解释:

  • WindowsButtonMacButton 分别实现了 Button 接口。
  • WindowsTextBoxMacTextBox 分别实现了 TextBox 接口。
  • 每个具体类实现了它们特定风格的 render() 方法,输出不同的效果。

Step 3: 定义抽象工厂

工厂负责创建相关产品(按钮 + 文本框)。我们定义一个抽象工厂接口,声明创建按钮和文本框的方法。

cpp 复制代码
// 抽象工厂:定义创建一组产品的方法
class GUIFactory {
public:
    virtual std::unique_ptr<Button> createButton() const = 0;   // 创建按钮
    virtual std::unique_ptr<TextBox> createTextBox() const = 0; // 创建文本框
    virtual ~GUIFactory() = default;
};

解释:

  • GUIFactory 是抽象工厂,定义了创建按钮和文本框的接口。
  • 返回值使用了 std::unique_ptr,可以自动管理对象生命周期,防止内存泄漏。

Step 4: 定义具体工厂

实现两个具体工厂,用于创建 Windows 和 Mac 风格的产品。

cpp 复制代码
// 具体工厂 1: Windows 工厂
class WindowsFactory : public GUIFactory {
public:
    std::unique_ptr<Button> createButton() const override {
        return std::make_unique<WindowsButton>(); // 创建 Windows 按钮
    }

    std::unique_ptr<TextBox> createTextBox() const override {
        return std::make_unique<WindowsTextBox>(); // 创建 Windows 文本框
    }
};

// 具体工厂 2: Mac 工厂
class MacFactory : public GUIFactory {
public:
    std::unique_ptr<Button> createButton() const override {
        return std::make_unique<MacButton>(); // 创建 Mac 按钮
    }

    std::unique_ptr<TextBox> createTextBox() const override {
        return std::make_unique<MacTextBox>(); // 创建 Mac 文本框
    }
};

解释:

  • WindowsFactoryMacFactory 分别实现了 GUIFactory,负责生成各自风格的产品。
  • 每个工厂实现了 createButton()createTextBox()

Step 5: 编写客户端代码

客户端通过抽象工厂使用产品,而不关心具体实现。

cpp 复制代码
// 客户端代码:通过工厂创建和使用产品
void renderUI(const GUIFactory& factory) {
    auto button = factory.createButton(); // 创建按钮
    auto textBox = factory.createTextBox(); // 创建文本框

    button->render(); // 渲染按钮
    textBox->render(); // 渲染文本框
}

int main() {
    std::cout << "Windows GUI:\n";
    WindowsFactory windowsFactory;
    renderUI(windowsFactory); // 使用 Windows 工厂

    std::cout << "\nMac GUI:\n";
    MacFactory macFactory;
    renderUI(macFactory); // 使用 Mac 工厂

    return 0;
}

解释:

  • renderUI 是客户端函数,通过 GUIFactory 创建产品。
  • 客户端无需关心具体工厂或产品的实现,只依赖于抽象接口。

4. 输出结果

运行上述代码,将输出以下结果:

复制代码
Windows GUI:
Rendering Windows-style Button
Rendering Windows-style TextBox

Mac GUI:
Rendering Mac-style Button
Rendering Mac-style TextBox

5. 模式的优缺点

优点
  1. 分离具体类:客户端与具体产品的实现解耦。
  2. 确保产品一致性:某个具体工厂生产的所有产品风格一致。
  3. 易于扩展:可以新增工厂和产品系列,而无需修改客户端代码。
缺点
  1. 增加复杂性:需要定义多组接口和类,代码量较多。
  2. 扩展产品族困难:如果需要新增产品(比如菜单),需要修改所有工厂接口和实现。

总结

抽象工厂模式通过工厂接口屏蔽了具体产品的创建细节,使代码更加灵活和可扩展。它非常适合需要生成一组相关对象不希望客户端代码依赖于具体实现的场景。在实际开发中,抽象工厂模式广泛应用于跨平台工具、插件系统等领域。

完整代码实现

以下是抽象工厂模式的完整实现,并将所有输出改为中文形式,便于理解。

cpp 复制代码
#include <iostream>
#include <memory> // 用于智能指针管理

// 抽象产品 A: 按钮
class Button {
public:
    virtual void render() const = 0; // 渲染按钮(纯虚函数)
    virtual ~Button() = default;     // 虚析构,防止内存泄漏
};

// 抽象产品 B: 文本框
class TextBox {
public:
    virtual void render() const = 0; // 渲染文本框(纯虚函数)
    virtual ~TextBox() = default;
};

// 具体产品 A1: Windows 风格按钮
class WindowsButton : public Button {
public:
    void render() const override {
        std::cout << "渲染 Windows 风格按钮" << "\n"; // 中文输出
    }
};

// 具体产品 B1: Windows 风格文本框
class WindowsTextBox : public TextBox {
public:
    void render() const override {
        std::cout << "渲染 Windows 风格文本框" << "\n"; // 中文输出
    }
};

// 具体产品 A2: Mac 风格按钮
class MacButton : public Button {
public:
    void render() const override {
        std::cout << "渲染 Mac 风格按钮" << "\n"; // 中文输出
    }
};

// 具体产品 B2: Mac 风格文本框
class MacTextBox : public TextBox {
public:
    void render() const override {
        std::cout << "渲染 Mac 风格文本框" << "\n"; // 中文输出
    }
};

// 抽象工厂:定义创建一组产品的方法
class GUIFactory {
public:
    virtual std::unique_ptr<Button> createButton() const = 0;   // 创建按钮
    virtual std::unique_ptr<TextBox> createTextBox() const = 0; // 创建文本框
    virtual ~GUIFactory() = default;
};

// 具体工厂 1: Windows 工厂
class WindowsFactory : public GUIFactory {
public:
    std::unique_ptr<Button> createButton() const override {
        return std::make_unique<WindowsButton>(); // 创建 Windows 按钮
    }

    std::unique_ptr<TextBox> createTextBox() const override {
        return std::make_unique<WindowsTextBox>(); // 创建 Windows 文本框
    }
};

// 具体工厂 2: Mac 工厂
class MacFactory : public GUIFactory {
public:
    std::unique_ptr<Button> createButton() const override {
        return std::make_unique<MacButton>(); // 创建 Mac 按钮
    }

    std::unique_ptr<TextBox> createTextBox() const override {
        return std::make_unique<MacTextBox>(); // 创建 Mac 文本框
    }
};

// 客户端代码:通过工厂创建和使用产品
void renderUI(const GUIFactory& factory) {
    // 使用工厂创建按钮和文本框
    auto button = factory.createButton(); // 创建按钮
    auto textBox = factory.createTextBox(); // 创建文本框

    // 渲染创建的产品
    button->render();
    textBox->render();
}

int main() {
    // 使用 Windows 工厂创建产品
    std::cout << "Windows 界面:" << "\n"; // 中文输出
    WindowsFactory windowsFactory;
    renderUI(windowsFactory);

    // 使用 Mac 工厂创建产品
    std::cout << "\nMac 界面:" << "\n"; // 中文输出
    MacFactory macFactory;
    renderUI(macFactory);

    return 0;
}

输出结果

运行该程序,输出如下:

复制代码
Windows 界面:
渲染 Windows 风格按钮
渲染 Windows 风格文本框

Mac 界面:
渲染 Mac 风格按钮
渲染 Mac 风格文本框

本文由mdnice多平台发布

相关推荐
小坏坏的大世界5 小时前
C++ STL常用容器总结(vector, deque, list, map, set)
c++·算法
liulilittle6 小时前
C++ TAP(基于任务的异步编程模式)
服务器·开发语言·网络·c++·分布式·任务·tap
励志要当大牛的小白菜7 小时前
ART配对软件使用
开发语言·c++·qt·算法
PAK向日葵8 小时前
【算法导论】如何攻克一道Hard难度的LeetCode题?以「寻找两个正序数组的中位数」为例
c++·算法·面试
QQ_43766431412 小时前
C++11 右值引用 Lambda 表达式
java·开发语言·c++
liulilittle14 小时前
C++/CLI与标准C++的语法差异(一)
开发语言·c++·.net·cli·clr·托管·原生
小狄同学呀14 小时前
VS插件报错,g++却完美编译?API调用错因分析
c++
程序员编程指南14 小时前
Qt 数据库连接池实现与管理
c语言·数据库·c++·qt·oracle
小乖兽技术14 小时前
C#与C++交互开发系列(二十四):WinForms 应用中嵌入C++ 原生窗体
c++·c#·交互
张北北.15 小时前
【深入底层】C++开发简历4+4技能描述6
java·开发语言·c++