设计模式之享元模式【结构型模式】

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档> 学习的最大理由是想摆脱平庸,早一天就多一份人生的精彩;迟一天就多一天平庸的困扰。各位小伙伴,如果您:
想系统/深入学习某技术知识点…
一个人摸索学习很难坚持,想组团高效学习…
想写博客但无从下手,急需写作干货注入能量…
热爱写作,愿意让自己成为更好的人…

文章目录

  • 前言
  • 一、概述
  • 二、结构
  • 三、案例实现
  • 四、优缺点和使用场景
  • 五、JDK源码解析
  • 总结


前言

一、概述
二、结构
三、案例实现
四、优缺点和使用场景
五、JDK源码解析


一、概述

定义:

​ 运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似对象的开销,从而提高系统资源的利用率。

二、结构

享元(Flyweight )模式中存在以下两种状态:

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

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

  • 抽象享元角色(Flyweight):通常是一个接口或抽象类,在抽象享元类中声明了具体享元类公共的方法,这些方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法来设置外部数据(外部状态)。
  • 具体享元(Concrete Flyweight)角色 :它实现了抽象享元类,称为享元对象;在具体享元类中为内部状态提供了存储空间。通常我们可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象。
  • 非享元(Unsharable Flyweight)角色 :并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可设计为非共享具体享元类;当需要一个非共享具体享元类的对象时可以直接通过实例化创建。
  • 享元工厂(Flyweight Factory)角色 :负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。

三、案例实现

【例】俄罗斯方块

下面的图片是众所周知的俄罗斯方块中的一个个方块,如果在俄罗斯方块这个游戏中,每个不同的方块都是一个实例对象,这些对象就要占用很多的内存空间,下面利用享元模式进行实现。
在这里插入图片描述
先来看类图:
在这里插入图片描述
代码如下:

俄罗斯方块有不同的形状,我们可以对这些形状向上抽取出AbstractBox,用来定义共性的属性和行为。

public abstract class AbstractBox {public abstract String getShape();public void display(String color) {System.out.println("方块形状:" + this.getShape() + " 颜色:" + color);}
}

接下来就是定义不同的形状了,IBox类、LBox类、OBox类等。

public class IBox extends AbstractBox {@Overridepublic String getShape() {return "I";}
}public class LBox extends AbstractBox {@Overridepublic String getShape() {return "L";}
}public class OBox extends AbstractBox {@Overridepublic String getShape() {return "O";}
}

提供了一个工厂类(BoxFactory),用来管理享元对象(也就是AbstractBox子类对象),该工厂类对象只需要一个,所以可以使用单例模式。并给工厂类提供一个获取形状的方法。

public class BoxFactory {private static HashMap<String, AbstractBox> map;private BoxFactory() {map = new HashMap<String, AbstractBox>();AbstractBox iBox = new IBox();AbstractBox lBox = new LBox();AbstractBox oBox = new OBox();map.put("I", iBox);map.put("L", lBox);map.put("O", oBox);}public static final BoxFactory getInstance() {return SingletonHolder.INSTANCE;}private static class SingletonHolder {private static final BoxFactory INSTANCE = new BoxFactory();}public AbstractBox getBox(String key) {return map.get(key);}
}

Client 类

public class Client {public static void main(String[] args) {//获取I图形对象AbstractBox box1 = BoxFactory.getInstance().getShape("I");box1.display("灰色");//获取L图形对象AbstractBox box2 = BoxFactory.getInstance().getShape("L");box2.display("绿色");//获取O图形对象AbstractBox box3 = BoxFactory.getInstance().getShape("O");box3.display("蓝色");//获取I图形对象AbstractBox box4 = BoxFactory.getInstance().getShape("O");box4.display("灰色");System.out.println("两次获取到的O对象是否是同一个对象"+(box4==box3));}
}

在这里插入图片描述

在这里插入图片描述

四、优缺点和使用场景

1,优点

