享元模式介绍

目录

一、享元模式介绍

1.1 享元模式定义

1.2 享元模式原理

1.2.1 享元模式类图

1.2.2 模式角色说明

1.2.3 示例代码

二、享元模式的应用

2.1 需求说明

2.2 需求实现

2.2.1 类图

2.2.2 具体实现

2.2.2.1 抽象享元类

2.2.2.2 共享享元类-白色棋子

2.2.2.3 共享享元类-黑色棋子

2.2.2.4 享元工厂类

2.2.2.5 测试类

三、享元模式总结

3.1 享元模式的优点

3.2 享元模式的缺点

3.3 享元模式的适用场景


一、享元模式介绍

1.1 享元模式定义

享元模式 (flyweight pattern) 的原始定义是:摒弃了在每个对象中保存所有数据的方式,通过共享多个对象所共有的相同状态,从而让我们能在有限的内存容量中载入更多对象。从这个定义中你可以发现,享元模式要解决的核心问题就是节约内存空间,使用的办法是找出相似对象之间的共有特征,然后复用这些特征。所谓“享元”,顾名思义就是被共享的单元。
比如: 一个文本字符串中存在很多重复的字符,如果每一个字符都用一个单独的对象来表示,将会占用较多的内存空间,我们可以使用享元模式解决这一类问题。

享元模式通过共享技术实现相同或者相似对象的重用,在逻辑上每一个出现的字符都有一个对象与之对应,然而在物理上他们却是共享同一个享元对象。

1.2 享元模式原理

1.2.1 享元模式类图

1.2.2 模式角色说明

享元模式的主要有以下角色:

  • 抽象享元角色(Flyweight):

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

  1. 内部状态,即不会随着环境的改变而改变的可共享部分。
  2. 外部状态,指随环境改变而改变的不可以共享的部分。享元模式的实现要领就是区分应用中的这两种状态,并将外部状态外部化。
  • 可共享的具体享元(Concrete Flyweight)角色 :

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

  • 非共享的具体享元(Unshared Flyweight)角色 :

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

  • 享元工厂(Flyweight Factory)角色 :

负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。

1.2.3 示例代码

package main.java.cn.test.flyweight.V1;

/**
 * @author ningzhaosheng
 * @date 2024/1/14 17:20:06
 * @description 抽象享元类
 */
public abstract class Flyweight {
    public abstract void operation(String extrinsicState);
}

package main.java.cn.test.flyweight.V1;

/**
 * @author ningzhaosheng
 * @date 2024/1/14 17:20:46
 * @description 可共享-具体享元类
 */
public class ConcreteFlyweight extends Flyweight {
    //内部状态 intrinsicState作为成员变量,同一个享元对象的内部状态是一致的
    private String intrinsicState;

    public ConcreteFlyweight(String intrinsicState) {
        this.intrinsicState = intrinsicState;
    }

    /**
     * 外部状态在使用时由外部设置,不保存在享元对象中,即使是同一个对象
     *
     * @param extrinsicState 外部状态,每次调用可以传入不同的外部状态
     */
    @Override
    public void operation(String extrinsicState) {
        //实现业务方法
        System.out.println("=== 享元对象内部状态" + intrinsicState + ",外部状态:" + extrinsicState);
    }

}

package main.java.cn.test.flyweight.V1;

/**
 * @author ningzhaosheng
 * @date 2024/1/14 17:23:16
 * @description 非共享具体享元类
 */
public class UnsharedConcreteFlyweight extends Flyweight {
    private String intrinsicState;

    public UnsharedConcreteFlyweight(String intrinsicState) {
        this.intrinsicState = intrinsicState;
    }

    @Override
    public void operation(String extrinsicState) {
        System.out.println("=== 使用不共享对象,内部状态: " + intrinsicState + ",外部状态: " + extrinsicState);
    }
}

package main.java.cn.test.flyweight.V1;

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

/**
 * @author ningzhaosheng
 * @date 2024/1/14 17:25:01
 * @description 享元工厂类
 * 作用: 作为存储享元对象的享元池.用户获取享元对象时先从享元池获
 * 取,有则返回,没有创建新的享元对象返回给用户,并在享元池中保存新增的对象.
 */
public class FlyweightFactory {
    //定义一个HashMap用于存储享元对象,实现享元池
    private Map<String, Flyweight> pool = new HashMap();

    public FlyweightFactory() {
        //添加对应的内部状态
        pool.put("A", new ConcreteFlyweight("A"));
        pool.put("B", new ConcreteFlyweight("B"));
        pool.put("C", new ConcreteFlyweight("C"));
    }

