每日一个设计模式之【享元模式】

文章目录

  • 每日一个设计模式之【享元模式】
    • ☁️前言🎉🎉🎉
    • 🌻享元模式概述
    • 🌱享元模式的实现
    • 🌲总结

每日一个设计模式之【享元模式】

☁️前言🎉🎉🎉

  大家好✋,我是知识汲取者😄,今天给大家带来一篇有关享元模式的学习笔记。众所周知能够熟练使用设计模式是一个优秀程序猿的必备技能,当我们在项目中选择一个或多个合适的设计模式,不仅能大大提高项目的稳健性可移植性可维护性,同时还能让你的代码更加精炼,具备艺术美感

  有时候我们在系统中由于对象创建的数量过多,会造成内存溢出,比如我们设计一个下围棋的程序,黑子有181颗、白子有180颗,总共有361颗,如果我们一股脑地不加以注意,每落一颗子就创建一个对象的化,那问题就大量,得创建大量对象。而享元模式的出现就能很好地解决这一弊端,很大程度地节约了内存,同时也能够提高程序的性能,享元模式有类似于线程池,它可以提供一个享元池,享元池中的对象可以被共享,能够被系统复用,从而大大降低对象的创建数量……话不多说,且看下文

image-20221116203813552

推荐阅读

  • 设计模式导学:🚪传送门
  • 每日一个设计模式系列专栏:🚪传送门
  • 设计模式专属Gitee仓库:✈启程
  • 设计模式专属Github仓库:🚀上路
  • 知识汲取者的个人主页:💓点我哦

csdn

🌻享元模式概述

  • 什么是享元模式

    享元模式(Flyweight Pattern)是一种结构型模式,它利用共享技术实现对象的复用,进而减少内存占用和提高程序性能

  • 享元模式的作用:减少对象的创建,减少内存占用和提高程序性能

  • 享元模式的优缺点

    • 优点

      • 提高系统性能。共享模式能够使用一个”享元池“存储提前需要创建的对象,后续需要使用对象可以直接从享元池中获取,而无需去创建,提高对象的创建速度
      • 节约内存。共享模式让对象能够共享使用,进而复用,大大节约了内存的消耗

      ……

    • 缺点

      • 具有一定局限性。享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式
      • 提高了系统的复杂度。享元模式需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱,并且还可能会引起线程安全问题
      • 过渡使用会造成不必要的内存资源浪费。享元模式中享元池的对象都是提前创建的,会占用系统的资源

      ……

  • 享元模式的引用场景

    • 系统需要创建大量粒度较小且相似的对象,可以使用享元模式

    • 系统需要一个缓冲池,可以使用享元模式进行实现

    ……

  • 享元模式的角色划分

    • 抽象享元类(Flyweight):是一个具体实体的抽象类,内部含有所有具体享元类公有的方法,可以是抽象类、接口
    • 具体享元类(ConcreteFlyweight):是一个具体类,内部存储了内部状态和外部状态(也可以将这两个状态存储在抽象享元类中),其实例化对象是享元对象
    • 非共享享元类(UnsharedConcreteFlyweight):是一个具体的类,是享元对象的外部状态
    • 享元工厂(FlyweightFactory):享元模式中的核心角色,内部含有一个享元池,用于提前创建并存储需要使用的享元对象
    • 客户端(Client):系统的外部交互者(对应本文示例中的测试类)
  • 相关概念

    • 内部状态(Intrinsic State):存储在享元对象内部并且不会随环境改变而改变的状态,内部状态可以共享(例如:围棋的颜色)
    • 外部状态(Extrinsic State):随环境改变而改变的状态。享元对象的外部状态通常由客户端保存,并在享元对象被创建之后,需要使用的时候再传入到享元对象内部。一个外部状态与另一个外部状态之间是相互独立的,且不可共享(例如:围棋的坐标)

🌱享元模式的实现

示例

围棋分为黑子和白子两种,所有的棋子非黑即白,所以棋子的颜色是可以被共享的,这是围棋的内部状态;所有的围棋在落子后都有一个独一无二的坐标,所以围棋的落子坐标是不可以被共享的,这是围棋的外部状态。使用享元模式实现围棋对象的创建

