【再谈设计模式】享元模式~对象共享的优化妙手

一、引言

        在软件开发过程中,我们常常面临着创建大量细粒度对象的情况,这可能会导致内存占用过高、性能下降等问题。享元模式(Flyweight Pattern)就像是一位空间管理大师,它能够在不影响功能的前提下,有效地减少对象的数量,从而优化系统资源的使用。

二、定义与描述

        享元模式是一种结构型设计模式,它主要用于通过共享尽可能多的相似对象来减少内存使用和提高性能。其核心思想是将对象的状态分为内部状态(intrinsic state)和外部状态(extrinsic state)。内部状态是对象可共享的部分,它不会随环境的改变而改变;外部状态则是随环境变化而变化的部分,不能被共享。

三、抽象背景

        假设我们正在开发一个游戏,游戏中有许多相同类型的怪物,这些怪物可能只有一些属性(如生命值、攻击力等)的差异。如果我们为每个怪物都创建一个独立的对象,那么随着怪物数量的增加,内存的消耗将变得非常大。享元模式就可以用来解决这个问题,将怪物的通用属性(如外观、基本行为等内部状态)进行共享,而将每个怪物特有的属性(如当前生命值、当前攻击力等外部状态)单独处理。

四、适用场景与现实问题解决

  • 图形绘制系统
    • 在图形绘制系统中,可能需要绘制大量相同类型的图形,如圆形、矩形等。这些图形的形状(内部状态)是固定的,但它们的位置、颜色(外部状态)可能不同。通过享元模式,可以共享图形的形状对象,减少内存占用。

  • 文档编辑器中的字符处理
    • 文档编辑器中有大量的字符,每个字符的字体样式、大小等属性可能不同,但字符的基本形状(内部状态)是相同的。享元模式可以用来共享字符的基本形状对象。

五、享元模式的现实生活的例子

  • 汽车租赁公司
    • 汽车租赁公司有多种类型的汽车可供租赁,如轿车、SUV等。每一种类型的汽车(内部状态)是固定的,包括车型、座位数等。而汽车的颜色、当前里程数(外部状态)是随每一次租赁而变化的。租赁公司可以将相同类型的汽车看作是享元对象,共享汽车的基本信息,从而更好地管理车辆资源。

  • 咖啡店的咖啡杯
    • 咖啡店有不同种类的咖啡杯,如拿铁杯、卡布奇诺杯等。杯子的形状(内部状态)是固定的,但是杯子里咖啡的量、是否加糖(外部状态)是不同的。咖啡店可以将相同类型的杯子看作享元对象,共享杯子的基本形状信息。

六、初衷与问题解决

        初衷是为了减少内存中对象的数量,提高系统的性能和资源利用率。通过共享内部状态,避免了创建大量重复的对象,从而解决了因对象数量过多导致的内存占用过大和性能下降的问题。

七、代码示例

Java示例

类图:

  • FlyweightFactory 类有一个私有属性 flyweights(类型为 Map<String, Flyweight>)用于存储享元对象,并且有 getFlyweight 方法,根据传入的 key 来获取或创建具体的享元对象。
  • Flyweight 是抽象类,有受保护的属性 key,构造方法以及抽象方法 operation,定义了享元对象的基本结构和行为规范。
  • ConcreteFlyweight 类继承自 Flyweight 类,实现了自己的构造方法,并覆写了 operation 方法,用于提供具体的享元行为实现。

流程图:

        首先创建 FlyweightFactory 对象,然后两次调用 getFlyweight 方法来获取享元对象(第二次调用时会复用第一次创建的对象,因为已经存在对应 key 的对象了),最后分别调用获取到的享元对象的 operation 方法来执行具体操作。 

代码:

import java.util.HashMap;
import java.util.Map;

// 享元工厂类
class FlyweightFactory {
    private Map<String, Flyweight> flyweights = new HashMap<>();

    public Flyweight getFlyweight(String key) {
        if (!flyweights.containsKey(key)) {
            flyweights.put(key, new ConcreteFlyweight(key));
        }
        return flyweights.get(key);
    }
}

// 抽象享元类
abstract class Flyweight {
    protected String key;

    public Flyweight(String key) {
        this.key = key;
    }