    //根据内部状态来进行查找
    public Flyweight getFlyweight(String key) {
        //对象存在,从享元池直接返回
        if (pool.containsKey(key)) {
            System.out.println("===享元池中存在,直接复用,key:" + key);
            return pool.get(key);
        } else {
            //如果对象不存在,先创建一个新的对象添加到享元池中,然后返回
            System.out.println("===享元池中不存在,创建并复用, key:" + key);
            Flyweight fw = new ConcreteFlyweight(key);
            pool.put(key, fw);
            return fw;
        }
    }
}

二、享元模式的应用

2.1 需求说明

五子棋中有大量的黑子和白子,它们的形状大小都是一样的,只是出现的位置不同,所以一个棋子作为一个独立的对象存储在内存中,会导致大量的内存的浪费,我们使用享元模式来进行优化。

2.2 需求实现

2.2.1 类图

2.2.2 具体实现

2.2.2.1 抽象享元类
package main.java.cn.test.flyweight.V2;

/**
 * @author ningzhaosheng
 * @date 2024/1/14 17:34:28
 * @description 抽象享元类: 五子棋类
 */
public abstract class GobangFlyweight {
    public abstract String getColor();

    public void display() {
        System.out.println("棋子颜色: " + this.getColor());
    }

}

2.2.2.2 共享享元类-白色棋子
package main.java.cn.test.flyweight.V2;

/**
 * @author ningzhaosheng
 * @date 2024/1/14 17:35:10
 * @description 共享享元类-白色棋子
 */
public class WhiteGobang extends GobangFlyweight{
    @Override
    public String getColor() {
        return "白色";
    }
}
2.2.2.3 共享享元类-黑色棋子
package main.java.cn.test.flyweight.V2;

/**
 * @author ningzhaosheng
 * @date 2024/1/14 17:35:45
 * @description 共享享元类-黑色棋子
 */
public class BlackGobang extends GobangFlyweight {
    @Override
    public String getColor() {
        return "黑色";
    }
}

2.2.2.4 享元工厂类
package main.java.cn.test.flyweight.V2;

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

/**
 * @author ningzhaosheng
 * @date 2024/1/14 17:36:19
 * @description 享元工厂类-生产围棋棋子,使用单例模式进行设计
 */
public class GobangFactory {
    private static GobangFactory factory = new GobangFactory();
    private static Map<String, GobangFlyweight> pool;

    //设置共享对象的内部状态,在享元对象中传递
    private GobangFactory() {
        pool = new HashMap<String, GobangFlyweight>();
        GobangFlyweight black = new BlackGobang(); //黑子
        GobangFlyweight white = new WhiteGobang(); //白子
        pool.put("b", black);
        pool.put("w", white);
    }

    //返回享元工厂类唯一实例
    public static final GobangFactory getInstance() {
        return SingletonHolder.INSTANCE;
    }

    //静态内部类-单例
    private static class SingletonHolder {
        private static final GobangFactory INSTANCE = new GobangFactory();
    }

    //通过key获取集合中的享元对象
    public GobangFlyweight getGobang(String key) {
        return pool.get(key);
    }

}

2.2.2.5 测试类
package main.java.cn.test.flyweight.V2;

/**
 * @author ningzhaosheng
 * @date 2024/1/14 17:37:45
 * @description 测试类
 */
public class Test {
    public static void main(String[] args) {
        //获取享元工厂对象
        GobangFactory instance = GobangFactory.getInstance();
        //获取3颗黑子
        GobangFlyweight b1 = instance.getGobang("b");
        GobangFlyweight b2 = instance.getGobang("b");
        GobangFlyweight b3 = instance.getGobang("b");
        System.out.println("判断两颗黑子是否相同: " + (b1 == b2));
        //获取2颗白子
        GobangFlyweight w1 = instance.getGobang("w");
        GobangFlyweight w2 = instance.getGobang("w");
        System.out.println("判断两颗白子是否相同: " + (w1 == w2));
        //显示棋子
        b1.display();
        b2.display();
        b3.display();
        w1.display();
        w2.display();
    }
}

三、享元模式总结

3.1 享元模式的优点

  • 极大减少内存中相似或相同对象数量,节约系统资源,提供系统性能

比如,当大量商家的商品图片、固定文字(如商品介绍、商品属性)在不同的网页进行展示时,通常不需要重复创建对象,而是可以使用同一个对象,以避免重复存储而浪费内存空间。由于通过享元模式构建的对象是共享的,所以当程序在运行时不仅不用重复创建,还能减少程序与操作系统的 IO 交互次数,大大提升了读写性能。

  • 享元模式中的外部状态相对独立,且不影响内部状态

3.2 享元模式的缺点

  • 为了使对象可以共享,需要将享元对象的部分状态外部化,分离内部状态和外部状态,使程序逻辑复杂

3.3 享元模式的适用场景

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

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

  • 在 Java 中,享元模式一个常用的场景就是,使用数据类的包装类对象的valueOf() 方法。

