详解享元模式

news2025/2/8 5:57:02

引言

        在计算机中,内存是非常宝贵的资源,而程序中可能会有大量相似或相同的对象,它们的存在浪费了许多空间。而享元模式通过共享这些对象,从而解决这种问题的。

1.概念

        享元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,它是一种对象结构型模式。

        享元对象能做到共享的关键是区分了内部状态(Intrinsic State)和外部状态(Extrinsic State):

        (1)内部状态:存储在享元对象内部并且不会随环境改变而改变的状态,内部状态可以共享。如字符不会随外部环境的变化而变化,无论在任何环境下字符“a”始终是“a”,都不会变成“b”。

        (2)外部状态:随环境改变而改变的、不可以共享的状态。外部状态通常由客户端保存,并在享元对象被创建之后,需要用的时候再传入到享元对象内部。一个外部状态与另一个外部状态之间是相互独立的。如字符的颜色和字体大小,字符“a”可以是红色的,也可以是绿色的,可以是小四号字体,也可以是三号字体。

        区分内部状态和外部状态保证可以将具有相同内部状态的对象存储在享元池中,享元池中的对象是可以实现共享的,需要的时候就将对象从享元池中取出,实现对象的复用。通过向取出的对象注入不同的外部状态,可以得到一系列相似的对象,而这些对象在内存中实际上只存储一份。

2.模式结构

3.模式分析

        Flyweight:抽象享元类,可以是接口或抽象类,在抽象享元类中声明了具体享元类公共的方法,这些方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法来设置外部数据(外部状态)。

        ConcreteFlyweight:具体享元类,它实现了抽象享元类,其实例称为享元对象。在具体享元类中为内部状态提供了存储空间,通常我们可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象。

        UnsharedConcreteFlyweight:非共享具体享元类,并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可设计为非共享具体享元类,当需要一个非共享具体享元类的对象时可以直接通过实例化创建。

        享元类的设计很重要,核心代码如下:

public class Flyweight {

    //内部状态intrinsicState作为成员变量,同一个享元对象其内部状态是一致的

    private String intrinsicState;

    public Flyweight(String intrinsicState) {

        this.intrinsicState = intrinsicState;

    }

    //外部状态extrinsicState在使用时由外部设置,不保存在享元对象中

    public void operation(String extrinsicState){

        //设置外部状态

    }

}

        FlyweightFactory:享元工厂类,享元工厂类用于创建并管理享元对象,它针对抽象享元类编程,将各种类型的具体享元对象存储在一个享元池中,享元池一般设计为一个存储“键值对”的集合(或其他类型的集合),可以结合工厂模式进行设计。当用户请求一个具体享元对象时,享元工厂提供一个存储在享元池中已创建的实例或者创建一个新的实例(如果不存在的话),返回新创建的实例并将其存储在享元池中。核心代码如下:

public class FlyweightFactory {

    //定义一个HashMap用于存储享元对象,实现享元池

    private HashMap<String,Flyweight> flyweights = new HashMap<>();

    public Flyweight getFlyweight(String key) {

        //如果对象存在,则直接从享元池获取

        if (flyweights.containsKey(key)) {

            return (Flyweight) flyweights.get(key);

        } else {//如果对象不存在,先创建一个新的对象添加到享元池中,然后返回

            Flyweight fw = new ConcreteFlyweight();

            flyweights.put(key, fw);

            return fw;

        }

    }

}

4.具体实例分析

        Shape:抽象享元类,包含享元类的内部状态shape和外部状态xy坐标。具体代码如下:

//抽象享元类

public abstract class  {

    //内部状态是shape

    public abstract String getShape();

    //外部状态是中心xy坐标

    public void draw(int x,int y){

        System.out.println("形状:" + this.getShape() + " 坐标x:" + x + " 坐标y:" + y);

    }

}

        Circle:具体享元类,继承抽象享元类Shape,并实现getShape()方法,返回实例的内部状态:形状是圆。具体代码如下:

//具体享元类:圆

public class Circle extends Shape{

    public String getShape(){

        return "圆";

    }

}

        Rectangle:具体享元类,继承抽象享元类Shape,并实现getShape()方法,返回实例的内部状态:形状是矩形。具体代码如下:

//具体享元类:矩形

public class Rectangle extends Shape{

    public String getShape(){

        return "矩形";

    }

}

        ShapeFlyweightFactory:享元工厂类,使用单例模式设计享元工厂类,保证全局只有唯一的工厂实例(懒汉模式实现)。同时创建HashMap作为共享享元类的享元池,保证不重复创建相同或相似的对象。具体代码如下:

//享元工厂类:单例模式

public class ShapeFlyweightFactory {

    //单例模式实现只有一个全局唯一的工厂(懒汉模式)

    private static ShapeFlyweightFactory shapeFlyweightFactory = new ShapeFlyweightFactory();

    //定义一个HashMap用于存储享元对象,实现享元池

    private static HashMap<String,Shape> flyweights = new HashMap<>();

    //返回唯一实例工厂

    public static ShapeFlyweightFactory getInstance(){

        return shapeFlyweightFactory;

    }

    public Shape getShapeFlyweight(String shape) {

        //如果对象存在,则直接从享元池获取

        if (flyweights.containsKey(shape)) {

            return flyweights.get(shape);

        } else {//如果对象不存在,先创建一个新的对象添加到享元池中,然后返回

            Shape flyweight;

            if(shape.equals("圆")){

                flyweight = new Circle();

            }else if(shape.equals("矩形")) {

                flyweight = new Rectangle();

            }else{

                System.out.println("系统没有这个形状!");

                return null;

            }

            flyweights.put(shape, flyweight);

            return flyweight;

        }

    }

}

        Client:客户端,创建多个相同对象,观察是否是同一个对象,并给对象添加外部状态。具体代码如下:

public class Client {

    public static void main(String[] args) {

        //获得唯一的享元工厂实例

        ShapeFlyweightFactory shapeFlyweightFactory = ShapeFlyweightFactory.getInstance();

        //从工厂中获得形状

        Shape circle1 = shapeFlyweightFactory.getShapeFlyweight("圆");

        Shape circle2 = shapeFlyweightFactory.getShapeFlyweight("圆");

        System.out.println("circle1==circle2? " + (circle1 == circle2));

        Shape rectangle1 = shapeFlyweightFactory.getShapeFlyweight("矩形");

        Shape rectangle2 = shapeFlyweightFactory.getShapeFlyweight("矩形");

        System.out.println("rectangle1==rectangle2? " + (rectangle1 == rectangle2));

        //为形状添加外部状态:颜色

        circle1.draw(1,2);

        circle2.draw(2,3);

        rectangle1.draw(1,3);

        rectangle2.draw(4,6);

    }

}

        运行代码,结果如下:

        结果显示,创建多个相同对象,享元模式的背后只会创建一个,并存储在享元池中共享,这样就节省了内存资源。

5.模式扩展

        (1)在享元模式的享元工厂类中通常提供一个静态的工厂方法用于返回享元对象,使用简单工厂模式来生成享元对象。

        (2)在一个系统中,通常只有唯一一个享元工厂,因此可以使用单例模式进行享元工厂类的设计。

        (3)去掉非共享具体享元类,标准的享元模式就演变为单纯享元模式,即单纯享元模式中所有具体享元类都可以共享。

        (4)享元模式+组合模式=复合享元模式,统一对多个享元对象设置外部状态。

        通过复合享元模式,可以确保复合享元类CompositeConcreteFlyweight中所包含的每个单纯享元类ConcreteFlyweight都具有相同的外部状态,而这些单纯享元的内部状态往往可以不同。如果希望为多个内部状态不同的享元对象设置相同的外部状态,可以考虑使用复合享元模式。

        (5)Java中String类采用享元模式。

public class StringFlyweight {

    public static void main(String[] args) {

        String str1 = "abcd";

        String str2 = "abcd";

        String str3 = "ab" + "cd";

        String str4 = "ab";

        str4 += "cd";

        System.out.println(str1 == str2);

        System.out.println(str1 == str3);

        System.out.println(str1 == str4);

        str2 += "e";

        System.out.println(str1 == str2);

    }

}

        这段代码的输出如下:

        在Java中,如果每次执行String str1 = “abcd”创建一个新的字符串对象,内存开销将会很大。为此,Java设计了字符串常量池,字符串常量池中存储着唯一的字符串对象。每次创建相同的字符串都是对这唯一对象的引用,因此str1==str2==str3成立。而str4创建时由于初始值是“ab”,进行字符串连接操作Java为其重新分配了内存,创建了一个新的对象“abcd”,因此str4!=str1。而str2再次进行连接操作时,也创建了一个新的对象“abcde”,因此str2此时和str1指向的不是同一个对象。

        关于Java String类这种在修改享元对象时,先将原有对象复制一份,然后在新对象上再实施修改操作的机制称为“Copy On Write”。