    abstract void operation();
}

// 具体享元类
class ConcreteFlyweight extends Flyweight {
    public ConcreteFlyweight(String key) {
        super(key);
    }

    @Override
    void operation() {
        System.out.println("具体享元 " + key + " 被调用");
    }
}

public class Main {
    public static void main(String[] args) {
        FlyweightFactory factory = new FlyweightFactory();
        Flyweight flyweight1 = factory.getFlyweight("A");
        Flyweight flyweight2 = factory.getFlyweight("A");
        flyweight1.operation();
        flyweight2.operation();
    }
}

C++示例

#include <iostream>
#include <unordered_map>

// 抽象享元类
class Flyweight {
public:
    virtual void operation() = 0;
    virtual ~Flyweight() {}
};

// 具体享元类
class ConcreteFlyweight : public Flyweight {
private:
    std::string key;
public:
    ConcreteFlyweight(std::string key) : key(key) {}
    void operation() override {
        std::cout << "具体享元 " << key << " 被调用" << std::endl;
    }
};

// 享元工厂类
class FlyweightFactory {
private:
    std::unordered_map<std::string, Flyweight*> flyweights;
public:
    Flyweight* getFlyweight(std::string key) {
        if (flyweights.find(key) == flyweights.end()) {
            flyweights[key] = new ConcreteFlyweight(key);
        }
        return flyweights[key];
    }
    ~FlyweightFactory() {
        for (auto it : flyweights) {
            delete it.second;
        }
    }
};

int main() {
    FlyweightFactory factory;
    Flyweight* flyweight1 = factory.getFlyweight("A");
    Flyweight* flyweight2 = factory.getFlyweight("A");
    flyweight1->operation();
    flyweight2->operation();
    return 0;
}

Python示例

class FlyweightFactory:
    def __init__(self):
        self.flyweights = {}

    def get_flyweight(self, key):
        if key not in self.flyweights:
            self.flyweights[key] = ConcreteFlyweight(key)
        return self.flyweights[key]


class Flyweight:
    def __init__(self, key):
        self.key = key

    def operation(self):
        pass


class ConcreteFlyweight(Flyweight):
    def operation(self):
        print(f"具体享元 {self.key} 被调用")


if __name__ == "__main__":
    factory = FlyweightFactory()
    flyweight1 = factory.get_flyweight("A")
    flyweight2 = factory.get_flyweight("A")
    flyweight1.operation()
    flyweight2.operation()

Go示例

package main

import (
    "fmt"
)

// 抽象享元接口
type Flyweight interface {
    operation()
}

// 具体享元结构体
type ConcreteFlyweight struct {
    key string
}

func (cf *ConcreteFlyweight) operation() {
    fmt.Printf("具体享元 %s 被调用\n", cf.key)
}

// 享元工厂结构体
type FlyweightFactory struct {
    flyweights map[string]Flyweight
}

func NewFlyweightFactory() *FlyweightFactory {
    return &FlyweightFactory{
        flyweights: make(map[string]Flyweight),
    }
}

func (ff *FlyweightFactory) getFlyweight(key string) Flyweight {
    if _, ok := ff.flyweights[key];!ok {
        ff.flyweights[key] = &ConcreteFlyweight{key}
    }
    return ff.flyweights[key]
}

func main() {
    factory := NewFlyweightFactory()
    flyweight1 := factory.getFlyweight("A")
    flyweight2 := factory.getFlyweight("A")
    flyweight1.operation()
    flyweight2.operation()
}

八、享元模式的优缺点

  • 优点

    • 减少内存占用:通过共享对象,大大减少了创建对象所需的内存空间,特别是在处理大量相似对象时效果显著。
    • 提高性能:减少了对象的创建和销毁操作,从而提高了系统的运行速度。
    • 易于维护:将对象的内部状态和外部状态分离,使得代码结构更加清晰,易于理解和维护。
  • 缺点

    • 增加复杂性:需要额外的代码来管理享元对象的创建、共享和维护,这可能会增加系统的复杂性。
    • 外部状态管理:外部状态的处理需要额外的设计考虑,如果处理不当可能会导致逻辑混乱。

