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

news2024/12/29 0:56:27

一、引言

        在软件开发过程中,我们常常面临着创建大量细粒度对象的情况,这可能会导致内存占用过高、性能下降等问题。享元模式(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)。在这种模式下,享元对象可以组合成更复杂的结构。例如,在图形绘制系统中,不仅可以共享单个图形(如圆形、矩形)的享元对象,还可以将这些享元对象组合成更复杂的图形(如由多个圆形和矩形组成的复杂图案),而这个复杂图案本身也可以作为一个享元对象被共享。这样可以进一步提高系统的灵活性和资源利用率。

思维导图:

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Unity 读Excel,读取xlsx文件解决方案

Unity读取表格数据 效果&#xff1a; 思路&#xff1a; Unity可以解析Json&#xff0c;但是读取Excel需要插件的帮助&#xff0c;那就把这个功能分离开&#xff0c;读表插件就只管读表转Json&#xff0c;Unity就只管Json解析&#xff0c;中间需要一个存储空间&#xff0c;使用…

WebRTC服务质量(11)- Pacer机制(03) IntervalBudget

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;…

STM32完全学习——FATFS0.15移植SD卡

一、下载FATFS源码 大家都知道使用CubMAX可以很快的将&#xff0c;FATFS文件管理系统移植到单片机上&#xff0c;但是别的芯片没有这么好用的工具&#xff0c;就需要自己从官网下载源码进行移植。我们首先解决SD卡的驱动问题&#xff0c;然后再移植FATFS文件管理系统。 二、SD…

Ollama+OpenWebUI+llama3本地部署

引言 llama3在4月19日刚刚发布&#xff0c;官方的对比结果中在开源模型中堪称世界第一&#xff0c;整好周六日有时间&#xff0c;在魔搭社区上测试一下 2 安装Ollama 2.1 下载Ollama 登录Ollama官网下载Ollama安装包 GitHub&#xff1a;https://github.com/ollama/ollama?t…

新品:SA628F39大功率全双工音频传输模块

SA628F39是一款高集成度的8W大功率全双工无线数据语音一体通话模块&#xff0c;专为高效、稳定的远程通信设计。该模块内置高速微控制器、高性能射频芯片、功率放大器、ESD静电保护和硬件看门狗芯片&#xff0c;具备反接保护、过流过压保护和防死机保护等多重安全功能&#xff…

Unity EasyAR入门教程

文章目录 Easy不用下载盗版安装SDK注册应用ID最简单的识别注意点 Easy不用下载盗版 EasyAR可以免费试用&#xff0c;如果不在乎水印&#xff0c;直接去官网下载即可 EasyAR官网 安装SDK 先下载&#xff0c;官网找到下载页&#xff0c;如下选择 下载后不是普通的unityPackag…

ID卡网络读卡器Python小程序开发

UDP网络读卡器Python示例代码。 主页UI设计界面&#xff1a; UI代码&#xff1a; from PyQt5 import QtCore, QtGui, QtWidgetsclass Ui_Frame(object):def setupUi(self, Frame):Frame.setObjectName("Frame")Frame.resize(1082, 685)font QtGui.QFont()font.set…

yarn list --pattern vuex-module-decorators

dgqdgqdeMac-mini spid-admin % yarn list --pattern vuex-module-decorators yarn list v1.22.22 └─ vuex-module-decorators0.16.1 ✨ Done in 0.24s.好的&#xff0c;这段代码是一个典型的 Vuex 模块定义&#xff0c;使用了 vuex-module-decorators 库。这个库为 Vuex 提…

微机接口课设——基于Proteus和8086的打地鼠设计(8255、8253、8259)Proteus中Unknown 1-byte opcode / Unknown 2-byte opcode错误

原理图设计 汇编代码 ; I/O 端口地址定义 IOY0 EQU 0600H IOY1 EQU 0640H IOY2 EQU 0680HMY8255_A EQU IOY000H*2 ; 8255 A 口端口地址 MY8255_B EQU IOY001H*2 ; 8255 B 口端口地址 MY8255_C EQU IOY002H*2 ; 8255 C 口端口地址 MY8255_MODE EQU IOY003H*2 ; …

【HENU】河南大学计院2024 计算机网络 期末复习知识点

