引言

享元模式,也叫蝇量模式(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 等底层实现中,都有用到享元模式。

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

Java常用设计模式————享元模式相关推荐

  1. 【设计模式】Java设计模式 - 享元模式

    [设计模式]Java设计模式 - 享元模式

  2. Python设计模式-享元模式

    Python设计模式-享元模式 基于Python3.5.2,代码如下 #coding:utf-8class Coffee:name = ""price = 0def __init_ ...

  3. 10-Python与设计模式--享元模式

    10-Python与设计模式--享元模式 一.网上咖啡选购平台 假设有一个网上咖啡选购平台,客户可以在该平台上下订单订购咖啡,平台会根据用户位置进行线下配送.假设其咖啡对象构造如下: class Co ...

  4. 设计模式--享元模式实现C++

    /********************************* *设计模式--享元模式实现 *C++语言 *Author:WangYong *Blog:http://www.cnblogs.co ...

  5. 初学Java常用设计模式之——原型模式

    声明:转载请附上原文链接 提示:标题序号从3开始,是照应不同设计模式笔记发布的顺序而定的,比如,第上一篇文章 初学Java常用设计模式之--工厂模式 序号从2开始. 标题后面之所以加上了解,是因为相对 ...

  6. Unity设计模式——享元模式(附代码)

    Unity设计模式--享元模式(附源码) 享元Flyweight模式是什么 享元模式是一种结构型设计模式, 它摒弃了在每个对象中保存所有数据的方式, 通过共享多个对象所共有的相同状态, 让你能在有限的 ...

  7. 第二十二章 Caché 设计模式 享元模式

    文章目录 第二十二章 Caché 设计模式 享元模式 定义 优点 使用场景 结构图 描述 完整示例 实体类 抽象享元角色 实现享元角色 享元工厂 调用 思考 第二十二章 Caché 设计模式 享元模式 ...

  8. JavaScript设计模式-享元模式

    JavaScript设计模式-享元模式 概念 例子 内部状态与外部状态 享元模式的通用结构 例子 总结 github仓库地址:点击 [设计模式例子](https://github.com/fanhua ...

  9. java设计模式---享元模式

    Java深入到一定程度,就不可避免的碰到设计模式这一概念,了解设计模式,将使自己 对java中的接口或抽象类应用有更深的理解.设计模式在java的中型系统中应用广 泛,遵循一定的编程模式,才能使自己的 ...

最新文章

  1. 利用redis实现分布式请求防重复提交
  2. php获取url文件大小,PHP通过URL获取文件大小
  3. leetcode 494. 目标和
  4. db2 linux 导入数据_MySQL数据的导出和导入(Linux)
  5. ubuntu 16.04 x86_64中arm-none-linux-gnueabi-不起作用时的解决方案
  6. ABAP术语-Implementation
  7. [工具]勒索病毒解密工具汇总
  8. PyTorch 深度学习入门
  9. 伦敦银实时行情中的引爆点
  10. 《Scrum实战》第3次课【富有成效的每日站会】作业汇总
  11. 11083 旅游背包(优先做)
  12. 解决方案:macOS Mojave下Pycharm运行pygame无法加载外星人游戏图片以及无法修改颜色
  13. 电脑的任务栏只显示一条杠,没有图标怎么解决,看这里!!!
  14. v-for图片九宫显示
  15. Undefined、Null和NaN有什么区别?
  16. 用OpenWrt软路由做旁路由-VMWARE版
  17. Win10 连接无线不能输入密码字符,一输入就卡死
  18. 【数学建模】数学建模学习3---非线性规划(例题+matlab代码实现)
  19. KISSY整体架构流程
  20. 7-1 时间换算 (10分)

热门文章

  1. Java类类getComponentType()方法与示例
  2. C程序对整数中设置为1的位数进行计数
  3. python 示例_是Python中带有示例的关键字
  4. 面经分享:历时半个月,终于拿到了蚂蚁金服的offer!
  5. 关联数组(associative array)
  6. Git报错: OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to github.com:443
  7. 使用Jquery插件bTabs实现多页签打开效果
  8. Undefined control sequence. \makecover
  9. OCP-052考试题库汇总(58)-CUUG内部解答版
  10. jquery.js把我的时间修改了为什么?_电气老手在PLC程序调试修改时的几个必备小窍门,看你知道几个?...