12结构型设计模式——享元模式

#程序员如何平衡日常编码工作与提升式学习?#

一、享元模式简介

享元模式(Flyweight Pattern)是一种结构型设计模式,它旨在通过共享大量细粒度对象来减少内存使用和提高性能。这个模式适用于那些在系统中存在大量相似对象的情况,通过共享相同的对象实例来减少内存消耗。

享元”即“共享单元”的意思。在享元模式中,它指的是可以被共享的细粒度对象。

  • 享元模式的核心思想是分离对象的变与不变部分,将可以共享的部分(不变部分)提取出来进行共享,以减少内存消耗和提高系统性能。
  • 享元模式通常涉及将对象信息分为内部状态和外部状态。内部状态是存储在享元对象内部且不会随环境改变而改变的状态,是可以共享的部分。外部状态则是随环境改变而改变、不可共享的状态,需要由客户端传入享元对象。

需求背景 

        在面向对象系统的代码设计实现中,创建对象是最常见的操作。如果一个程序创建了太多的对象,就会造成很大的存储开销。特别是针对大量轻量级(Flyweight)的对象,他们可能有很多相似的行为。比如设计文档编辑器的过程中,假设为每一个字母都创建一个对象,拉丁字母26个,希腊字母24个,俄语字母33个,尤其是汉字多达3000个。假设就创建(26+24+33+3000)==3083个对象,然后每一个字母调用了10万次,每一个字母在不同的位置可能存在不同的含义,例如拉丁字母a,可能是大写A或者a字体大小不同,甚至是形态不同(三维、平面或者斜体、粗体等)。需要为每一个a创建10万次对象吗?很明显这不现实。因此我们引入了享元模式,将a对象的状态分为内部状态外部状态,将相同的(不会变化的)部分作为内部状态存储在享元对象中,将不同的(会变化的)部分作为外部状态存储在外部对象中,在特殊的条件下引入外部对象以参数或其他形式传递给享元对象。从而达成节省内存空间并实现共享对象的目标。

享元模式的结构图

享元模式的概念和实现步骤

主要概念
  1. 享元(Flyweight): 是一个接口或者抽象类,定义了具体享元对象需要实现的功能。
  2. 具体享元(ConcreteFlyweight): 实现了享元接口,并在内部维护了享元对象的状态。
  3. 享元工厂(FlyweightFactory): 用于创建和管理享元对象,确保共享的享元对象被复用。
  4. 外部状态(Extrinsic State): 享元对象不负责管理的状态(UnshareConcreteFlyweight),需要由客户端传入,以确保享元对象的状态不被修改。
实现步骤
  1. 创建享元接口: 定义享元对象需要实现的操作。
  2. 实现具体享元类: 实现享元接口,管理内部状态。
  3. 创建享元工厂: 负责创建和管理具体享元对象,确保共享。
  4. 定义外部状态: 客户端需要维护的状态。

享元模式应用场景和优缺点

在实际开发中,享元模式常用于数据库连接池的管理、多线程线程池的使用以及游戏渲染优化等场景。其特点是当系统中存在大量重复或相似的对象,且这些对象消耗大量内存时,使用享元模式可以显著减少内存使用。例如,在文本编辑器中,每一个字符可以是一个享元;在游戏开发中,大量使用的粒子效果、树木、花草等也可以通过享元模式来优化内存使用。

  • 优点:显著减少内存消耗,提高系统性能和资源利用率。
  • 缺点:增加系统设计的复杂性,需要维护一个享元池,并考虑线程安全问题。同时,读取外部状态可能会使运行时间变长。

二、享元模式的设计方法

展示如何使用享元模式来管理拉丁字母的不同变体,避免为每一个字母的变体创建新的对象,从而节省内存并提高性能。

flyweight.cpp

#include <iostream>
#include <string>
#include <map>
#include <memory>

// 享元接口
class Flyweight {
public:
    virtual ~Flyweight() {}
    virtual void draw(const std::string& externalState) const = 0;
};

// 具体享元
class ConcreteFlyweight : public Flyweight {
public:
    ConcreteFlyweight(const std::string& internalState) : internalState_(internalState) {}
    
    void draw(const std::string& externalState) const override {
        std::cout << "Drawing " << internalState_ << " with " << externalState << std::endl;
    }

private:
    std::string internalState_; // 内部状态
};

// 享元工厂
class FlyweightFactory {
public:
    std::shared_ptr<Flyweight> getFlyweight(const std::string& internalState) {
        if (flyweights_.find(internalState) == flyweights_.end()) {
            flyweights_[internalState] = std::make_shared<ConcreteFlyweight>(internalState);
        }
        return flyweights_[internalState];
    }

private:
    std::map<std::string, std::shared_ptr<Flyweight>> flyweights_;
};


void doWorking() {
    FlyweightFactory factory;

    // 创建一些共享的字母对象
    auto a1 = factory.getFlyweight("a");
    auto A1 = factory.getFlyweight("A");

    // 绘制这些字母对象,带上不同的外部状态
    a1->draw("12pt, 粗体");
    a1->draw("14pt, 立方体");
    A1->draw("12pt, 方方正正的斜体");
    A1->draw("16pt, 五彩斑斓的黑体");

    return ;
}

int main() {
	//模拟干活
	doWorking();
	return 0;
}

运行效果

三、总结

享元模式的核心思想就是分离对象的变与不变部分,将可以共享的部分(不变部分)提取出来进行共享,以减少内存消耗和提高系统性能,例如flyweight.cpp中的代码设计中把字体的大小作为相同(不变)的部分,作为共享对象数据,而不同的形态则作为外部对象引入。

  • 14
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值