比如,使用 Integer.valueOf() 方法时,实际的代码实现中有一个叫 IntegerCache 的静态类,它就是一直缓存了 -127 到 128 范围内的数值,如下代码所示,你可以在 Java JDK 中的 Integer 类的源码中找到这段代码。

package main.java.cn.test.flyweight.V2;

/**
 * @author ningzhaosheng
 * @date 2024/1/14 17:47:05
 * @description
 */
public class Test1 {
    public static void main(String[] args) {
        Integer i1 = 127;
        Integer i2 = 127;
        System.out.println("i1和i2对象是否是同一个对象?" +
                (i1 == i2));
        Integer i3 = 128;
        Integer i4 = 128;
        System.out.println("i3和i4对象是否是同一个对象?" +
                (i3 == i4));
    }

    //传入的值在-128 - 127 之间,直接从缓存中返回
    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
}

可以看到 Integer 默认先创建并缓存 -128 ~ 127 之间数的 Integer 对象,当调用 valueOf 时如果参数在 -128 ~ 127 之间则计算下标并从缓存中返回,否则创建一个新的 Integer 对象。其实享元模式本质上就是找到对象的不可变特征,并缓存起来,当类似对象使用时从缓存中读取,以达到节省内存空间的目的。

好了,本次分享就到这里,欢迎大家继续阅读《设计模式》专栏其他设计模式内容,如果有帮助到大家,欢迎大家点赞+关注+收藏,有疑问也欢迎大家评论留言!

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

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

相关文章

JAVA SECS发送Report C#处理SECS Report SECS发送事件资料大全 S6F11 建立通讯S1F13

发送S6F11非常简单&#xff0c;只需5~6行代码&#xff0c;最核心是代码清晰易懂。 任何人都可以一看就能上手&#xff0c;如果说用代码可读性作为不可替代性的壁垒就无话可说了。 private void buttonS6F11_Click(object sender, EventArgs e) {int nTransaction 0;// 数据部…

Rust之旅 - Rust概念、Windows安装、环境配置

&#x1f339;作者主页&#xff1a;青花锁 &#x1f339;简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java微服务架构公号作者&#x1f604; &#x1f339;简历模板、学习资料、面试题库、技术互助 &#x1f339;文末获取联系方式 &#x1f4dd; 系列专栏目录 [Java项目…

【SpringBoot】SpringBoot 项目初始化方法

github 搜索 springboot 模板 github 搜索 springboot 模板&#xff0c;拉取现成代码。 SpringBoot 官方的模板生成器 SpringBoot 官方的模板生成器&#xff08;https://start.spring.io/&#xff09; 在 IDEA 开发工具中生成 这里我修改成阿里的镜像主要是要使用 Java8。 …

制作一个Python聊天机器人

我们学习一下如何使用 ChatterBot 库在 Python 中创建聊天机器人&#xff0c;该库实现了各种机器学习算法来生成响应对话&#xff0c;还是挺不错的 什么是聊天机器人 聊天机器人也称为聊天机器人、机器人、人工代理等&#xff0c;基本上是由人工智能驱动的软件程序&#xff0…

最多购买宝石数目 - 华为OD统一考试

OD统一考试&#xff08;C卷&#xff09; 分值&#xff1a; 100分 题解&#xff1a; Java / Python / C 题目描述 橱窗里有一排宝石&#xff0c;不同的宝石对应不同的价格&#xff0c;宝石的价格标记为 gems[i],0<i<n, n gems.length 宝石可同时出售0个或多个&#xff…

test2测试

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起探讨和分享Linux C/C/Python/Shell编程、机器人技术、机器学习、机器视觉、嵌入式AI相关领域的知识和技术。 磁盘满的本质分析 专栏&#xff1a;《Linux从小白到大神》 | 系统学习Linux开发、VIM/GCC/GDB/Make工具…

如何发挥 Sketch在UI和UX设计中的作用

Sketch是一款专业的矢量图形设计软件&#xff0c;主要应用于UI设计、移动应用设计、Web设计等领域。假如你是一个交互设计师或UI设计师&#xff0c;那么你一定知道Sketch这一强大的矢量设计软件&#xff1b;如果你使用了Photoshop&#xff0c;那么在你接触到Sketch之后&#xf…

物联网协议Coap之C#基于Mozi的CoapServer实现解析

目录 前言 一、C#的Coap Server实现 1、CoapServer相关类 2、主要类解析 3、资源控制器定义 4、ResourceManager管理器 二、CoapServer生命周期 1、Server创建代码 2、服务端创建 3、绑定endpoint 4、准备接收请求 总结 前言 在之前的关于物联网协议的介绍中&#…

汽车连接器接线端子和多芯线束连接界面