  • 极大减少内存中相似或相同对象数量,节约系统资源,提供系统性能
  • 享元模式中的外部状态相对独立,且不影响内部状态

2,缺点:

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

3,使用场景:

  • 一个系统有大量相同或者相似的对象,造成内存的大量耗费。
  • 对象的大部分状态都可以外部化,可以将这些外部状态传入对象中。
  • 在使用享元模式时需要维护一个存储享元对象的享元池,而这需要耗费一定的系统资源,因此,应当在需要多次重复使用享元对象时才值得使用享元模式。

五、JDK源码解析

Integer类使用了享元模式。我们先看下面的例子:

public class Demo {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));}
}

运行上面代码,结果如下:

在这里插入图片描述
为什么第一个输出语句输出的是true,第二个输出语句输出的是false?通过反编译软件进行反编译,代码如下:

public class Demo {public static void main(String[] args) {Integer i1 = Integer.valueOf((int)127);Integer i2 Integer.valueOf((int)127);System.out.println((String)new StringBuilder().append((String)"i1\u548ci2\u5bf9\u8c61\u662f\u5426\u662f\u540c\u4e00\u4e2a\u5bf9\u8c61\uff1f").append((boolean)(i1 == i2)).toString());Integer i3 = Integer.valueOf((int)128);Integer i4 = Integer.valueOf((int)128);System.out.println((String)new StringBuilder().append((String)"i3\u548ci4\u5bf9\u8c61\u662f\u5426\u662f\u540c\u4e00\u4e2a\u5bf9\u8c61\uff1f").append((boolean)(i3 == i4)).toString());}
}

上面代码可以看到,直接给Integer类型的变量赋值基本数据类型数据的操作底层使用的是 valueOf() ,所以只需要看该方法即可

public final class Integer extends Number implements Comparable<Integer> {public static Integer valueOf(int i) {if (i >= IntegerCache.low && i <= IntegerCache.high)return IntegerCache.cache[i + (-IntegerCache.low)];return new Integer(i);}private static class IntegerCache {static final int low = -128;static final int high;static final Integer cache[];static {int h = 127;String integerCacheHighPropValue =sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");if (integerCacheHighPropValue != null) {try {int i = parseInt(integerCacheHighPropValue);i = Math.max(i, 127);// Maximum array size is Integer.MAX_VALUEh = Math.min(i, Integer.MAX_VALUE - (-low) -1);} catch( NumberFormatException nfe) {}}high = h;cache = new Integer[(high - low) + 1];int j = low;for(int k = 0; k < cache.length; k++)cache[k] = new Integer(j++);// range [-128, 127] must be interned (JLS7 5.1.7)assert IntegerCache.high >= 127;}private IntegerCache() {}}
}

可以看到 Integer 默认先创建并缓存 -128 ~ 127 之间数的 Integer 对象,当调用 valueOf 时如果参数在 -128 ~ 127 之间则计算下标并从缓存中返回,否则创建一个新的 Integer 对象。


总结

以上就是设计模式之享元模式【结构型模式】的相关知识点,希望对你有所帮助。
积跬步以至千里,积怠惰以至深渊。时代在这跟着你一起努力哦!

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

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

相关文章

FridaHook(三)——AllSafe App wp

By ruanruan&#xff0c;2022/04/21 文章目录 1、不安全的日志记录2、硬编码3、pin绕过&#xff08;1&#xff09;反编译查看方法判断逻辑&#xff08;2&#xff09;hook方法A、Hook areEqual(Object,Object)B、Hook checkPin(a) &#xff08;3&#xff09;页面效果&#xff08…

芯课堂 | 一种带WIFI的智能多电机控制系统

现有技术中&#xff0c;每台智能家电基本均需配置一台电机及一个WiFi模组&#xff0c;每台智能家电的电机均通过对应的WiFi模组连接家庭无线路由器进行组网&#xff0c;从而实现网络连接。 但是&#xff0c;这种方式存在技术瓶颈。例如&#xff0c;当一个家庭中智能家电的数量…

80V 72V 60V 48V 降12V 5V 3.3V 功耗低降压恒压芯片H6603

输入电压80V、72V、60V、48V&#xff1a;这些是电源系统中的不同电压水平&#xff0c;通常用于驱动各种设备。例如&#xff0c;电动汽车、电动自行车或工业设备中的电池系统可能以这些电压级别工作。 降12V&#xff1a;这可能是指一种电源模块&#xff0c;其功能是将输入电压&…

Linux 文件的压缩和解压

zip压缩&#xff1a;指定目录的文件压缩到指定目录下。 可采用 zip 压缩命令&#xff1a; 例如&#xff1a;要将 /path/to/source/directory/* 目录下的所有文件压缩到 /path/to/destination 目录下的 archive.zip 文件中 zip -j /path/to/destination/archive.zip /path/to/s…

MYSQL学习——聚合函数

目录 1. 聚合函数介绍 1) AVG和SUM函数 2) MIN和MAX函数 3) COUNT函数 2. GROUP BY 1) 基本使用 2) 使用WITH ROLLUP 3. HAVING 1) 基本使用 2) WHERE和HAVING的对比 4. SELECT的执行过程 1) 查询的结构 2) SQL的执行原理 1. 聚合函数介绍 什么是聚合函数 聚合函…

