设计模式之享元模式

目录

1.概述

2.结构

3.实现

4.应用场景

5.总结


1.概述

享元模式(Flyweight Pattern)主要用于减少创建对象的数量,通过使用共享对象来支持大量的细粒度对象,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。在享元模式中,有些对象可以被多个客户端共享,以减少创建对象的数量。享元模式的核心在于享元工厂类,它负责创建和管理享元对象,并提供对外访问的接口。

2.结构

享元模式的结构图如下所示:

具体角色定义:

抽象享元(Flyweight): 定义了具体享元和非共享享元的接口,通常包含了设置外部状态的方法。
具体享元(Concrete Flyweight) :实现了抽象享元接口,包含了内部状态和外部状态。内部状态是可以被共享的,而外部状态则由客户端传递。上面的具体享元类ConcreteFlyweight是的内部状态是可以共享的,UnsharedConcreteFlyweight是指那些不需要共享的Flyweight子类,因为Flyweight接口共享成为可能,但是并不强制共享。
享元工厂(Flyweight Factory) :负责创建和管理享元对象,通常包含一个池(缓存)用于存储和复用已经创建的享元对象。工厂类内部维护一个享元对象的集合(通常是一个哈希表或字典),用于存储已经创建的享元对象。提供获取享元对象的方法,当请求某个享元对象时,工厂类首先检查集合中是否存在该对象,如果存在则直接返回,否则创建新的享元对象并添加到集合中。
客户端(Client) : 使用享元工厂获取享元对象,并通过设置外部状态来操作享元对象。客户端通常不需要关心享元对象的具体实现。

3.实现

简单实现代码如下:

cpp 复制代码
#include <iostream>  
#include <unordered_map>  
#include <string>  
  
// 享元接口  
class Flyweight {  
public:  
    virtual ~Flyweight() {}  
    virtual void operation(const std::string& extrinsicState) = 0;  
};  
  
// 具体的享元类  
class ConcreteFlyweight : public Flyweight {  
private:  
    std::string intrinsicState; // 内部状态  
  
public:  
    ConcreteFlyweight(const std::string& state) : intrinsicState(state) {}  
  
    void operation(const std::string& extrinsicState) override {  
        std::cout << "Intrinsic: " << intrinsicState << ", Extrinsic: " << extrinsicState << std::endl;  
    }  
};  
  
// 享元工厂类  
class FlyweightFactory {  
public:
    ~FlyweightFactory(){
        for (auto& it : m_flyweights){
            delete it.second;
        }
        m_flyweights.clear();
    }
  
public:  
    Flyweight* getFlyweight(const std::string& key) {  
        if (m_flyweights.find(key) == m_flyweights.end()) {  
            m_flyweights[key] = new ConcreteFlyweight(key);  
        }  
        return m_flyweights[key];  
    }  
private:  
    std::unordered_map<std::string, Flyweight*> m_flyweights;  
};  
  
int main() {  
    FlyweightFactory factory;  
    Flyweight* fw1 = factory.getFlyweight("A");  
    Flyweight* fw2 = factory.getFlyweight("B");  
    Flyweight* fw3 = factory.getFlyweight("A"); // 复用 fw1  
  
    fw1->operation("X");  
    fw2->operation("Y");  
    fw3->operation("Z"); // 使用与 fw1 相同的对象   
  
    return 0;  
}

在这个示例中,Flyweight 是一个接口,定义了享元对象应该具有的操作。ConcreteFlyweight 是具体的享元类,它包含内部状态(intrinsicState)并实现了操作。FlyweightFactory 是一个工厂类,它负责创建和管理享元对象。通过使用 FlyweightFactory,我们可以确保对于相同的内部状态,只创建一个 ConcreteFlyweight 对象,并在后续请求时复用它。

4.应用场景

在 C++ 中,享元模式的应用场景主要出现在需要处理大量相似或重复对象,且这些对象的内存占用较大时。通过共享这些对象的状态,享元模式能够显著减少内存消耗,并提高系统的性能。

具体来说,以下是一些 C++ 中享元模式的应用场景:

图形界面开发:在图形用户界面中,可能需要大量的按钮、图标或其他 UI 元素。这些元素通常具有相似的外观和行为,但数量众多。通过应用享元模式,可以共享这些元素的内部状态,减少内存占用。

字符串处理:在 C++ 中,字符串的创建和销毁是一个常见的性能瓶颈。使用享元模式,可以设计一个字符串缓存池,对于相同的字符串,只在缓存池中保留一份实例,多次使用时直接引用该实例,避免了重复的创建和销毁操作。

数据库连接池:在数据库应用中,频繁地创建和关闭数据库连接会消耗大量的系统资源。通过使用享元模式实现连接池,可以复用已建立的数据库连接,提高系统性能和稳定性。

游戏开发:在游戏中,经常需要创建大量的相似对象,如棋子、怪物、子弹等。这些对象可能具有相同的属性或行为,但由于数量众多,如果每个对象都单独创建,将会占用大量的内存。使用享元模式,可以将这些对象的共享部分提取出来,只保留一份实例,从而大大减少内存消耗。

总的来说,享元模式在 C++ 中的应用场景主要是那些需要处理大量相似或重复对象,且内存消耗成为性能瓶颈的情况。通过共享对象的状态,享元模式能够优化内存使用,提高系统的整体性能。

5.总结

优点:

(1) 减少内存占用:享元模式通过共享相同或相似的对象,避免了大量相同对象的重复创建,从而显著降低了系统的内存占用。

(2) 提高系统性能:由于减少了对象的创建和销毁,享元模式可以提高系统的运行效率。特别是在需要大量创建对象的场景中,这种性能提升尤为明显。

(3) 支持高并发:在高并发的系统中,对象的创建和销毁可能会成为性能瓶颈。享元模式通过共享对象实例,减少了对象的创建和销毁,从而支持更高的并发量。

缺点:

(1) 提高了系统的复杂度:享元模式需要分离出对象的内部状态和外部状态,这增加了系统的复杂性和编程难度。同时,为了管理这些状态,可能需要引入额外的数据结构(如哈希表等),进一步增加了系统的复杂性。

(2) 可能导致系统混乱:如果外部状态具有固有化的性质,不应该随着内部状态的变化而变化,但在实际使用中违反了这一原则,就可能导致系统混乱。因此,在使用享元模式时,需要特别注意外部状态的管理。

(3) 不适合所有场景:虽然享元模式在某些场景下可以提高系统性能和减少内存占用,但并不是所有场景都适合使用享元模式。例如,当对象的状态经常变化或者对象之间的差别较大时,使用享元模式可能并不合适。

综上所述,享元模式在减少内存占用和提高系统性能方面具有明显的优势,但同时也存在提高系统复杂度和可能导致系统混乱的缺点。因此,在决定是否使用享元模式时,需要根据具体的应用场景和需求进行权衡。

相关推荐
微露清风6 小时前
系统性学习C++-第十八讲-封装红黑树实现myset与mymap
java·c++·学习
CSARImage7 小时前
C++读取exe程序标准输出
c++
一只小bit7 小时前
Qt 常用控件详解:按钮类 / 显示类 / 输入类属性、信号与实战示例
前端·c++·qt·gui
一条大祥脚7 小时前
26.1.9 轮廓线dp 状压最短路 构造
数据结构·c++·算法
项目題供诗8 小时前
C语言基础(一)
c++
@areok@9 小时前
C++opencv图片(mat)传入C#bitmap图片
开发语言·c++·opencv
鸽芷咕9 小时前
【2025年度总结】时光知味,三载同行:落笔皆是沉淀,前行自有光芒
linux·c++·人工智能·2025年度总结
羑悻的小杀马特9 小时前
指尖敲代码,笔尖写成长:2025年度总结与那些没说出口的碎碎念
linux·c++·博客之星·2025年度总结
linweidong9 小时前
C++thread pool(线程池)设计应关注哪些扩展性问题?
java·数据库·c++
cpp_25019 小时前
P2708 硬币翻转
数据结构·c++·算法·题解·洛谷