文章目录

  • 引子
  • 享元模式
  • 总结
    • 优点
    • 缺点
    • 享元模式和单例模式的不同

引子

小帅就职于一家游戏公司,参与开发一款RPG游戏,他负责设计游戏里的怪物。有一个场景是玩家冲进了怪物的老巢,里面有成百上千的飞龙,成群的飞龙向玩家冲过来,这时候游戏画面就会出现卡顿,体验非常不好。领导让小帅优化一下这个场景,让游戏体验变的丝滑如水。

原来的代码如下:

怪物类:


/*** 怪物类*/
public class Monster {public static enum Color {RED, BLACK;}/*** 名称*/private String name;/*** 技能*/private String skill;/*** 颜色*/private Color color;/*** 其他数据*/private String otherData;/*** x坐标*/private int x;/*** y坐标*/private int y;public Monster(String name, String skill, Color color, String otherData, int x, int y) {this.name = name;this.skill = skill;this.color = color;this.otherData = otherData;this.x = x;this.y = y;}@Overridepublic String toString() {return "怪物名称:" + name + ",技能:" + skill + ",颜色:" + color + ", 其他数据:" + otherData + ",x坐标:" + x + ",y坐标:" + y;}
}

游戏场景类:

/*** 游戏场景*/
public class GameScene {public static void main(String[] args) {List<Monster> monsterList = new ArrayList<Monster>();Random rand = new Random();for(int i = 0; i < 1000; i++) {monsterList.add(new Monster("飞龙","空袭", Monster.Color.BLACK, "其他数据", rand.nextInt(1000), rand.nextInt(1000)));}for(Monster monster : monsterList) {System.out.println(monster);}}
}

一部分输出:

怪物名称:飞龙,技能:空袭,颜色:RED, 其他数据:其他数据,x坐标:578,y坐标:636
怪物名称:飞龙,技能:空袭,颜色:RED, 其他数据:其他数据,x坐标:40,y坐标:44
怪物名称:飞龙,技能:空袭,颜色:BLACK, 其他数据:其他数据,x坐标:309,y坐标:250
怪物名称:飞龙,技能:空袭,颜色:RED, 其他数据:其他数据,x坐标:301,y坐标:405
怪物名称:飞龙,技能:空袭,颜色:BLACK, 其他数据:其他数据,x坐标:945,y坐标:35
怪物名称:飞龙,技能:空袭,颜色:BLACK, 其他数据:其他数据,x坐标:192,y坐标:625
怪物名称:飞龙,技能:空袭,颜色:RED, 其他数据:其他数据,x坐标:136,y坐标:852
怪物名称:飞龙,技能:空袭,颜色:RED, 其他数据:其他数据,x坐标:158,y坐标:853
怪物名称:飞龙,技能:空袭,颜色:BLACK, 其他数据:其他数据,x坐标:290,y坐标:29
怪物名称:飞龙,技能:空袭,颜色:BLACK, 其他数据:其他数据,x坐标:454,y坐标:76
怪物名称:飞龙,技能:空袭,颜色:RED, 其他数据:其他数据,x坐标:901,y坐标:910

小帅发现是飞龙太多,占用的内存太大,导致卡顿,有没有一种节省内存的方法呢?

仔细观察一下飞龙对象,飞龙只有两种,一种是红色,另一种是黑色,其他的属性都是一样的,还有就是它们在地图上的坐标不同。我们能不能复用相同的飞龙数据,只是把不同位置飞龙的坐标独立开来呢?

刚好有个设计模式就是解决这个问题的。

享元模式

享元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度对象的复用。 系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,它是一种对象结构型模式。