HDFS WebHDFS 读写文件分析及HTTP Chunk Transfer Coding相关问题探究

文章目录 前言需要回答的问题DataNode端基于Netty的WebHDFS Service的实现 基于重定向的文件写入流程写入一个大文件时WebHDFS和Hadoop Native的块分布差异 基于重定向的数据读取流程尝试读取一个小文件尝试读取一个大文件 读写过程中的Chunk Transfer-Encoding支持写文件使用C…

xcode安装及运行源码

抖音教学视频 目录 1、xcode 介绍 2、xcode 下载 3、xocde 运行ios源码 4、快捷键 1、xcode 介绍 Xcode 是运行在操作系统Mac OS X上的集成开发工具&#xff08;IDE&#xff09;&#xff0c;由Apple Inc开发。Xcode是开发 macOS 和 iOS 应用程序的最快捷的方式。Xcode 具有…

体感游戏开发体感互动游戏

体感健身游戏是一种利用特定技术来跟踪和响应玩家身体动作的互动式电子游戏。这种游戏类型的目的是通过有趣、动态的方式鼓励用户进行身体活动和健康锻炼。下面是有关体感健身游戏的一些重要信息&#xff1a; 体感游戏技术背景 体感技术&#xff1a;这些游戏通常使用运动传感…

C //练习 4-5 给计算器程序增加访问sin、exp与pow等库函数的操作。有关这些库函数的详细信息,参见附录B.4节中的头文件<math.h>。

C程序设计语言 &#xff08;第二版&#xff09; 练习 4-5 练习 4-5 给计算器程序增加访问sin、exp与pow等库函数的操作。有关这些库函数的详细信息&#xff0c;参见附录B.4节中的头文件<math.h>。 注意&#xff1a;代码在win32控制台运行&#xff0c;在不同的IDE环境下…

2024.1.11 关于 Jedis 库操作 Redis 基本演示

目录 引言 通用命令 SET & GET EXISTS & DEL KEYS EXPIRE & TTL TYPE String 类型命令 MGET & MSET GETRANGE & SETRANGE APPEND INCR & DECR List 类型命令 LPUSH & LRANG LPOP & LPOP BLPOP & BRPOP LLEN Set 类型命…

SwiftUI 为任意视图加上徽章(Badge)而想到的(下)

