当前位置: 首页 > article >正文

【C++设计模式】工厂方法设计模式:深入解析从基础到进阶

在这里插入图片描述

1. 引言

在软件开发的世界里,设计模式如同巧妙的建筑蓝图,为解决常见问题提供了行之有效的方案。工厂方法模式作为一种广受欢迎的创建型设计模式,以其独特的优势在众多项目中得到广泛应用。它不仅能够为对象的创建提供通用且灵活的方式,还能有效隐藏实现细节,提升代码的可维护性和可扩展性。本文将全方位深入探讨工厂方法模式,从基础定义、实现过程,到进阶优化和功能扩展,带领读者全面掌握这一重要的设计模式。

2. 工厂方法模式的基础定义与目标

2.1 定义

工厂方法模式提供了一种通用的方式来创建对象实例,它能够很好地隐藏实现细节,特别是对于派生类的实现细节。这种模式属于创建型设计模式,核心在于将对象的创建和使用分离,通过一个工厂函数,根据输入参数来返回正确的对象实例。

2.2 目标

在实际开发中,直接在代码中显式地创建具体对象会增加代码的耦合度,不利于后续的扩展和维护。例如在游戏开发场景下,若有一个 GameObject 类型的对象,直接使用 new ObjectOne 这样的方式创建对象,会使代码变得复杂且难以管理。而工厂方法模式通过工厂函数,避免了在代码中直接决定创建哪种对象,从而提高了代码的可维护性和灵活性。

3. 基础实现工厂方法模式

3.1 利用继承和多态

工厂方法模式的关键在于利用继承和基于继承的多态性。首先,我们创建一个接口类 IGameObject

class IGameObject {
public:
    virtual ~IGameObject() = default;
    virtual void update() = 0;
    virtual void render() = 0;
};

这里的 updaterender 是纯虚函数,任何从 IGameObject 派生的类都必须实现这些函数。

接着,创建具体的派生类,如 PlaneBoat

class Plane : public IGameObject {
public:
    Plane() {}
    void update() override {}
    void render() override {}
};

class Boat : public IGameObject {
public:
    Boat() {}
    void update() override {}
    void render() override {}
};

3.2 创建工厂函数

创建一个工厂函数 makeGameObjectFactory,用于根据输入参数创建相应的对象:

#include <string>
#include <memory>

std::unique_ptr<IGameObject> makeGameObjectFactory(const std::string& type) {
    if (type == "plane") {
        return std::make_unique<Plane>();
    } else if (type == "boat") {
        return std::make_unique<Boat>();
    }
    return nullptr;
}

main 函数中,可以这样使用工厂函数:

int main() {
    std::unique_ptr<IGameObject> myObject = makeGameObjectFactory("plane");
    if (myObject) {
        myObject->update();
        myObject->render();
    }
    return 0;
}

4. 基础优化工厂方法模式

4.1 避免使用字符串作为参数

使用字符串作为工厂函数的参数存在一些问题,例如大小写敏感、容易输入错误等。为了避免这些问题,我们可以使用 enum class 来定义对象类型:

enum class ObjectType {
    Plane,
    Boat
};

std::unique_ptr<IGameObject> makeGameObjectFactory(ObjectType type) {
    switch (type) {
        case ObjectType::Plane:
            return std::make_unique<Plane>();
        case ObjectType::Boat:
            return std::make_unique<Boat>();
        default:
            return nullptr;
    }
}

main 函数中,调用方式如下:

int main() {
    std::unique_ptr<IGameObject> myObject = makeGameObjectFactory(ObjectType::Plane);
    if (myObject) {
        myObject->update();
        myObject->render();
    }
    return 0;
}

4.2 使用智能指针

为了避免手动管理内存,我们可以使用智能指针,如 std::unique_ptrstd::shared_ptr。在上述代码中,我们已经使用了 std::unique_ptr,它可以在对象不再使用时自动释放内存,避免了内存泄漏的问题。

