设计模式学习笔记之享元模式

本贴最后更新于 2108 天前,其中的信息可能已经时过境迁

前言

这是一篇学习笔记,内容很多是来源于网上的资料,然后按照自己学习情况进行的总结,有些是自身的感受,有些是网上比较好的资料的引用。

如果有人看到我写的笔记有不对的地方欢迎留言指出来,是真的欢迎指出来,因为我可能会错很久,然后才发现。学习技术不能闭门造车,要多交流,多讨论,多思考才能成长的快,学的快。

我的个人博客:海加尔金鹰

什么是享元模式

享元模式的定义与意义

Use sharing to support large numbers of fine-grained objects efficiently.
使用共享对象可有效地支持大量的细粒度的对象。

在面向对象的过程当中,对象的创建是很常见的事情,当对象的的数量过多时,就会带来运行性能下降的问题(消耗了太多的内存)。
享元模式主要用来避免系统创建多个重复对象,达到减少内存消耗的目的。
享元对象能做到共享的关键是区分内部状态和外部状态,内部状态指的是对象创建不会在发生变化的属性,外部状态是指会发生改变的属性。
比较金典的例子说明:围棋游戏当中,存在大量的黑子和白子,每个黑子除了落点位置不同之外,其他的大小颜色都相同。通过享元模式只需要创建一个黑子对象。每次落子的时候获取到的黑子都是同一个对象,大大减少了对象的创建和销毁,提高了性能。

享元模式的结构与角色

享元模式四大角色:

  1. 抽象享元角色(Flyweight):是所有的具体享元类的基类,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入。
  2. 具体享元(Concrete Flyweight)角色:实现抽象享元角色中所规定的接口。
  3. 非享元(Unsharable Flyweight)角色:是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法。
  4. 享元工厂(Flyweight Factory)角色:负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。1^

享元模式结构实例图:

继承
设置外部状态
创建
Concrete Flyweight
Flyweight
Unsharable Flyweight
Flyweight Factory

享元模式代码实现

享元模式两种分类:
单纯享元模式:不包含非享元角色的,所有的具体享元角色都是可以共享的。
复合享元模式:包含非享元角色,通过组合模式,为不同的单纯享元对象设置相同的外部状态。

单纯享元模式

第一步:创建享元角色类

public abstract class Flyweight { /** * location 表示位置属性,外部状态,会发生改变。 */ public abstract void setLocation(String location); }

第二步:创建具体享元类,内部状态在创建对象时必须设置好。