概览 在 SwiftUI 为任意视图加上徽章(Badge)而想到的(上) 这篇文章中,我们讨论了如何使用 Preference 技术打造 SwiftUI 中任意视图上徽章的实现。 虽然,我们完成了一系列挑战最后基本得偿所愿,不过在上篇的实现中仍有些许不尽如人意之处。 在本篇博文中,您将学到如下…

【web安全】弱口令,以及不同领域的弱口令爆破

弱口令的概念 简单的理解就是简单的&#xff0c;容易让别人猜出来的密码。 弱口令的分类 弱口令分了三类 简单常用的易记密码 这种密码是人均常用的密码&#xff0c;比如活到了这么大&#xff0c;我见过很多很多的wifl密码是88888888或者12345678这种的密码。很多情况下数…

Jmeter 性能压测 —— TPS与QPS

1、TPS和QPS的区别 TPS&#xff1a;意思是每秒事务数&#xff0c;具体事务的定义都是人为的&#xff0c;可以一个接口、多个接口、一个业务流程等等。 一个事务是指事务内第一个请求发送到接收到最后一个请求的响应的过程&#xff0c;以此来计算使用的时间和完成的事务个数。…

WPF 布局

了解 WPF中所有布局如下&#xff0c;我们一一尝试实现&#xff0c;本文档主要以图形化的形式展示每个布局的功能。 布局&#xff1a; Border、 BulletDecorator、 Canvas、 DockPanel、 Expander、 Grid、 GridView、 GridSplitter、 GroupBox、 Panel、 ResizeGrip、 Separat…

蓝屏代码0x000007E解决办法

概述 出现该问题&#xff1a; 1、硬件冲突造成的蓝屏 驱动冲突&#xff1a;与其他设备或应用程序的驱动冲突可能会引起系统崩溃。 2、内存虚拟不足造成的蓝屏 错误配置&#xff1a;不正确的配置或设置可能会导致蓝屏错误。 3、超频后也可能出现蓝屏 CUP超频或者显卡超频后出现蓝…

NAS使用的一些常见命令 ssh sftp 上传 下载 ALL in one

目录 登陆上传/下载内网穿透 登陆 ssh 登陆 ssh usernameserverIP -p portNumsftp 登陆 sftp -P portNum usernameserverIP上传/下载 如ls等&#xff0c;远程服务器操作 如lls等&#xff0c;本机操作&#xff0c;前缀为l 文件 put **** 将本机上文件上传到远程服务器上当…

分析一个项目(微信小程序篇)三

目录 接下来分析接口方面&#xff1a; home接口&#xff1a; categories接口&#xff1a; details接口&#xff1a; login接口&#xff1a; 分析一个项目讲究的是如何进行对项目的解析分解&#xff0c;进一步了解项目的整体结构&#xff0c;熟悉项目的结构&#xff0c;能够…

kylin集群反向代理(健康检查)

前面一篇文章提到了使用nginx来对kylin集群进行反向代理&#xff0c; kylin集群使用nginx反向代理-CSDN博客文章浏览阅读349次&#xff0c;点赞8次&#xff0c;收藏9次。由于是同一个集群的&#xff0c;元数据没有变化&#xff0c;所以&#xff0c;直接将原本的kylin使用scp的…

C++学习笔记——类继承

目录 一、一个简单的基类 1.1封装性 1.2继承性 1.3虚函数 1.4多态性 二、基类 2.1一个简单的C基类的示例 2.2 Animal是一个基类。 三、继承 3.1概念 3.2is-a关系 3.3多态公有继承 3.4静态联编和动态联编 3.5访问控制 3.6ABC理念 一、一个简单的基类 C中的基类是一…

02. 坦克大战项目-准备工作和绘制坦克

02. 坦克大战项目-准备工作和绘制坦克 01. 准备工作 1. 首先我们要创建四个类 1. Tank类 介绍&#xff1a;Tank 类主要用来表示坦克的基本属性和行为 public class Tank {private int x;//坦克的横坐标private int y;//坦克的纵坐标public int getX() {return x;}public v…