(图片来源:https://design-patterns.readthedocs.io/zh_CN/latest/structural_patterns/flyweight.html)

  • Flyweight:抽象享元类
  • ConcreteFlyweight:共享具体享元类,ConcreteFlyweight对象必须是可共享的,它所存储的状态必须是内部的,不可变的。
  • UnsharedConcreteFlyweight: 非共享具体享元类
  • FlyweightFactory:享元工厂类,当用户请求一个flyweight对象时,FlyweightFactory对象提供一个实例,如果实例已经创建过就直接拿来用,如果没创建过,就新建一个实例。
  • Client:维持一个对flyweight对象的引用;计算或存储一个(多个)flyweight的外部状态。

由于飞龙都长一样,成群的飞龙冲过来,只是它们在地图上的位置不同而已。享元模式的要点是找出对象中不变的部分(飞龙单位)和变化的部分(坐标),把它们区分开来,复用不变的部分,达到节省内存的目的。

不可变对象,我们就可以利用享元模式将对象设计成享元(飞龙单位),在内存中只保留一份实例,然后再和坐标组合起来就是一只活蹦乱跳的飞龙啦。

重构后的代码如下:

怪物单位类:

*** 怪物单位*/
public class MonsterUnit {protected static enum Color {RED, BLACK;}/*** 名称*/protected String name;/*** 技能*/protected String skill;/*** 颜色*/protected Color color;/*** 其他数据*/protected String otherData;public MonsterUnit(String name, String skill, Color color, String otherData) {this.name = name;this.skill = skill;this.color = color;this.otherData = otherData;}
}

怪物单位工厂类:

/*** 怪物单位工厂*/
public class MonsterUnitFactory {private static final Map<String, MonsterUnit> monsters = new HashMap<>();/*** 获取怪物单位* @param name* @param skill* @param color* @param otherData* @return*/public static MonsterUnit getMonsterUnit(String name, String skill, MonsterUnit.Color color, String otherData) {// 用怪物的名称和颜色作为map的key,每种颜色的怪物复用一个对象MonsterUnit monsterUnit = monsters.get(name + color);if(monsterUnit == null) {monsterUnit = new MonsterUnit(name, skill, color, otherData);monsters.put(name + color, monsterUnit);}return monsterUnit;}
}

怪物类:

/*** 怪物*/
public class Monster {/*** 怪物单位*/private MonsterUnit monsterUnit;/*** x坐标*/private int x;/*** y坐标*/private int y;public Monster(MonsterUnit monsterUnit, int x, int y) {this.monsterUnit = monsterUnit;this.x = x;this.y = y;}@Overridepublic String toString() {return "怪物名称:" + monsterUnit.name + ",技能:" + monsterUnit.skill + ",颜色:" + monsterUnit.color + ", 其他数据:" + monsterUnit.otherData + ",x坐标:" + x + ",y坐标:" + y;}}

游戏场景类:

/*** 游戏场景*/
public class GameScene {public static void main(String[] args) {List<Monster> monsterList = new ArrayList<Monster>();Random rand = new Random();// 怪物Monster monster;// 红色怪物单位MonsterUnit redMonsterUnit;// 黑色怪物单位MonsterUnit blackMonsterUnit;// 获取红色怪物单位redMonsterUnit = MonsterUnitFactory.getMonsterUnit("飞龙","空袭", MonsterUnit.Color.RED , "其他数据");// 获取黑色怪物单位blackMonsterUnit = MonsterUnitFactory.getMonsterUnit("飞龙","空袭", MonsterUnit.Color.BLACK, "其他数据");for(int i = 0; i < 1000; i++) {// 怪物单位加上坐标生产怪物对象monster = new Monster(rand.nextInt() > 0 ? redMonsterUnit : blackMonsterUnit, rand.nextInt(1000), rand.nextInt(1000));monsterList.add(monster);}for(Monster item : monsterList) {System.out.println(item);}}
}

输出:

怪物名称:飞龙,技能:空袭,颜色:BLACK, 其他数据:其他数据,x坐标:874,y坐标:57
怪物名称:飞龙,技能:空袭,颜色:BLACK, 其他数据:其他数据,x坐标:754,y坐标:692
怪物名称:飞龙,技能:空袭,颜色:BLACK, 其他数据:其他数据,x坐标:396,y坐标:535
怪物名称:飞龙,技能:空袭,颜色:RED, 其他数据:其他数据,x坐标:653,y坐标:281
怪物名称:飞龙,技能:空袭,颜色:RED, 其他数据:其他数据,x坐标:649,y坐标:995
怪物名称:飞龙,技能:空袭,颜色:BLACK, 其他数据:其他数据,x坐标:195,y坐标:94
怪物名称:飞龙,技能:空袭,颜色:RED, 其他数据:其他数据,x坐标:678,y坐标:921
怪物名称:飞龙,技能:空袭,颜色:RED, 其他数据:其他数据,x坐标:571,y坐标:743
怪物名称:飞龙,技能:空袭,颜色:RED, 其他数据:其他数据,x坐标:631,y坐标:271
怪物名称:飞龙,技能:空袭,颜色:BLACK, 其他数据:其他数据,x坐标:794,y坐标:634
怪物名称:飞龙,技能:空袭,颜色:BLACK, 其他数据:其他数据,x坐标:492,y坐标:973
怪物名称:飞龙,技能:空袭,颜色:BLACK, 其他数据:其他数据,x坐标:485,y坐标:46

这里我们只是创建了两个飞龙单位,一个红色的飞龙和一个黑色的飞龙,把它们放到Map里,通过工厂类获取。

Monster(怪物类)复用了MonsterUnit(怪物单位),这里只是对MonsterUnit对象的引用,是不变的部分,这种不可变的状态叫做内部状态;而坐标是动态变化的,每个对象都不一样,这种可变的状态叫做外部状态

应用享元模式的要点是要把类的成员变量拆分为两个部分:

  • 内部状态: 包含不变的、 可在许多对象中重复使用的成员变量。
  • 外部状态: 包含每个对象各自不同的场景的成员变量。

内部状态存储于ConcreteFlyweight对象中,而外部状态则由Client对象存储或计算。当用户调用flyweight对象的操作时,要将外部状态传递给它。


如果外部状态可以计算出来,不用传递进来,那么就可以做到用时间换空间,让节省的空间最大化。

总结

所谓“享元”,顾名思义就是被共享的单元。享元模式的意图是复用对象,节省内存,前提是享元对象是不可变对象。具体来讲,当一个系统中存在大量重复对象的时候,我们就可以利用享元模式,将对象设计成享元,在内存中只保留一份实例,供多处代码引用。这样可以减少内存中对象的数量,以起到节省内存的目的。

享元模式的代码实现非常简单,主要是通过工厂模式,在工厂类中,通过一个Map 或者List 来缓存已经创建好的享元对象,以达到复用的目的。

优点

  • 如果程序中有很多相似对象, 那么你将可以节省大量内存。

缺点

  • 代码会变得更加复杂,需要拆分内部状态和外部状态。

享元模式和单例模式的不同

  • 在享元模式中,一个类可以创建多个对象;而在单例模式中,一个类只能创建一个对象。
  • 享元模式的意图是为了复用对象,节省内存;单例模式的意图是为了控制对象的个数。

享元模式--大量的飞龙相关推荐

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

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

  2. 【设计模式】享元模式

    上周代码评审,看到同事使用了"享元模式".想起自己也不懂,着手学习之. 参考的优秀的文章: Java的享元模式 这篇博文为学习之理解.感悟,如理解不真确,请慷慨指出. 本文只讨论单 ...

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

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

  4. 设计模式之享元模式、例子分析

    1. 定义 享元模式(Flyweight):运用共享技术有效地支持大量细粒度的对象 按我的理解来说,享元,就是共享单元,重复运用,这个模式的核心是复用代码,按不同需要,创建并共用一个实例(不同需要是共 ...

  5. 13Flyweight(享元)模式

    技术交流QQ群:1027579432,欢迎你的加入! 1.Flyweight(享元)模式动机 在软件系统采用纯粹对象方案的问题在于大量细粒度的对象会很快充斥在系统中,从而带来很高的运行时代价--主要指 ...

  6. 享元模式 Flyweight Pattern

    享元模式的定义为:采用一个共享来避免大量拥有相同内容对象的开销. 这种开销中最常见.直观的就是内存的损耗.享元模式以共享的方式高效地支持大量的细粒度对象. 在名字和定义中都体现了共享这个核心概念,那么 ...

  7. JAVA 设计模式 享元模式

    用途 享元模式 (Flyweight) 运用共享技术有效地支持大量细粒度的对象. 享元模式是一种结构型模式. 结构 图-享元模式结构图 Flyweight : 它是所有具体享元类的超类或接口,通过这个 ...

  8. 设计模式(享元模式)

    享元模式可以理解是一个对象的缓存,通过构建一个享元工厂对所有的对象进行管理. 代码如下: Flyweight 享元的接口 public interface Flyweight {public void ...

  9. Flyweight 享元模式

    public class Font //12+8 bytes 8用于垃圾手机{string fontName; //4 bytesint size; //4 bytes Color color; // ...

最新文章

  1. (转载)netstat -r查看路由表时Flags的含义
  2. 安卓双摄像头录像_android开发之调用手机的摄像头使用MediaRecorder录像并播放_Android_脚本之家...
  3. android手机存储大小设置在哪里看,Android 最新获取手机内置存储大小,SD卡存储空间大小方法...
  4. 第2课第3节_Java面向对象编程_继承性_P【学习笔记】
  5. ng-content的一个实际例子
  6. ASP.NET Core文件上传、下载与删除
  7. dart 怎么判断function的返回值为空_R 学习-第二章第二节- 尝试写个Function,超级菜鸟教程...
  8. 有没有跟新型肺炎确诊患者同乘车?百度可查!
  9. 外呼机器人起名_智能外呼机器人十大厂商
  10. AIoT 又迎一利器,涂鸦云开发平台来了!
  11. different intergers
  12. IO流总结-知识体系
  13. Gnu Screen用法 【转】
  14. 神经元模型图手工制作,神经元模型图手工模型
  15. ubuntu安装ROS运行rosdep init错误,cannot download default sources list from....
  16. VMware网络配置基础
  17. pdf添加水印的方法,pdf加水印步骤
  18. 比大衣保暖,比羽绒服显瘦!这件高级反貂绒毛衣火了!不起球不掉毛,明明毛茸茸的却巨显瘦,舒适又保暖....YYDS!...
  19. 2022年为什么降薪也要跳槽?机会比涨薪很重要?
  20. 灵雀云ACP成功通过金融信创生态实验室适配验证

热门文章

  1. 运动型蓝牙耳机怎么佩戴的,运动蓝牙耳机的不同款式盘点
  2. 一个解决MacBook Pro 突然连接无线不能上网 方案
  3. 【入门PLC】02、博途TIA Portal V16的HMI简单操作学习(人机交互界面)
  4. 爬取起点中文网的小说
  5. 史玉柱论民营企业的“13种死法”
  6. MATLAB筛选数据
  7. cocos creator 如何制作九宫格抽奖
  8. 深度剖析数据在内存中的存储(修炼内功~吊打面试官)
  9. cf 678E - Another Sith Tournament 【状压dp】
  10. 双碳时代,数据中心供配电的“智”与“能”