public class ConcreteFlyweight extends Flyweight { //颜色 内部状态,创建后不会改变的属性 private String color = null; //创建对象的时候必须设置好对象的内部状态,同时设置该构造器为protect 不允许在其他的地方创建该对象,只能通过享元工厂类创建。方便全局的管理。 protected ConcreteFlyweight(String color){ this.color=color; } @Override public void setLocation(String location) { // doSomething System.out.println("===color :" +color+"===location :"+location); } }

第三步:创建享元工厂类,通过单例模式保证每次获取到的工厂类都是同一个,不然无法实现全局共享。

public class FlyweightFactory { /** * 通过工厂模式控制享元对象的生成时, * 必须保证每次获取到的工厂是同一个工厂, * 所以采用单例模式来控制工厂模式的创建。 */ private static FlyweightFactory instance = new FlyweightFactory(); private FlyweightFactory() { } public static FlyweightFactory getInstance() { return instance; } //享元池 private Map<String, Flyweight> flyweightMap = new HashMap<>(); /** * 单纯享元对象获取 * @param color * @return */ public Flyweight getFlyweight(String color) { // 判断享元池是否包含这个享元对象 if (flyweightMap.containsKey(color)) { return flyweightMap.get(color); } else { //如果不包含就创建一个 Flyweight flyweight = new ConcreteFlyweight(color); flyweightMap.put(color, flyweight); return flyweight; } } public void showNumber(){ System.out.println("享元池里面现在有:"+flyweightMap.size()+"个享元对象"); } }

第四步:创建测试类进行测试及结果

public class FlyweightTest { public static void main(String[] args) { FlyweightFactory flyweightFactory = FlyweightFactory.getInstance(); flyweightFactory.getFlyweight("黑色").setLocation("北京"); flyweightFactory.getFlyweight("黑色").setLocation("南京"); flyweightFactory.getFlyweight("黑色").setLocation("西京"); flyweightFactory.getFlyweight("黑色").setLocation("东京"); flyweightFactory.getFlyweight("白色").setLocation("北京"); flyweightFactory.getFlyweight("白色").setLocation("上海"); flyweightFactory.getFlyweight("红色").setLocation("广东"); flyweightFactory.getFlyweight("红色").setLocation("北京"); flyweightFactory.showNumber(); } }
===color :黑色===location :北京 ===color :黑色===location :南京 ===color :黑色===location :西京 ===color :黑色===location :东京 ===color :白色===location :北京 ===color :白色===location :上海 ===color :红色===location :广东 ===color :红色===location :北京 享元池里面现在有:3个享元对象

可以看到只创建了三个对象。节省了对象的创建。

复合享元模式

在单纯享元模式的代码基础上,多了一个非享元角色,主要功能是为多个不同的享元对象设置统一的外部状态。
第一步:添加非享元角色

public class UnsharableFlyweight extends Flyweight { //用于保存单纯享元对象 private Map<String,Flyweight> flys = new HashMap<>(); /** * 增加一个新的单纯享元对象到集合中 * @param key * @param fly 单纯享元对象 */ public void add(String key , Flyweight fly){ flys.put(key,fly); } /** * 为所有的单纯享元对象统一设置相同的外部状态 * @param location */ @Override public void setLocation(String location) { for (String s : flys.keySet()) { flys.get(s).setLocation(location); } } }

第二步:在享元工厂类当中添加创建非享元对象的方法

/** * 复合享元工厂方法 */ public Flyweight getUnsharableFlyweight(List<String> colors){ UnsharableFlyweight fly= new UnsharableFlyweight(); for(String color: colors){ fly.add(color,this.getFlyweight(color)); } return fly; }

第三步:创建测试类进行测试及结果

public class FlyweightTest { public static void main(String[] args) { FlyweightFactory flyweightFactory = FlyweightFactory.getInstance(); List<String> colors = new ArrayList<>(); colors.add("黑色"); Flyweight flyweight1 = flyweightFactory.getUnsharableFlyweight(colors); Flyweight flyweight2 = flyweightFactory.getUnsharableFlyweight(colors); System.out.println("非享元角色是否可以共享对象:" + (flyweight1 == flyweight2)); System.out.println("---------------------------------"); String color = "白色"; Flyweight fly1 = flyweightFactory.getFlyweight(color); Flyweight fly2 = flyweightFactory.getFlyweight(color); System.out.println("享元角色是否可以共享对象:" + (fly1 == fly2)); flyweightFactory.showNumber(); } }
非享元角色是否可以共享对象:false --------------------------------- 享元角色是否可以共享对象:true 享元池里面现在有:2个享元对象

享元模式典型应用及总结

典型应用

可以参考 JAVA 里面的 String 类,Integer,Long 类等等。

享元模式优点

  • 可以极大减少内存中对象的数量,节约系统资源,提高系统性能。
  • 享元模式的内部状态不会改变,因此可以在不同的环境中被共享。

享元模式缺点

  • 系统代码变得复杂多样,需要分离出内部状态和外部状态。

总结:个人感觉享元模式的使用频率应该会越来越少了,毕竟享元模式的主要目的是节约内存,提高性能。但是目前硬件条件的提升,渐渐弥补了内存不足的缺点。并且如果现在一个程序要占用非常高的内存的话,需要认真考量下这个程序到底设计的有没有问题。

  • 设计模式

    设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

    201 引用 • 120 回帖
  • 享元模式
    2 引用

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • 爬虫

    网络爬虫(Spider、Crawler),是一种按照一定的规则,自动地抓取万维网信息的程序。

    106 引用 • 275 回帖
  • flomo

    flomo 是新一代 「卡片笔记」 ,专注在碎片化时代,促进你的记录,帮你积累更多知识资产。

    6 引用 • 143 回帖
  • 代码片段

    代码片段分为 CSS 与 JS 两种代码,添加在 [设置 - 外观 - 代码片段] 中,这些代码会在思源笔记加载时自动执行,用于改善笔记的样式或功能。

    用户在该标签下分享代码片段时需在帖子标题前添加 [css] [js] 用于区分代码片段类型。

    208 引用 • 1469 回帖 • 1 关注
  • OpenCV
    15 引用 • 36 回帖
  • 旅游

    希望你我能在旅途中找到人生的下一站。

    100 引用 • 905 回帖
  • MyBatis

    MyBatis 本是 Apache 软件基金会 的一个开源项目 iBatis,2010 年这个项目由 Apache 软件基金会迁移到了 google code,并且改名为 MyBatis ,2013 年 11 月再次迁移到了 GitHub。

    173 引用 • 414 回帖 • 359 关注
  • MongoDB

    MongoDB(来自于英文单词“Humongous”,中文含义为“庞大”)是一个基于分布式文件存储的数据库,由 C++ 语言编写。旨在为应用提供可扩展的高性能数据存储解决方案。MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,是类似 JSON 的 BSON 格式,因此可以存储比较复杂的数据类型。

    91 引用 • 59 回帖
  • HTML

