第 14 章 享元模式

1、展示网站项目需求

小型的外包项目,给客户A做一个产品展示网站, 客户A的朋友感觉效果不错,也希望做这样产品展示网站,但是要求都有些不同:

  1. 有客户要求以新闻的形式发布
  2. 客户人要求以博客的形式发布
  3. 有客户希望以微信公众号的形式发布

2、传统方案解决网站展现项目

方案描述

  1. 直接复制粘贴一份,然后根据客户不同要求,进行定制修改
  2. 给每个网站租用一个空间
  3. 方案设计示意图


问题分析:传统方案解决网站展现项目

  1. 需要的网站结构相似度很高,而且都不是高访问量网站,如果分成多个虚拟空间来处理,相当于一个相同网站的实例对象很多,造成服务器的资源浪费
  2. 解决思路:整合到一个网站中,共享其相关的代码和数据,对于硬盘、内存、 CPU、数据库空间等服务器资源都可以达成共享,减少服务器资源
  3. 对于代码来说, 由于是一份实例,维护和扩展都更加容易
  4. 上面的解决思路就可以使用享元模式来解决

3、享元模式基本介绍

  1. 享元模式(Flyweight Pattern) 也叫蝇量模式:运用共享技术有效地支持大量细粒度的对象
  2. 常用于系统底层开发,解决系统的性能问题。像数据库连接池,里面都是创建好的连接对象,在这些连接对象中有我们需要的则直接拿来用,避免重新创建, 如果没有我们需要的,则创建一个
  3. 享元模式能够解决重复对象的内存浪费的问题,当系统中有大量相似对象,需要缓冲池时。 不需总是创建新对象,可以从缓冲池里拿。这样可以降低系统内存,同时提高效率
  4. 享元模式经典的应用场景就是池技术了,String`常量池、数据库连接池、缓冲池等等都是享元模式的应用,享元模式是池技术的重要实现方式

代码示例

String s = "hello";
String s2 = new String("hello");

内存结构

4、享元模式的原理类图

  1. FlyWeight 是抽象的享元角色,他是产品的抽象类,定义了对象的外部状态和内部状态(后面介绍) 的接口规范(接口)或默认实现(抽象类)
  2. ConcreteFlyWeight 是具体的享元角色,是具体的产品类,实现抽象角色定义相关业务方法
  3. UnsharedConcreteFlyWeight 是不可共享的角色,一般不会出现在享元工厂中
  4. FlyWeightFactory 是享元工厂类,用于构建池的容器,提供从池中获取对象的相关方法

5、内部状态和外部状态

比如围棋、五子棋、跳棋,它们都有大量的棋子对象,围棋和五子棋只有黑白两色,跳棋颜色多一点,所以棋子颜色就是棋子的内部状态;而各个棋子之间的差别就是位置的不同,当我们落子后,落子颜色是定的,但位置是变化的,所以棋子坐标就是棋子的外部状态

  1. 享元模式提出了两个要求:细粒度和共享对象。这里就涉及到内部状态和外部状态了,即将对象的信息分为两个部分: 内部状态和外部状态
  2. 内部状态指对象共享出来的信息,存储在享元对象内部且不会随环境的改变而改变
  3. 外部状态指对象得以依赖的一个标记,是随环境改变而改变的、不可共享的状态

举个例子:围棋理论上有361个空位可以放棋子,每盘棋都有可能有两三百个棋子对象产生,因为内存空间有限,一台服务器很难支持更多的玩家玩围棋游戏,如果用享元模式来处理棋子,那么棋子对象就可以减少到只有两个实例,这样就很好的解决了对象的开销问题

6、享元模式解决网站展现项目

应用实例要求

使用享元模式完成, 前面提出的网站外包问题


类图


代码实现

  1. WebSite:产品的抽象类,定义了产品对象的内部和外部状态的规范,即前面所说的 FlyWeight

    public abstract class WebSite {public abstract void use(User user);// 抽象方法}
    
  2. ConcreteWebSite:具体的产品类,继承了 WebSite 抽象类,实现了具体的业务方法,即前面所说的 ConcreteFlyWeight

    //具体网站
    public class ConcreteWebSite extends WebSite {// 共享的部分,内部状态private String type = ""; // 网站发布的形式(类型)// 构造器public ConcreteWebSite(String type) {this.type = type;}@Overridepublic void use(User user) {System.out.println("网站的发布形式为:" + type + " 在使用中 .. 使用者是" + user.getName());}}
    
  3. WebSiteFactory:产品工厂类,生产具体的产品(WebSite 对象),并构建产品池,即前面所说的 FlyWeightFactory

    // 网站工厂类,根据需要返回压一个网站
    public class WebSiteFactory {// 集合, 充当池的作用private HashMap<String, ConcreteWebSite> pool = new HashMap<>();// 根据网站的类型,返回一个网站, 如果没有就创建一个网站,并放入到池中,并返回public WebSite getWebSiteCategory(String type) {if (!pool.containsKey(type)) {// 就创建一个网站,并放入到池中pool.put(type, new ConcreteWebSite(type));}return (WebSite) pool.get(type);}// 获取网站分类的总数 (池中有多少个网站类型)public int getWebSiteCount() {return pool.size();}}
    
  4. User:实体类

    public class User {private String name;public User(String name) {super();this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}}
    
  5. Client:客户端

    public class Client {public static void main(String[] args) {// 创建一个工厂类WebSiteFactory factory = new WebSiteFactory();// 客户要一个以新闻形式发布的网站WebSite webSite1 = factory.getWebSiteCategory("新闻");webSite1.use(new User("tom"));// 客户要一个以博客形式发布的网站WebSite webSite2 = factory.getWebSiteCategory("博客");webSite2.use(new User("jack"));// 客户要一个以博客形式发布的网站WebSite webSite3 = factory.getWebSiteCategory("博客");webSite3.use(new User("smith"));// 客户要一个以博客形式发布的网站WebSite webSite4 = factory.getWebSiteCategory("博客");webSite4.use(new User("king"));System.out.println("网站的分类个数=" + factory.getWebSiteCount());}}
    

总结

  1. 利用享元模式,我们能够把外部状态(User)和内部状态(type)分开,对于共享的部分,我们共用即可

  2. 比如网站类型(type)不同时,我们才会创建对应的网站实例,再将其放入对象池中,如果网站类型(type)相同,我们直接共享即可(享元)

  3. 博客类型相同可以共用,但是我们可以通过传入 User 形参,让不同的使用者,访问同一份博客

7、JDK Interger 源码分析

代码

public class FlyWeight {public static void main(String[] args) {// 如果 Integer.valueOf(x) x 在 -128 --- 127 直接,就是使用享元模式返回,如果不在该范围类,则仍然 new// 小结:// 1. 在valueOf 方法中,先判断值是否在 IntegerCache 中,如果不在,就创建新的Integer(new), 否则,就直接从 缓存池返回// 2. valueOf 方法,就使用到享元模式// 3. 如果使用valueOf 方法得到一个Integer 实例,范围在 -128 - 127 ,执行速度比 new 快Integer x = Integer.valueOf(127); // 得到 x实例,类型 IntegerInteger y = new Integer(127); // 得到 y 实例,类型 IntegerInteger z = Integer.valueOf(127);// ..Integer w = new Integer(127);System.out.println(x.equals(y)); // 大小,trueSystem.out.println(x == y); // falseSystem.out.println(x == z); // trueSystem.out.println(w == x); // falseSystem.out.println(w == y); // falseInteger x1 = Integer.valueOf(200);Integer x2 = Integer.valueOf(200);System.out.println("x1==x2=" + (x1 == x2)); // false}}

源码追踪

  1. Integer.valueOf() 方法:该方法使用享元模式,如果数字范围在 [IntegerCache.low, IntegerCache.high] 之间,则直接返回缓存池中的对象,否则使用 new 的方式创建

    public static Integer valueOf(int i) {if (i >= IntegerCache.low && i <= IntegerCache.high)return IntegerCache.cache[i + (-IntegerCache.low)];return new Integer(i);
    }
    
  2. IntegerCache 用于为 [-128, 127] 数值的缓存池,事先就已经将 cache[] 缓存池创建好了

    private static class IntegerCache {static final int low = -128;static final int high;static final Integer cache[];static {// high value may be configured by propertyint 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) {// If the property cannot be parsed into an int, ignore it.}}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() {}
    }
    

8、享元模式的注意事项

享元模式的注意事项和细节

  1. 在享元模式这样理解,“享”就表示共享,“元”表示对象
  2. 系统中有大量对象, 这些对象消耗大量内存, 并且对象的状态大部分可以外部化时,我们就可以考虑选用享元模式
  3. 用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象,经常用HashMap存储共享对象
  4. 享元模式大大减少了对象的创建,降低了程序内存的占用,提高效率
  5. 享元模式提高了系统的复杂度。需要分离出内部状态和外部状态,而外部状态具有固化特性,不应该随着内部状态的改变而改变,这是我们使用享元模式需要注意的地方
  6. 使用享元模式时,注意划分内部状态和外部状态,并且需要有一个工厂类加以控制。
  7. 享元模式经典的应用场景是需要缓冲池的场景,比如 String 常量池、 数据库连接池

第 14 章 享元模式相关推荐

  1. 第11章 享元模式(Flyweight Pattern)

    第11章 享元模式(Flyweight Pattern) 原文 第11章 享元模式(Flyweight Pattern) 概述:   面向对象的思想很好地解决了抽象性的问题,一般也不会出现性能上的问题 ...

  2. 【设计模式】第七章 享元模式

    第七章 享元模式 文章目录 第七章 享元模式 一.介绍 二.结构 三.实现 一.介绍 在面向对象程序设计过程中,有时会面临要创建大量相同或相似对象实例的问题.创建过多的对象将会耗费很多的系统资源,它是 ...

  3. 【设计模式2022】第十三章 享元模式

    [设计模式2022]第十三章 享元模式 文章目录 [设计模式2022]第十三章 享元模式 一.概述 二.结构 三.实现 四.分析 1.优点 2.缺点 3.使用场景 五.Integer 包装类 一.概述 ...

  4. 实现对象的复用——享元模式

    本文转载自 :http://blog.csdn.net/lovelion/article/details/7667781 当前咱们国家正在大力倡导构建和谐社会,其中一个很重要的组成部分就是建设资源节约 ...

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

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

  6. 软件设计模式--第三章 结构型模式--享元模式

    目录 第二章 结构型模式 1.结构型模式概述 2.享元模式 (1)模式的定义与特点 (2)模式的结构与实现(Flyweight ) (3)应用场景 (4)扩展 第二章 结构型模式 1.结构型模式概述 ...

  7. 设计模式之享元模式(Flyweight)摘录

    23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而 ...

  8. 北风设计模式课程---享元模式

    北风设计模式课程---享元模式 一.总结 一句话总结: 不仅要通过视频学,还要看别的博客里面的介绍,搜讲解,搜作用,搜实例 1.享元模式的本质是什么? 池技术:各种缓存池都是享元模式的体现 说到享元模 ...

  9. 26享元模式(Flyweight Pattern)

    面向对象的代价     面向对象很好地解决了系统抽象性的问题,同时在大多数情况下,也不会损及系统的性能.但是,在 某些特殊的应用中下,由于对象的数量太大,采用面向对象会给系统带来难以承受的内存开销.比 ...

最新文章

  1. NginxApachePHP参数汇总
  2. 非结构化信息-》半结构化-》结构化-》关联数据体系-》数据挖掘-》故事化呈现-》决策导向
  3. 怎么查电脑系统版本_电脑系统垃圾清理利器,专业、小巧且免费,有这一个就够了...
  4. 精妙SQL语句【转】
  5. 广搜 广搜 poj 3984
  6. 第一个cocos2d-x 项目
  7. 离线安装NLTK工具包
  8. acriviti流程经过节点后执行方法,serviceTask
  9. MyCat分片规则之枚举分片
  10. 【项目难点】电商后台管理系统
  11. 服务器数据抓包(原来微信图片真的可以抓包看的)
  12. mpeg 音频格式科普
  13. 苹果手机如何设置喜欢的铃声?想给自己的iphone换个别具特色的铃声吗?
  14. 大一第一学期总结:既然选择了远方,便只顾风雨兼程
  15. 注解(annotations)列表
  16. 温州科技职业学院 计算机网络技术,浙江【温州科技职业学院】_计算机网络技术专业建设方案.doc...
  17. 抖音电脑版怎么自动播放视频?
  18. 类似staruml的工具有哪些?
  19. 条码打印出现乱码的解决方案
  20. 何金龙 mysql_办innodb

热门文章

  1. 抗腹泻药行业调研报告 - 市场现状分析与发展前景预测
  2. 2017级C语言大作业 - 见缝插针
  3. TikTok 与 Oracle 的交易将开创两个危险的先例
  4. 日均请求量1.6万亿次背后,DNSPod的秘密-国密DoH篇
  5. .NET 6 Preview 4 已发布,这些新功能值得关注!
  6. AI 工程师的崩溃是在哪一瞬间
  7. “我在苹果商店下载了一个诈骗App,损失60万美金!”
  8. 红帽停止对 CentOS 投资
  9. 牛!发出中国第一封电子邮件,注册登记域名 CN,中国互联网之父传奇
  10. Dubbo to Mesh 云原生架构改造方案解析