6.优缺点

        主要优点如下:

        (1)可以极大减少内存中对象的数量,使得相同或相似对象在内存中只保存一份,从而可以节约系统资源,提高系统性能。

        (2)享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。

        主要缺点如下:

        (1)享元模式使得系统变得复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂化。

        (2)为了使对象可以共享,享元模式需要将享元对象的部分状态外部化,而读取外部状态将使得运行时间变长。

7.适用情况

        (1)一个系统有大量相同或者相似的对象,造成内存的大量耗费。

        (2)对象的大部分状态都可以外部化,可以将这些外部状态传入对象中。

        (3)在使用享元模式时需要维护一个存储享元对象的享元池,而这需要耗费一定的系统资源,因此,应当在需要多次重复使用享元对象时才值得使用享元模式。

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

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

相关文章

openEuler22.03LTS系统升级docker至26.1.4以支持启用ip6tables功能

本文记录了openEuler22.03LTS将docker升级由18.09.0升级至26.1.4的过程&#xff08;当前docker最新版本为27.5.1&#xff0c;生产环境为保障稳定性&#xff0c;选择升级到上一个大版本26的最新小版本&#xff09;。 一、现有环境 1、系统版本 [rootlocalhost opt]# cat /etc…

< OS 有关 > Ubuntu 版本升级 实践 24.04 -> 24.10, 安装 .NET

原因&#xff1a; 想安装 .NET 9 去编译 GitHut 项目&#xff0c;这回用不熟悉的 Ubuntu来做&#xff0c;不知道怎么拐去给 Ubuntu 升级&#xff0c;看到现在版本是 24.10 但不是 LTS 版本&#xff0c;记录下升级过程。 一、实践过程&#xff1a; 1. 查看当前版本 命令1: l…

某咨询大数据解决方案介绍(32页PPT)

本文档介绍了一个大数据平台解决方案&#xff0c;旨在解决企业当前面临的数据问题&#xff0c;包括数据定义缺失、重复采集和存储、数据不完整以及缺乏可靠决策依据等。通过引入大数据技术&#xff0c;该方案强调从被动的IT支撑向主动的数据核心服务转型&#xff0c;以实现科学…

matlab simulink 汽车四分之一模型主动被动悬架-LQR

1、内容简介 略 matlab simulink 可以交流、咨询、答疑 124- 2、内容说明 略汽车悬架系统由弹性元件、导向元件和减振器组成,是车身与车轴之间连接的所有组合体零件的总称,也是车架(或承载式车身)与车桥(或车轮)之间一切力传递装置的总称,其主要功能是使车轮与地面有很好的…

从零开始:OpenCV 图像处理快速入门教程

文章大纲 第1章 OpenCV 概述 1.1 OpenCV的模块与功能  1.2 OpenCV的发展 1.3 OpenCV的应用 第2章 基本数据类型 2.1 cv::Vec类 2.2 cv&#xff1a;&#xff1a;Point类 2.3 cv&#xff1a;&#xff1a;Rng类 2.4 cv&#xff1a;&#xff1a;Size类 2.5 cv&#xff1a;&…

强化学习笔记6——异同策略、AC、等其他模型总结

异步两种方法&#xff1a;1&#xff1a;经验回放 2&#xff1a;数据动作非同时产生 举例QLearning为什么是异策略&#xff1f; 生成动作时e的概率从Q表选&#xff0c;1-e概况随机。 更新策略时&#xff0c;贪心策略选择Q_max作为动作。 策略优化两种主要方法&#xff1a;基于梯…

Linux提权--passwd提权

passwd​ 命令用于更改用户密码。在 Linux 系统中&#xff0c;普通用户可以通过 passwd​ 更改自己的密码&#xff0c;但如果攻击者能够以某种方式执行 passwd​ 命令更改 root 用户的密码&#xff0c;他们就能获取 root 权限。 1.常见的 passwd 提权方法 SUID 设置&#xff1…

一、本地部署安装 DeepSeek 并训练本地知识库,并调用对话框进行问答

本地部署安装 DeepSeek 1、硬件环境 操作系统&#xff1a;Windows10 内存&#xff1a;16G 显卡&#xff1a;NIVIDIA GeForce RTX 2060 6G 2、安装步骤 &#xff08;1&#xff09;安装 Ollama 访问Ollama 官网&#xff0c;点击 “Download for Windows” 下载安装程序。下载…

海思的一站式集成环境Hispark Studio更新了