    HTML5 是 HTML 下一个的主要修订版本,现在仍处于发展阶段。广义论及 HTML5 时,实际指的是包括 HTML、CSS 和 JavaScript 在内的一套技术组合。

    108 引用 • 295 回帖
  • GitBook

    GitBook 使您的团队可以轻松编写和维护高质量的文档。 分享知识,提高团队的工作效率,让用户满意。

    3 引用 • 8 回帖
  • PHP

    PHP(Hypertext Preprocessor)是一种开源脚本语言。语法吸收了 C 语言、 Java 和 Perl 的特点,主要适用于 Web 开发领域,据说是世界上最好的编程语言。

    167 引用 • 408 回帖 • 484 关注
  • 黑曜石

    黑曜石是一款强大的知识库工具,支持本地 Markdown 文件编辑,支持双向链接和关系图。

    A second brain, for you, forever.

    25 引用 • 254 回帖 • 2 关注
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3203 引用 • 8217 回帖 • 2 关注
  • ZooKeeper

    ZooKeeper 是一个分布式的,开放源码的分布式应用程序协调服务,是 Google 的 Chubby 一个开源的实现,是 Hadoop 和 HBase 的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。

    61 引用 • 29 回帖 • 7 关注
  • RESTful

    一种软件架构设计风格而不是标准,提供了一组设计原则和约束条件,主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

    30 引用 • 114 回帖 • 6 关注
  • abitmean

    有点意思就行了

    36 关注
  • Facebook

    Facebook 是一个联系朋友的社交工具。大家可以通过它和朋友、同事、同学以及周围的人保持互动交流,分享无限上传的图片,发布链接和视频,更可以增进对朋友的了解。

    4 引用 • 15 回帖 • 441 关注
  • 锤子科技

    锤子科技(Smartisan)成立于 2012 年 5 月,是一家制造移动互联网终端设备的公司,公司的使命是用完美主义的工匠精神,打造用户体验一流的数码消费类产品(智能手机为主),改善人们的生活质量。

    4 引用 • 31 回帖
  • Typecho

    Typecho 是一款博客程序,它在 GPLv2 许可证下发行,基于 PHP 构建,可以运行在各种平台上,支持多种数据库(MySQL、PostgreSQL、SQLite)。

    12 引用 • 67 回帖 • 445 关注
  • RYMCU

    RYMCU 致力于打造一个即严谨又活泼、专业又不失有趣,为数百万人服务的开源嵌入式知识学习交流平台。

    4 引用 • 6 回帖 • 61 关注
  • WebSocket

    WebSocket 是 HTML5 中定义的一种新协议,它实现了浏览器与服务器之间的全双工通信(full-duplex)。

    48 引用 • 206 回帖 • 281 关注
  • AngularJS

    AngularJS 诞生于 2009 年,由 Misko Hevery 等人创建,后为 Google 所收购。是一款优秀的前端 JS 框架,已经被用于 Google 的多款产品当中。AngularJS 有着诸多特性,最为核心的是:MVC、模块化、自动化双向数据绑定、语义化标签、依赖注入等。2.0 版本后已经改名为 Angular。

    12 引用 • 50 回帖 • 516 关注
  • Kafka

    Kafka 是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者规模的网站中的所有动作流数据。 这种动作(网页浏览,搜索和其他用户的行动)是现代系统中许多功能的基础。 这些数据通常是由于吞吐量的要求而通过处理日志和日志聚合来解决。

    36 引用 • 35 回帖
  • 以太坊

    以太坊(Ethereum)并不是一个机构,而是一款能够在区块链上实现智能合约、开源的底层系统。以太坊是一个平台和一种编程语言 Solidity,使开发人员能够建立和发布下一代去中心化应用。 以太坊可以用来编程、分散、担保和交易任何事物:投票、域名、金融交易所、众筹、公司管理、合同和知识产权等等。

    34 引用 • 367 回帖
  • 前端

    前端技术一般分为前端设计和前端开发,前端设计可以理解为网站的视觉设计,前端开发则是网站的前台代码实现,包括 HTML、CSS 以及 JavaScript 等。

    247 引用 • 1340 回帖
  • GitLab

    GitLab 是利用 Ruby 一个开源的版本管理系统,实现一个自托管的 Git 项目仓库,可通过 Web 界面操作公开或私有项目。

    46 引用 • 72 回帖 • 1 关注
  • Chrome

    Chrome 又称 Google 浏览器,是一个由谷歌公司开发的网页浏览器。该浏览器是基于其他开源软件所编写,包括 WebKit,目标是提升稳定性、速度和安全性,并创造出简单且有效率的使用者界面。

    63 引用 • 289 回帖
  • 招聘

    哪里都缺人,哪里都不缺人。

    188 引用 • 1057 回帖