九、享元模式的升级版

        一种常见的升级版是组合享元模式(Composite Flyweight Pattern)。在这种模式下,享元对象可以组合成更复杂的结构。例如,在图形绘制系统中,不仅可以共享单个图形(如圆形、矩形)的享元对象,还可以将这些享元对象组合成更复杂的图形(如由多个圆形和矩形组成的复杂图案),而这个复杂图案本身也可以作为一个享元对象被共享。这样可以进一步提高系统的灵活性和资源利用率。

思维导图:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/943439.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

用Python写炸金花游戏

文章目录 **代码分解与讲解**1. **扑克牌的生成与洗牌**2. **给玩家发牌**3. **打印玩家的手牌**4. **定义牌的优先级**5. **判断牌型**6. **确定牌型优先级**7. **比较两手牌的大小**8. **打印结果** 完整代码 以下游戏规则&#xff1a; 那么我们要实现的功能&#xff0c;就是…

WebRTC服务质量(07)- 重传机制(04) 接收NACK消息

WebRTC服务质量&#xff08;01&#xff09;- Qos概述 WebRTC服务质量&#xff08;02&#xff09;- RTP协议 WebRTC服务质量&#xff08;03&#xff09;- RTCP协议 WebRTC服务质量&#xff08;04&#xff09;- 重传机制&#xff08;01) RTX NACK概述 WebRTC服务质量&#xff08;…

Cadence学习笔记 11 PCB中器件放置

基于Cadence 17.4&#xff0c;四层板4路HDMI电路 更多Cadence学习笔记&#xff1a;Cadence学习笔记 1 原理图库绘制Cadence学习笔记 2 PCB封装绘制Cadence学习笔记 3 MCU主控原理图绘制Cadence学习笔记 4 单片机原理图绘制Cadence学习笔记 5 四路HDMI原理图绘制Cadence学习笔记…

Docker 入门:如何使用 Docker 容器化 AI 项目(二)

四、将 AI 项目容器化&#xff1a;示例实践 - 完整的图像分类与 API 服务 让我们通过一个更完整的 AI 项目示例&#xff0c;展示如何将 AI 项目容器化。我们以一个基于 TensorFlow 的图像分类模型为例&#xff0c;演示如何将训练、推理、以及 API 服务过程容器化。 4.1 创建 …

三层交换机配置

一&#xff0c;三层交换 概念&#xff1a;三层交换技术就是&#xff1a;二层交换技术三层转发技术(路由器功能)。它解决了局域网中网段划分之后&#xff0c;网段中子网必须依赖路由器进行管理的局面&#xff0c;解决了传统路由器低速&#xff0c;复杂所造成的网络瓶颈问题。 …

LabVIEW应用在工业车间

LabVIEW作为一种图形化编程语言&#xff0c;以其强大的数据采集和硬件集成功能广泛应用于工业自动化领域。在工业车间中&#xff0c;LabVIEW不仅能够实现快速开发&#xff0c;还能通过灵活的硬件接口和直观的用户界面提升生产效率和设备管理水平。尽管其高成本和初期学习门槛可…

【CSS in Depth 2 精译_094】16.2:CSS 变换在动效中的应用(下)——导航菜单的文本标签“飞入”特效与交错渲染效果的实现

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第五部分 添加动效 ✔️【第 16 章 变换】 ✔️ 16.1 旋转、平移、缩放与倾斜 16.1.1 变换原点的更改16.1.2 多重变换的设置16.1.3 单个变换属性的设置 16.2 变换在动效中的应用 16.2.1 放大图标&am…

Qt使用QZipWriter和QZipReader来解压、压缩文件

首先感谢这位博主的无私奉献&#xff1a;Qt - 实现压缩文件、文件夹和解压缩操作 - [BORUTO] - 博客园 多文件和目录压缩时&#xff0c;不改变原始文件和目录的相对位置结构&#xff0c;需要在addFile和addDirectory时&#xff0c;需要带上相对路径&#xff0c;如下&#xff1…

PH热榜 | 2024-12-23

1. Websparks 标语&#xff1a;让你的创意变为现实的AI软件工程师 介绍&#xff1a;现在&#xff0c;构建网页应用从未如此简单快捷&#xff01;WebSparks是一个基于人工智能的平台&#xff0c;它能让开发者、设计师&#xff0c;甚至不懂编程的人&#xff0c;都能在很短的时间…

