Java常用设计模式————享元模式

引言

享元模式,也叫蝇量模式(Flyweight Pattern)。运用共享技术有效地支持大量细粒度的对象。

享元模式常用于系统底层开发,解决系统的性能问题。例如数据库连接池,里面都是创建好的连接对象,在这些连接对象中,如果有我们需要的则直接拿来用,避免重新创建,如果没有,再去创建。

享元模式能够解决重复对象的内存浪费问题,在一些不需要总是创建新对象,可以使用对象资源池时,可以从资源池中获取。这样可以降低系统内存消耗,同时提高效率。

享元模式经典的应用场景就是池技术,String常量池、数据库连接池、缓冲池等等都是享元模式的应用。

一、享元模式类图

享元模式经常会配合简单工厂来使用。

上图中,ShapeFactory是获取Shape的工厂类,其实这就是使用了简单工厂模式,其中的shapeMap是一个常量池,存储了Shape类型,本例中就是Circle对象。

getCircle方法需要传入一个参数,这个参数是颜色信息,方法内部会判断常量池中是否已经存在对应颜色信息的Circle对象,如果有则直接返回,如果没有,就会创建,并放入到shapeMap中,这是享元模式的关键代码

注意,在使用享元模式时,有时候需要区分享元对象的内部状态外部状态,以享元对象为参考系的话,内部状态是不变的,比如上图案例中,Circle对象是享元对象,其内部状态就是确定的颜色、圆心坐标、以及半径,而外部状态,可能是不同的使用者,因为享元对象是共享的,所以面对多个使用者,其内部状态自然就要求是不变的。在复杂的业务场景,可能需要享元对象暂时保存可变的外部状态,比如在数据库连接池的享元模式中,线程需要独占一个连接对象,这里面可能就需要连接对象暂时性保存线程的标识,待释放连接后才会清除状态。这个层面的问题是需要在使用享元模式时进一步思考的。

二、代码实现

Shape产品抽象接口

public interface Shape {void draw();
}

Circle具体的产品类

public class Circle implements Shape {private String color;private int x;private int y;private int radius;public Circle(String color) {super();this.color = color;}@Overridepublic void draw() {System.out.println("Circle: Draw() [Color : " + color + ", x : " + x + ", y :" + y + ", radius :" + radius);}// ----get、set---
}

ShapeFactory享元对象工厂

public class ShapeFactory {private static final Map<String, Shape> circleMap = new HashMap<>();public static Shape getCircle(String color) {System.out.println("获取" + color + "的圆形");Circle circle = (Circle) circleMap.get(color);if (circle == null) {circle = new Circle(color);circleMap.put(color, circle);System.out.println("Creating circle of color : " + color);}return circle;}
}

Client测试类

public class Client {private static final String[] colors = { "Red", "Green", "Blue", "White", "Black" };public static void main(String[] args) {for (int i = 0; i < 20; i++) {Circle circle = (Circle) ShapeFactory.getCircle(getRandomColor());circle.setX(getRandomX());circle.setY(getRandomY());circle.setRadius(100);circle.draw();}}private static String getRandomColor() {String color = colors[(int) (Math.random() * colors.length)];return color;}private static int getRandomX() {return (int) (Math.random() * 100);}private static int getRandomY() {return (int) (Math.random() * 100);}
}

输出结果:

获取White的圆形
Creating circle of color : White
Circle: Draw() [Color : White, x : 45, y :49, radius :100
获取Green的圆形
Creating circle of color : Green
Circle: Draw() [Color : Green, x : 85, y :96, radius :100
获取Blue的圆形
Creating circle of color : Blue
Circle: Draw() [Color : Blue, x : 0, y :48, radius :100
获取Red的圆形
Creating circle of color : Red
Circle: Draw() [Color : Red, x : 7, y :75, radius :100
获取Red的圆形
Circle: Draw() [Color : Red, x : 96, y :82, radius :100
获取Green的圆形
Circle: Draw() [Color : Green, x : 76, y :86, radius :100
获取Green的圆形
Circle: Draw() [Color : Green, x : 64, y :77, radius :100
获取Black的圆形
Creating circle of color : Black
Circle: Draw() [Color : Black, x : 51, y :90, radius :100
获取Red的圆形
Circle: Draw() [Color : Red, x : 55, y :22, radius :100
获取Blue的圆形
Circle: Draw() [Color : Blue, x : 25, y :87, radius :100
获取Black的圆形
Circle: Draw() [Color : Black, x : 39, y :2, radius :100
获取Black的圆形
Circle: Draw() [Color : Black, x : 80, y :10, radius :100
获取White的圆形
Circle: Draw() [Color : White, x : 10, y :96, radius :100
获取Green的圆形
Circle: Draw() [Color : Green, x : 64, y :68, radius :100
获取Red的圆形
Circle: Draw() [Color : Red, x : 56, y :85, radius :100
获取White的圆形
Circle: Draw() [Color : White, x : 87, y :44, radius :100
获取Blue的圆形
Circle: Draw() [Color : Blue, x : 39, y :77, radius :100
获取Blue的圆形

三、Integer中的享元模式

在Integer整型包装类中,用到了享元模式:

这个valueOf()相当于上一节案例中的 shapeFactory 的 getCircle(String color) 方法。

valueOf 需要传入一个 int 类型的参数用于创建 Integer 对象,可以看到,方法中会判断参数的大小,如果正好处于 IntegerCache 常量池的范围内,那么就会直接返回,如果没有,再通过new关键字来创建。下图是 IntegerCache 的部分代码:

名为 IntegerCache 的静态内部类会初始化一个 Integer 数组 cache[],用于存储 -128 到 127 之间的整数,据专家说,这些数字在实际应用中的使用频率极高,那么如果可以将这些数字以常量池的形式缓存起来,供客户端使用,那么系统的性能将会得到一定程度的提升。这就是享元模式的具体应用。

总结

享元模式需要在工厂模式的基础上维护一个资源池,供客户端获取享元对象。

它主要解决的问题是系统中大量重复对象的创建与销毁问题

缓存已经创建好的对象并直接返回可以极大的减少重复对象的内存占用,以及降低创建和销毁对象的系统开销。

在 JDK 的 Integer 、String 等底层实现中,都有用到享元模式。

有时候,需要区分享元对象的内部状态外部状态。以享元对象为参考系,内部状态是不变的,而外部状态可能是不同的调用者或者其他可变信息。这在复杂的享元模式中需要谨慎留意。

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

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

相关文章

IDEA——常用基础设置

一、设置入口 File—>Settings... 或者 在工具栏的“小扳手”图标。 二、主题设置 三、编辑通用设置 设置面板中的 Editor 3.1 自动导包 可以设置IDEA自动为程序导包&#xff0c;在书写时加入准确的导包&#xff0c;在书写时优化导包&#xff08;自动去掉未使用的&#…

IDEA——常用快捷键

引言 总结 IDEA 的常用快捷键&#xff0c;除了部分快捷键与 Eclipse 保持一致之外&#xff0c;枚举更多的实用快捷键。 一、如何设置快捷键 在 Settings -> Keymap 中&#xff0c;下拉框里选择 Eclipse &#xff0c;即可将 IDEA 的快捷键设置为与 Eclipse 保持一致。但并…

IDEA——常用代码模板

引言 IDEA 提供了一些内置的代码模板&#xff0c;可以让开发者快速方便的使用&#xff0c;当然 eclipse 中也是有的&#xff0c;比如输入 syso 快速生成输出语句&#xff0c;main 快速生成主函数等。 idea 的模板设置都在 Settings --> Live Templates 和 General-->Po…

IDEA——Git 的设置与使用

引言 在本机下载好 Git 之后&#xff0c;再去在 IDEA 中设置 Git 相关的参数。详细的 Git 操作和 Eclipse 大同小异&#xff0c;可以移步至&#xff1a;《Git必知必会》 一、设置Git执行程序路径 二、导入一个新的远程 git 托管项目 打开 File ——> New ——> Project…

IDEA——Maven的配置与使用

引言 简单介绍一下如何在 idea 中配置maven&#xff0c;以及如何去使用 maven 。 一、配置 Maven home Maven home 和 settings 文件一般都需要进行重新设置&#xff0c;关联到本机已经安装好的 maven 版本&#xff0c;settings 这里可以使用默认&#xff0c;也可以设置为 ma…

Spring Cloud Alibaba——Nacos实现服务治理

引言 本博客总结微服务开发中各个微服务调用的实现&#xff0c;并使用 Nacos 完成服务注册和发现。 文章中会涉及到 maven 的使用&#xff0c;以及 spring boot 的一些知识。开发工具采用 IDEA 2020.2。 设计一个电商订单和商品购买微服务&#xff0c;实现微服务的注册发现与…

Spring Cloud —— Feign 实现服务调用

引言 本篇博客简单介绍 Feign 的基础知识和基本应用&#xff0c;以前一篇博客《Spring Cloud Alibaba——Nacos实现服务治理》为代码基础&#xff0c;实现更简单的微服务调用方式。 一、什么是Feign restTemplate 实现的微服务调用方式&#xff1a; // 调用商品微服务&…

Spring Cloud —— 负载均衡与 Ribbon 应用

引言 本篇博客简单介绍微服务负载均衡的概念&#xff0c;并通过 IDEA 多端口启动应用的方式&#xff0c;模拟多个应用实例&#xff0c;使用自定义和 Ribbon 两种方式实现基本的负载均衡策略。 微服务代码以《Spring Cloud Alibaba——Nacos实现服务治理》为基础。 一、什么是…

Spring —— 容器内部逻辑

引言 上一篇关于IoC容器的详解《Spring —— IoC 容器详解》真是工程浩大&#xff0c;可以说Spring官网对核心中的核心IOC容器做了非常全面的使用说明&#xff0c;包括在《Spring揭秘》中让我一直没有成功的Method Injection&#xff0c;官网也解决了我的疑惑&#xff0c;并最…

2020 年度总结

2020年给我的感觉是短平快的一年。 由于年初的新冠肺炎疫情&#xff0c;我大半年都呆在北京的破旧出租屋里写代码。整个春天和夏天&#xff0c;平平无奇。 2月1日返京&#xff0c;居家办公&#xff0c;夜跑。8月复工&#xff0c;疯狂爆痘、烂脸&#xff0c;月末落户天津。9月…

JDBC——概述与JDBC的使用

引言 一直希望深入学习一下数据库持久化技术&#xff0c;接触过Hibernate、Mybatis&#xff0c;也使用过Spring事务管理来控制回滚操作&#xff0c;但是越发觉得底层知识有一定的知识盲区和空洞。 很多ORM框架都是基于JDBC规范来进行构建的&#xff0c;因此&#xff0c;学习J…

JDBC——编程式事务的实现逻辑

引言 数据库事务的概念和基础&#xff0c;总结在《MySQL 基础 ————事务与隔离级别总结》。 本篇博客通过“JDBC 纯编码”方式实现事务控制&#xff0c;完成一个 A 给 B 转账的小功能&#xff0c;在进一步熟练JDBC的编程流程的同时&#xff0c;重点关注 Java 语言如何操作…

排序算法——插入排序

一、算法思想 给定一个无序数列&#xff0c;模拟一个指针从第0位开始向后&#xff0c;始终保持当前位置左边的数列是有序的。 指针位置上的元素依次与前面的元素比较&#xff0c;当遇到小于自己的数或右边已经没有元素时&#xff0c;都停止比较&#xff0c;开始下一轮。 生活…

Linux 底层原理 —— epoll 与多路复用

引言 epoll 是 Linux 系统下高性能网络服务的必备技术&#xff0c;很多面试中高频出现的 Nginx、Redis 都使用了这一技术&#xff0c;本文总结 linux 多路复用模型的演变过程&#xff0c;看一看epoll 是如何实现高性能的。 一、相关基础知识 1.1 文件描述符 文件描述符&…

异或运算的应用

一、基础知识 异或运算&#xff0c;相异为1。 异或运算是一种常用的位运算&#xff0c;在算法题中&#xff0c;对于避免额外的空间复杂度有独特的用处。 异或运算也被称为“无进位相加”&#xff0c;它具有以下特性&#xff1a; 特性1&#xff1a;0 ^ N N 特性2&#xff1a…

单向队列、双端队列、栈的模型实现

引言 自己实现简单的队列、栈的逻辑结构。 队列都包含头和尾两个指针&#xff0c;简单的单向队列只能在一端&#xff08;如&#xff1a;head端&#xff09;入列&#xff0c;在另一端&#xff08;如&#xff1a;tail 端&#xff09;出列&#xff1b;双端队列可以在 head 进出&…

递归算法及其时间复杂度分析

引言 “递归” 一词是比较专业的计算机术语&#xff0c;在现实生活中&#xff0c;有一个更可爱的词——“套娃”。如果把“递归算法”叫做“套娃算法”&#xff0c;或许可以减少一些恐惧程度。 套娃是有限的&#xff0c;同样&#xff0c;递归也是有限的&#xff0c;这和我们经…

算法设计中的基础常用代码

引言 本篇博客旨在记录一些基础算法知识的常见组合用法&#xff0c;以及何时使用&#xff0c;需要注意的问题等&#xff0c;长期更新。 为什么要这样总结呢&#xff1f;难道掌握了位运算、常用算法工具API的定义还不够吗&#xff1f; 这是因为某些知识比如 &、 |、 ~、 …

Redis —— 常用命令一览

引言 参考《菜鸟教程 Redis 常用命令》&#xff0c;其中红色为极其重要&#xff0c;蓝色为重要。 一、总览 二、key相关命令 三、String 相关命令 四、Hash 相关命令 五、List 相关命令 六、Set 相关命令 七、ZSet 相关命令

Redis 实用技术——消息发布和订阅

引言 发布订阅模型是redis的重要功能&#xff0c;它可以像网站动态一样&#xff0c;将消息发送到多个订阅者的主页里。 一、常用命令 二、消息格式 消息是一个有三个元素的多块响应&#xff1a; 如上图&#xff0c;发布者向 mysub 频道发送了一条消息&#xff0c;redis会返回…