HiSpark Studio是海思提供的面向智能设备开发者提供一站式集成开发环境&#xff0c;支持代码编辑、编译、烧录和调试等功能。我以前在评测星闪芯片的时候用过&#xff0c;当时写了篇博客&#xff1a;【星闪开发连载】WS63E开发板Windows环境的构建_hispark studio-CSDN博客。那…

unity学习29:摄像机camera相关skybox 和 Render Texture测试效果

目录 1 摄像机 1.1 每个Scene里都自带一个摄像机 camera 1.2 可以创建多个camera 1.3 下面先看backgroundtype: 2 backgroundtype: 天空盒 skybox 2.1 清除标志,清除&#xff1a;天空盒 自选天空盒 2.2 window /Asset Store 2.3 导入skybox 3 backgroundtype: 纯色…

【Elasticsearch】Geo-distance聚合

geo_distance聚合的形状是圆形。它基于一个中心点&#xff08;origin&#xff09;和一系列距离范围来计算每个文档与中心点的距离&#xff0c;并将文档分配到相应的距离范围内。这种聚合方式本质上是以中心点为圆心&#xff0c;以指定的距离范围为半径的圆形区域来划分数据。 为…

音频进阶学习十二——Z变换

文章目录 前言一、Z变换1.Z变换的作用2.Z变换公式3.Z的状态表示1&#xff09; r 1 r1 r12&#xff09; 0 < r < 1 0<r<1 0<r<13&#xff09; r > 1 r>1 r>1 4.关于Z的解释 二、收敛域1.收敛域的定义2.收敛域的表示方式3.ROC的分析1&#xff09;当 …

easyxor

easyxor 一、查壳 无壳&#xff0c;64位 二、IDA分析 1.main 2.查看key与r(shifee提取) 三、脚本 r [0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, …

通过多层混合MTL结构提升股票市场预测的准确性,R²最高为0.98

“Boosting the Accuracy of Stock Market Prediction via Multi-Layer Hybrid MTL Structure” 论文地址&#xff1a;https://arxiv.org/pdf/2501.09760 ​​​​​​​ 摘要 本研究引入了一种创新的多层次混合多任务学习架构&#xff0c;致力于提升股市预测的效能。此架构融…

日本游戏机市场5年来首次陷入萎缩;特斯拉招人推进人形机器人量产;任天堂专利显示Switch2手柄可用作鼠标...| 游戏智眼日报

美团成立“算法顾问委员会” 美团宣布&#xff0c;近日&#xff0c;由外部专家学者组成的算法顾问委员会成立&#xff0c;为美团改进算法提供常态化咨询和指导。每个季度美团将举办算法恳谈会&#xff0c;持续邀请骑手、商家、用户、专家学者和媒体代表等共同参加。美团表示&a…

114-机器学习分类算法

1、内容简介 略 matlab simulink 114-机器学习分类算法可以交流、咨询、答疑 2、内容说明 略 Elong_6.24。ROCAUC confusion newdata Unbalanced_LR.car 3、仿真分析 略 4、参考论文 略

【论文阅读】On the Security of “VOSA“

On the Security of Verifiable and Oblivious Secure Aggregation for Privacy-Preserving Federated Learning -- 关于隐私保护联邦中可验证与遗忘的安全聚合的安全性 论文来源摘要Introduction回顾 VOSA 方案对VOSA不可伪造性的攻击对于类型 I 的攻击对于类型 II 的攻击 论文…

储能系统-系统架构

已更新系列文章包括104、61850、modbus 、单片机等&#xff0c;欢迎关注 IEC61850实现方案和测试-1-CSDN博客 快速了解104协议-CSDN博客 104调试工具2_104协议调试工具-CSDN博客 1 电池储能系统&#xff08;BESS&#xff09; 架构 电池储能系统主要包括、电池、pcs、本地控制…

ip属地是手机号还是手机位置?一文理清

在数字化和网络化的今天&#xff0c;IP属地这一概念逐渐成为了人们关注的焦点。特别是在社交媒体和在线平台上&#xff0c;IP属地的显示往往让人联想到用户的地理位置。然而&#xff0c;关于IP属地到底与手机号还是手机位置有关&#xff0c;却存在着不少误解和混淆。本文将深入…

迅为RK3568开发板篇OpenHarmony实操HDF驱动控制LED-编写应用APP

在应用代码中我们实现如下功能&#xff1a; 当应用程序启动后会获取命令行参数。如果命令行没有参数&#xff0c;LED 灯将循环闪烁&#xff1b;如果命令行带有参数&#xff0c;则根据传输的参数控制 LED 灯的开启或关闭。通过 HdfIoServiceBind 绑定 LED灯的 HDF 服务&#xff…