Opencv之对图片的处理和运算

Opencv实现对图片的处理和修改 目录 Opencv实现对图片的处理和修改灰度图读取灰度图转换灰度图 RBG图单通道图方法一方法二 单通道图显色合并单通道图 图片截取图片打码图片组合缩放格式1格式2 图像运算图像ma[m:n,x:y]b[m1:n1,x1:y1] add加权运算 灰度图 读取灰度图 imread(‘…

OpenLinkSaas使用手册-Git工具

在OpenLinkSaas的工具箱里面&#xff0c;最基础的一个就是Git仓库管理。Git仓库功能让git使用更加简单和强大&#xff0c;不仅可以使用常规的commit/pull/push/branch等功能外&#xff0c;还连接了Git仓库供应商的能力。 OpenLinkSass支持使用国内主流的Git仓库供应商的账号登录…

WebRTC服务质量(12)- Pacer机制(04) 向Pacer中插入数据

WebRTC服务质量&#xff08;01&#xff09;- Qos概述 WebRTC服务质量&#xff08;02&#xff09;- RTP协议 WebRTC服务质量&#xff08;03&#xff09;- RTCP协议 WebRTC服务质量&#xff08;04&#xff09;- 重传机制&#xff08;01) RTX NACK概述 WebRTC服务质量&#xff08;…

protobuf学习使用

1、概述 protobuf是Google开发的一种语言中立、平台无关、可扩展的序列化结构数据格式。允许定义一次数据结构&#xff0c;然后可以使用各种支持的语言来生成代码&#xff0c;以轻松地读写这些结构到一个二进制流中&#xff0c;如网络传输或文件&#xff0c;Protobuf支持多种编…

CTFHUB-web进阶-php

我们用蚁剑中的这个插件来做这些关卡 一.LD_PRELOAD 发现这里有一句话木马&#xff0c;并且把ant给了我们&#xff0c;我们直接连接蚁剑 右键 选择模式&#xff0c;都可以试一下&#xff0c;这里第一个就可以 点击开始 我们进入到目录&#xff0c;刷新一下&#xff0c;会有一个…

相机、镜头参数详解以及相关计算公式

一、工业相机参数 1、分辨率 相机每次采集图像的像素点数&#xff0c;也是指这个相机总共有多少个感光晶片。在采集图像时&#xff0c;相机的分辨率对检测精度有很大的影响&#xff0c;在对同样打的视场成像时&#xff0c;分辨率越高&#xff0c;对细节的展示越明显。 相机像素…

取多个集合的交集

1.我们取多个集合的交集&#xff0c;先把各个集合放入list中 List < Set < String > > listnew ArrayList<>();HashSet<String> set1new HashSet<>();set1.add( "A" );set1.add("B" );set1.add("C" );HashSet<…

leetcode之hot100---206环形链表(C++)

思路一&#xff1a;哈希表 遍历链表&#xff0c;同时借助哈希表判断当前遍历到的节点是否已经被访问过&#xff0c;如果当前节点已被访问过&#xff0c;则说明存在环 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* L…

文档解析丨高效准确的PDF解析工具,赋能企业非结构化数据治理

在数据为王的时代浪潮中&#xff0c;企业数据治理已成为组织优化运营、提高竞争力的关键。随着数字化进程的加速&#xff0c;企业所积累的数据量呈爆炸式增长&#xff0c;数据类型也愈发多样化&#xff0c;这些数据构成了现代企业数据资产的重要组成部分。 然而&#xff0c;传…

优化 invite_codes 表的 SQL 创建语句

-- auto-generated definition create table invite_codes (id int auto_incrementprimary key,invite_code varchar(6) not null comment 邀请码&#xff0c;6位整数&#xff0c;确保在有效期内…

C语言基础:指针(数组指针与指针数组)

数组指针与指针数组 数组指针 概念&#xff1a;数组指针是指向数组的指针&#xff0c;本质上还是指针 特点&#xff1a; 先有数组&#xff0c;后有指针 它指向的是一个完整的数组 一维数组指针&#xff1a; 语法&#xff1a; 数据类型 (*指针变量名)[行容量][列容量]; 案…