和光同尘_我的个人主页 一直游到海水变蓝。 计网复习 第一章互联网组成类别交换方式分组交换的要点&#xff1a;分组交换的优点&#xff1a; 网络性能指标体系结构网络协议五层协议 第二章&#xff1a;物理层物理层的主要任务&#xff08;四大特性&#xff09;通信的三种方式…

chatwoot 开源客服系统搭建

1. 准备开源客服系统&#xff08;我是用的Chatwoot &#xff09; 可以选择以下开源客服系统作为基础&#xff1a; Chatwoot: 功能强大&#xff0c;支持多渠道客户对接&#xff0c;&#xff08;支持app&#xff0c;web&#xff09;。Zammad: 现代的开源工单系统。FreeScout: 免…

python爬虫----爬取视频实战

python爬虫-爬取视频 本次爬取&#xff0c;还是运用的是requests方法 首先进入此网站中&#xff0c;选取你想要爬取的视频&#xff0c;进入视频页面&#xff0c;按F12&#xff0c;将网络中的名称栏向上拉找到第一个并点击&#xff0c;可以在标头中&#xff0c;找到后续我们想要…

【PPTist】表格功能

前言&#xff1a;这篇文章来探讨一下表格功能是怎么实现的吧&#xff01; 一、插入表格 我们可以看到&#xff0c;鼠标移动到菜单项上出现的提示语是“插入表格” 那么就全局搜索一下&#xff0c;就发现这个菜单在 src/views/Editor/CanvasTool/index.vue 文件中 <Popov…

Web安全攻防入门教程——hvv行动详解

Web安全攻防入门教程 Web安全攻防是指在Web应用程序的开发、部署和运行过程中&#xff0c;保护Web应用免受攻击和恶意行为的技术与策略。这个领域不仅涉及防御措施的实现&#xff0c;还包括通过渗透测试、漏洞挖掘和模拟攻击来识别潜在的安全问题。 本教程将带你入门Web安全攻防…

游戏开发线性空间下PS工作流程

前言 使用基于物理的渲染&#xff0c;为了保证光照计算的准确&#xff0c;需要使用线性空间&#xff1b; 使用线性空间会带来一个问题&#xff0c;ui 在游戏引擎中的渲染结果与 PS 中的不一致&#xff1a; PS&#xff08;颜色空间默认是sRGB伽马空间&#xff09;&#xff1a…

Segment Routing Overview

大家觉得有意义和帮助记得及时关注和点赞!!! Segment Routing (SR) 是近年来网络领域的一项新技术&#xff0c;“segment” 在这里 指代网络隔离技术&#xff0c;例如 MPLS。如果快速回顾网络设计在过去几十年的 发展&#xff0c;我们会发现 SR 也许是正在形成的第三代网络设计…

【连续学习之随机初始化算法 】2024Nature期刊论文Loss of plasticity in deep continual learning

1 介绍 年份&#xff1a;2024 期刊&#xff1a;Nature Dohare S, Hernandez-Garcia J F, Lan Q, et al. Loss of plasticity in deep continual learning[J]. Nature, 2024, 632(8026): 768-774. 本文提出的算法是“持续反向传播”&#xff08;continual backpropagation&a…

【NODE】01-fs和path常用知识点

前言 最近在使用express-generator知识进行搭建前后端通信&#xff0c;其中有些知识点涉及到nodejs的fs和path核心模块&#xff0c;因此另写一篇文章进行介绍和代码案例练习。 fs&#xff08;文件系统&#xff09;和 path 是 Node.js 的核心模块&#xff0c;用于文件操作和路径…

两分钟解决:vscode卡在设置SSH主机,VS Code-正在本地初始化VSCode服务器

问题原因 remote-ssh还是有一些bug的&#xff0c;在跟新之后可能会一直加载初始化SSH主机解决方案 1.打开终端2.登录链接vscode的账号&#xff0c;到家目录下3.找到 .vscode-server文件,删掉这个文件4.重启 vscode 就没问题了

干货ScottPlot4向ScottPlot5迁移

干货ScottPlot4向ScottPlot5迁移 干货满满1.背景2.需求的引出3.先说结论1.好消息2.坏消息 4.迁移的部分笔记ColorScottPlot.PlottableScottPlot.Plottables中的对象如何定义添加 ScottPlot.Plottable.ScatterPlot 对象ScatterPolygonMarker也类似 Scatter的marker formsPlot1Re…