5. 工厂方法模式的进阶扩展

5.1 单例包装工厂

5.1.1 动机与设计思路

单例模式确保一个类只有一个实例,并提供一个全局访问点。将工厂方法包装成单例类,我们可以实现唯一的对象创建点,这对于某些需要严格控制对象创建的场景非常有用。同时,单例包装还可以为工厂方法添加更多的抽象层次,从而实现一些额外的功能,例如对象计数。

5.1.2 代码实现

创建一个名为 FactoryGameObject 的类,并将之前的工厂方法 makeGameObjectFactory 移入该类中。为了实现单例模式,将构造函数、析构函数和拷贝构造函数设为私有,同时将工厂方法设为静态成员函数:

#include <memory>
#include <iostream>

// 前向声明 IGameObject
class IGameObject;

class FactoryGameObject {
private:
    FactoryGameObject() = default;
    ~FactoryGameObject() = default;
    FactoryGameObject(const FactoryGameObject&) = delete;
    FactoryGameObject& operator=(const FactoryGameObject&) = delete;

    static std::shared_ptr<IGameObject> createObject(const std::string& type);

public:
    static std::shared_ptr<IGameObject> getInstance(const std::string& type) {
        return createObject(type);
    }
};

在上述代码中,将 createObject 方法设为私有,通过公共的静态方法 getInstance 来调用它,以确保外部代码只能通过单例接口访问工厂方法。

5.2 对象计数功能实现

5.2.1 静态成员变量与初始化

为了实现对象计数功能,在 FactoryGameObject 类中添加静态成员变量来记录每种对象的创建数量:

class FactoryGameObject {
private:
    // ... 之前的代码 ...
    static int s_planeCount;
    static int s_boatCount;

    static std::shared_ptr<IGameObject> createObject(const std::string& type) {
        if (type == "plane") {
            s_planeCount++;
            return std::make_shared<Plane>();
        } else if (type == "boat") {
            s_boatCount++;
            return std::make_shared<Boat>();
        }
        return nullptr;
    }

public:
    // ... 之前的代码 ...
    static void printCounts() {
        std::cout << "Planes created: " << s_planeCount << std::endl;
        std::cout << "Boats created: " << s_boatCount << std::endl;
    }
};

// 静态成员变量初始化
int FactoryGameObject::s_planeCount = 0;
int FactoryGameObject::s_boatCount = 0;

在上述代码中,在 createObject 方法中对相应的计数器进行递增操作,并提供了一个 printCounts 方法来输出计数结果。

5.2.2 测试代码
int main() {
    auto plane = FactoryGameObject::getInstance("plane");
    auto boat1 = FactoryGameObject::getInstance("boat");
    auto boat2 = FactoryGameObject::getInstance("boat");

    FactoryGameObject::printCounts();

    return 0;
}

运行上述代码,我们可以看到输出结果显示创建了 1 个飞机对象和 2 个船对象。

5.3 统计活跃对象数量

5.3.1 问题分析

虽然我们已经实现了对象创建数量的统计,但对于活跃对象(即尚未被销毁的对象)的数量统计,由于使用了 std::shared_ptr,我们无法直接得知对象是否已经被销毁。

5.3.2 解决方案
  • 适配器模式:创建一个适配器类来包装 std::shared_ptr,并在构造函数和析构函数中进行计数操作:
template <typename T>
class MicsCountedSmartPointer {
private:
    std::shared_ptr<T> m_ptr;
    static int s_count;

public:
    MicsCountedSmartPointer(const std::shared_ptr<T>& ptr) : m_ptr(ptr) {
        s_count++;
    }

    ~MicsCountedSmartPointer() {
        s_count--;
    }

    static int getCount() {
        return s_count;
    }
};

template <typename T>
int MicsCountedSmartPointer<T>::s_count = 0;
  • 弱指针方法:使用 std::weak_ptr。维护一个 std::weak_ptr 列表,定期遍历该列表,检查哪些对象已经被销毁:
#include <vector>
#include <memory>

class FactoryGameObject {
private:
    // ... 之前的代码 ...
    static std::vector<std::weak_ptr<IGameObject>> s_objectList;

    static std::shared_ptr<IGameObject> createObject(const std::string& type) {
        std::shared_ptr<IGameObject> obj;
        if (type == "plane") {
            s_planeCount++;
            obj = std::make_shared<Plane>();
        } else if (type == "boat") {
            s_boatCount++;
            obj = std::make_shared<Boat>();
        }
        if (obj) {
            s_objectList.push_back(obj);
        }
        return obj;
    }

public:
    // ... 之前的代码 ...
    static int getActiveObjectCount() {
        int activeCount = 0;
        for (const auto& weakPtr : s_objectList) {
            if (!weakPtr.expired()) {
                activeCount++;
            }
        }
        return activeCount;
    }
};

std::vector<std::weak_ptr<IGameObject>> FactoryGameObject::s_objectList;

6. 工厂方法模式的优缺点

6.1 优点

  • 单一职责:工厂函数的职责明确,就是根据输入参数创建相应的对象,使得代码的逻辑更加清晰。
  • 灵活性:可以根据不同的需求轻松扩展工厂函数,支持更多的对象类型。
  • 可维护性:将对象的创建和使用分离,降低了代码的耦合度,便于后续的维护和修改。

6.2 缺点

  • 代码更新:当需要添加新的对象类型时,需要同时更新工厂函数和相关的类定义,这可能会增加代码的维护成本。
  • 多个工厂:对于复杂的系统,可能需要多个工厂函数来处理不同的对象层次结构,这会增加代码的复杂度。

7. 总结

工厂方法模式作为一种强大且实用的设计模式,为对象的创建提供了一种优雅而灵活的解决方案。从基础的定义和实现,到进阶的优化和功能扩展,我们逐步深入了解了该模式的各个方面。通过合理运用继承、多态、单例模式以及智能指针等技术,我们可以充分发挥工厂方法模式的优势,同时避免其潜在的缺点。在实际开发中,我们应根据具体的需求和场景,灵活运用工厂方法模式,并结合其他设计模式,构建出高效、可维护和可扩展的软件系统。


http://www.kler.cn/a/560523.html

相关文章:

  • 云电脑接入DeepSeek?探讨ToDesk云电脑、海马云、顺网云的AI潜能
  • API技术深度解析:构建高效、安全与可扩展的接口服务
  • Linux下文件权限与安全
  • 使用 INFINI Console 配置集群监控 Webhook 通知指南
  • 正则化及其在机器学习中的作用
  • fps动作系统4.1:移动系统
  • 2025年3月最新算法-鲸鱼迁徙优化算法Whale Migration Algorithm-附Matlab免费代码
  • Seata1.5.2学习(一)——分布式事务与安装配置
  • 【HarmonyOS Next】鸿蒙循环渲染ForEach,LazyForEach,Repeat使用心得体会
  • 9.【线性代数】—— 线性相关性, 向量空间的基,维数
  • 嵌入式产品级-超小尺寸游戏机(从0到1 硬件-软件-外壳)
  • 计算机毕业设计Hadoop+Spark+DeepSeek-R1大模型民宿推荐系统 hive民宿可视化 民宿爬虫 大数据毕业设计(源码+文档+PPT+讲解)
  • 反应扩散方程组数值解
  • 故障诊断 | PID搜索算法优化CatBoost故障诊断(MatlabPython)
  • uni-app开发安卓和ios app 真机调试
  • 【Git版本控制器】第五弹——远程仓库,push,pull,gitignore
  • 【R语言】ggplot2绘图常用操作
  • Android之APP更新(通过接口更新)
  • 嵌入式硬件篇---滤波器
  • 创建第一个 Maven 项目(一)