冷压接的开式压接和闭式压接以及热压接的超声波焊接对汽车连接器接线端子和多芯线束连接界面 连接器接线端子和多芯线束的连接是电子线束行业&#xff0c;特别是汽车行业常用的导线连接方式。汽车整车线束又由许多分支线束组成&#xff0c;而分支线束必须通过连接器实现连接&am…

HBase学习六:LSM树算法

1、简介 HBase是基于LSM树架构实现的,天生适合写多读少的应用场景。 LSM树本质上和B+树一样,是一种磁盘数据的索引结构。但和B+树不同的是,LSM树的索引对写入请求更友好。因为无论是何种写入请求,LSM树都会将写入操作处理为一次顺序写,而HDFS擅长的正是顺序写(且HDFS不…

IMDB电影评论的情感分析——paddle

项目地址&#xff1a;IMDB电影评论的情感分析 - 飞桨AI Studio星河社区 (baidu.com) 1. 实验介绍 1.1 实验目的 理解并掌握循环神经网络的基础知识点&#xff0c;包括模型的时序结构、模型的前向传播、反向传播等掌握长短时记忆网络LSTM和门控循环单元网络GRU的设计原理熟悉如…

移动云助力智慧交通数智化升级

智慧交通是在整个交通运输领域充分利用物联网、空间感知、云计算、移动互联网等新一代信息技术&#xff0c;综合运用交通科学、系统方法、人工智能、知识挖掘等理论与工具&#xff0c;以全面感知、深度融合、主动服务、科学决策为目标&#xff0c;推动交通运输更安全、更高效、…

O(1)插入、删除和随机元素[中等]

优质博文&#xff1a;IT-BLOG-CN 一、题目 实现RandomizedSet类&#xff1a; 【1】RandomizedSet()初始化RandomizedSet对象。 【2】bool insert(int val)当元素val不存在时&#xff0c;向集合中插入该项&#xff0c;并返回true&#xff1b;否则&#xff0c;返回false。 【3】…

java基本算法

1.链表 链表用来存储数据&#xff0c;由一系列的结点组成。这些结点的物理地址不一定是连续的&#xff0c;即可能连续&#xff0c;也可能不连续&#xff0c;但链表里的结点是有序的。一个结点由数据的值和下一个数据的地址组成。一个链表内的数据类型可以是多种多样的。数组也是…

Debian系统写Mysql时中文出现乱码无法定入的问题解决方案

原因是操作系统可能精简安装&#xff0c;没有GBK字符集&#xff0c;只有UTF8在转换或使用的时候有问题。 使用locale -a查看系统支持的字符集。正常的比较全的字符集的操作系统如下&#xff1a; 有问题的操作系统字符集如下&#xff1a; 解决方案&#xff1a; 步骤1&#…

protobuf学习日记 | 认识protobuf中的类型

目录 前言 一、标量数据类型 二、protobuf中的 “数组” 三、特殊类型 1、枚举类型 &#xff08;1&#xff09;类型讲解 &#xff08;2&#xff09;升级通讯录 2、Any类型 &#xff08;1&#xff09;类型讲解 &#xff08;2&#xff09;升级通讯录 3、oneof类型 …

【动态规划】【二分查找】【C++算法】730. 统计不同回文子序列

作者推荐 【动态规划】【数学】【C算法】18赛车 涉及知识点 动态规划 二分查找 LeetCode730. 统计不同回文子序列 给你一个字符串 s &#xff0c;返回 s 中不同的非空回文子序列个数 。由于答案可能很大&#xff0c;请返回对 109 7 取余 的结果。 字符串的子序列可以经由…

ubuntu源码安装MySQL

mysql下载路径 创建新数组 mysql sudo groupadd mysql# 创建用户 mysql ,指定属组为 mysql&#xff0c;禁止其登录 # --no-create-home选项&#xff0c;创建用户时不会自动创建主目录 sudo adduser --system --no-create-home --ingroup mysql --shell /sbin/nologin mysql创…

第二证券:大逆转!A股强势反弹,多家机构看好后市

周四&#xff0c;A股强势反弹&#xff0c;沪指2800点合浦还珠&#xff0c;两市成交量达8767亿元&#xff0c;较周三大幅增加2000多亿元。沪深300ETF大幅放量&#xff0c;华泰柏瑞沪深300ETF、嘉实沪深300ETF、易方达沪深300ETF和华夏沪深300ETF等4只沪深300ETF算计成交额超311亿…

常用中间件漏洞

IIS6 IIS7 安装 控制面板-----打开关闭windows功能 添加角色-----添加IIS 启动之后访问localhost 复现 服务器换成IIS7 访问报错 大概就是缺少CGI模块 问题解决 添加php-cgi的路径 添加脚本映射 修改php.ini文件 将 cgi.fix_pathinfo1 然后设置一个图片 访问 在后缀加上/.…