image-20221116211828203

  • Step1:创建抽象享元类

    package com.hhxy.chessman;/*** @author ghp* @date 2022/10/19* @title 棋子的抽象类* @description*/
    public abstract class Chessman {protected String color;//围棋的颜色属性,它是棋子的内部状态,享元池中所有的棋子对象共享protected Coordinates coordinates;//围棋的坐标属性,它是棋子外部状态,享元池中棋子对象无法共享/*** 获取棋子的颜色*/public abstract String getColor();/*** 展示棋子的颜色*/public void showColor(){System.out.print("棋子颜色为: " + this.getColor() + "; ");}/*** 展示棋子的坐标*/public void showCoordinates(Coordinates coordinates){this.coordinates = coordinates;System.out.print("棋子的坐标为: " + "(" + this.coordinates.getX() + "," + this.coordinates.getY() + ")");}}
    
  • Step2:创建具体享元类

    1)黑子:

    package com.hhxy.chessman;/*** @author ghp* @date 2022/10/19* @title* @description*/
    public class BlackChessman extends Chessman{public BlackChessman() {this.color = "黑色";}/*** 获取棋子的颜色*/@Overridepublic String getColor() {return this.color;}}
    

    2)白子:

    package com.hhxy.chessman;/*** @author ghp* @date 2022/10/19* @title* @description*/
    public class WhiteChessman extends Chessman{public WhiteChessman() {this.color = "白色";}/*** 获取棋子的颜色*/@Overridepublic String getColor() {return this.color;}}
    
  • Step3:创建非共享享元类

    package com.hhxy.chessman;/*** @author ghp* @date 2022/10/19* @title 坐标类* @description 用于标识围棋的外部状态*/
    public class Coordinates {private int x;//围棋棋子的横坐标private int y;//围棋棋子的纵坐标public Coordinates(int x, int y) {this.x = x;this.y = y;}public int getX() {return x;}public void setX(int x) {this.x = x;}public int getY() {return y;}public void setY(int y) {this.y = y;}
    }
    
  • Step4:创建享元工厂

    package com.hhxy.factory;import com.hhxy.chessman.BlackChessman;
    import com.hhxy.chessman.Chessman;
    import com.hhxy.chessman.WhiteChessman;import java.util.HashMap;
    import java.util.Map;/*** @author ghp* @date 2022/10/19* @title 棋子工厂类* @description 内部含有一个享元池,是享元模式的核心,用于批量创建同类型且具有共同属性的对象*/
    public class ChessmanFactory {private static ChessmanFactory instance;private static Map<String,Object> map;//享元池private ChessmanFactory(){//提前将棋子对象存入享元池中map = new HashMap<>();WhiteChessman whiteChessman = new WhiteChessman();BlackChessman blackChessman = new BlackChessman();map.put("whiteChessman",whiteChessman);map.put("blackChessman",blackChessman);}//使用单例模式创建享元工厂对象,提高效率的同时也能节约内存static{instance = new ChessmanFactory();}/*** 获取单例的工厂对象*/public static ChessmanFactory getInstance() {return instance;}public Chessman getChessman(String chessmanName){return (Chessman) map.get(chessmanName);}
    }
    
  • Step5:编写测试类

    package com.hhxy.test;import com.hhxy.chessman.Chessman;
    import com.hhxy.chessman.Coordinates;
    import com.hhxy.factory.ChessmanFactory;/*** @author ghp* @date 2022/10/19* @title 测试类* @description 用于测试享元模式*/
    public class Test {public static void main(String[] args) {Chessman[] chessmen = new Chessman[4];//获取棋子的工厂对象ChessmanFactory chessmanFactory = ChessmanFactory.getInstance();//从享元池中获取棋子对象chessmen[0] = chessmanFactory.getChessman("whiteChessman");chessmen[1] = chessmanFactory.getChessman("whiteChessman");chessmen[2] = chessmanFactory.getChessman("blackChessman");chessmen[3] = chessmanFactory.getChessman("blackChessman");//判断创建的对象是否是同一个,输出ture则证明是同一个对象System.out.println("判断两个黑棋是否相同:" + (chessmen[0].hashCode() == chessmen[1].hashCode()));System.out.println("判断两个白棋是否相同:" + (chessmen[2].hashCode() == chessmen[3].hashCode()));//展示每个棋子的颜色(棋子的内部状态)和坐标(棋子的外部状态)System.out.print("chessmen[0] ==> ");chessmen[0].showColor();chessmen[0].showCoordinates(new Coordinates(1,2));System.out.println();System.out.print("chessmen[1] ==> ");chessmen[1].showColor();chessmen[1].showCoordinates(new Coordinates(3,4));System.out.println();System.out.print("chessmen[2] ==> ");chessmen[2].showColor();chessmen[2].showCoordinates(new Coordinates(5,6));System.out.println();System.out.print("chessmen[3] ==> ");chessmen[3].showColor();chessmen[3].showCoordinates(new Coordinates(7,8));System.out.println();}
    }
    

    测试结果:

    image-20221116202512335
    csdn

🌲总结

  • 享元模式通过享元池能够进行对象的复用,很大程度的节约了系统的内存,但是享元对象必须是较小粒度,否则会占用较大内存,同时需要注意外部状态和内部状态的划分。关于Flyweight模式,一言以蔽之就是“通过尽量共享实例来避免new出实例

  • 相关设计模式

    • Singleton 模式:在FlyweightFactory角色中有时会使用Singleton模式。此外,如果使用了Singleton模式,由于只会生成一个Singleton角色,因此所有使用该实例的地方都共享同一个实例。在Singleton角色的实例中只持有intrinsic信息

    • Facatory 模式:享元模式一般都需要搭配工厂模式一起实现,才能最大程度减少内存的消耗(它们是天造地设的一对)

    • Composite 模式:有时可以使用Flyweight模式共享Composite模式中的Leaf角色

    • Proxy 模式:如果生成实例的处理需要花费较长时间,那么使用Flyweight模式可以提高程序的处理速度而Proxy模式则是通过设置代理提高程序的处理速度

自此,文章就结束了,如果觉得本文对你有一丢丢帮助的话😄,欢迎点赞👍+评论✍,您的支持将是我写出更加优秀文章的动力O(∩_∩)O

拜拜


上一篇:每日一个设计模式之【外观模式】

下一篇:每人一个设计模式之【代理模式】

参考文章

  • 图解设计模式
  • 菜鸟教程

在次致谢


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部