设计模式(十二) 享元模式
2016-07-22 14:52
375 查看
Flyweight Pattern
享元模式以共享的方式高效地支持大量细粒度对象的重用,享元对象能做到共享的关键是区分了内部状态(Intrinsic State)和外部状态(Extrinsic State)。
内部状态是存储在享元对象内部并且不会随环境改变而改变的状态,内部状态可以共享。
外部状态是随环境改变而改变的、不可以共享的状态。
正因为区分了内部状态和外部状态,我们可以将具有相同内部状态的对象存储在享元池中,享元池中的对象是可以实现共享的,需要的时候就将对象从享元池中取出,实现对象的复用。通过向取出的对象注入不同的外部状态,可以得到一系列相似的对象,而这些对象在内存中实际上只存储一份。
![](https://img-blog.csdn.net/20160722143525122)
Flyweight(抽象享元类):通常是一个接口或抽象类,在抽象享元类中声明了具体享元类公共的方法,这些方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法来设置外部数据(外部状态)。
ConcreteFlyweight(具体享元类):它实现了抽象享元类,其实例称为享元对象;在具体享元类中为内部状态提供了存储空间。通常我们可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象。
UnsharedConcreteFlyweight(非共享具体享元类):并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可设计为非共享具体享元类;当需要一个非共享具体享元类的对象时可以直接通过实例化创建。
FlyweightFactory(享元工厂类):享元工厂类用于创建并管理享元对象,它针对抽象享元类编程,将各种类型的具体享元对象存储在一个享元池中,享元池一般设计为一个存储“键值对”的集合(也可以是其他类型的集合),可以结合工厂模式进行设计。
当用户请求一个具体享元对象时,享元工厂提供一个存储在享元池中已创建的实例或者创建一个新的实例(如果不存在的话),返回新创建的实例并将其存储在享元池中。
带有外部状态的享元模式:
将外部状态封装后动态的插入共享数据中,以实现外部状态和内部状态的分离。
享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。
打印结果:
1. 什么是享元模式
享元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,它是一种对象结构型模式。享元模式以共享的方式高效地支持大量细粒度对象的重用,享元对象能做到共享的关键是区分了内部状态(Intrinsic State)和外部状态(Extrinsic State)。
内部状态是存储在享元对象内部并且不会随环境改变而改变的状态,内部状态可以共享。
外部状态是随环境改变而改变的、不可以共享的状态。
正因为区分了内部状态和外部状态,我们可以将具有相同内部状态的对象存储在享元池中,享元池中的对象是可以实现共享的,需要的时候就将对象从享元池中取出,实现对象的复用。通过向取出的对象注入不同的外部状态,可以得到一系列相似的对象,而这些对象在内存中实际上只存储一份。
2. 享元模式类角色解析
享元模式结构图:Flyweight(抽象享元类):通常是一个接口或抽象类,在抽象享元类中声明了具体享元类公共的方法,这些方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法来设置外部数据(外部状态)。
ConcreteFlyweight(具体享元类):它实现了抽象享元类,其实例称为享元对象;在具体享元类中为内部状态提供了存储空间。通常我们可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象。
UnsharedConcreteFlyweight(非共享具体享元类):并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可设计为非共享具体享元类;当需要一个非共享具体享元类的对象时可以直接通过实例化创建。
FlyweightFactory(享元工厂类):享元工厂类用于创建并管理享元对象,它针对抽象享元类编程,将各种类型的具体享元对象存储在一个享元池中,享元池一般设计为一个存储“键值对”的集合(也可以是其他类型的集合),可以结合工厂模式进行设计。
当用户请求一个具体享元对象时,享元工厂提供一个存储在享元池中已创建的实例或者创建一个新的实例(如果不存在的话),返回新创建的实例并将其存储在享元池中。
带有外部状态的享元模式:
将外部状态封装后动态的插入共享数据中,以实现外部状态和内部状态的分离。
3. 享元模式好处
可以极大减少内存中对象的数量,使得相同或相似对象在内存中只保存一份,从而可以节约系统资源,提高系统性能。享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。
4. 实操感想
享元模式难在对于不变的内部状态的分离,想清楚了这一点直接插入外部状态就能很方便的使用享元模式进行设计。5. 代码示例
一个多功能文档编辑器,在文本文档中可以插入图片、动画、视频等多媒体资料,为了节约系统资源,相同的图片、动画和视频在同一个文档中只需保存一份,但是可以多次重复出现,而且它们每次出现时位置和大小均可不同。//抽象享元类 public abstract class Media { //共有属性,每个媒体的资料的唯一ID protected String name; public Media(String name) { this.name = name; } //插入外部状态 public abstract void show(Coordinates coordinates); } //具体享元类 public class MediaImage extends Media { public MediaImage(String name) { super(name); } @Override public void show(Coordinates coordinates) { System.out.println(name + "Image show " + coordinates.toString()); } } //具体享元类 public class MediaVideo extends Media { public MediaVideo(String name) { super(name); } @Override public void show(Coordinates coordinates) { System.out.println(name + "Video show " + coordinates.toString()); } } //享元工厂类,使用单例实现。 public enum MediaFactory { INSTANCE; private HashMap<MediaType, HashMap<String, Media>> hashMap; MediaFactory() { hashMap = new HashMap<>(); } public Media getMedia(MediaType mediaType, String name) { HashMap<String, Media> stringMediaHashMap = hashMap.get(mediaType); if (stringMediaHashMap == null) { //创建一层放入 stringMediaHashMap = new HashMap<>(); hashMap.put(mediaType, stringMediaHashMap); } Media media = stringMediaHashMap.get(name); if (media == null) { switch (mediaType) { case Image: media = new MediaImage(name); break; case Video: media = new MediaVideo(name); break; case Animation: media = new MediaAnimation(name); break; } stringMediaHashMap.put(name, media); } return media; } } //媒体类型 public enum MediaType { Image, Video; } //测试类 public class ZZMainClass { public static void main(String[] args) { Media one = MediaFactory.INSTANCE.getMedia(MediaType.Video, "One"); Media one1 = MediaFactory.INSTANCE.getMedia(MediaType.Video, "One"); System.out.println(one == one1); Media imageOne = MediaFactory.INSTANCE.getMedia(MediaType.Image, "one"); Media imageTwo = MediaFactory.INSTANCE.getMedia(MediaType.Image, "two"); System.out.println(imageOne == imageTwo); one.show(new Coordinates(1, 1, 100, 100)); imageOne.show(new Coordinates(2, 2, 200, 300)); imageTwo.show(new Coordinates(3, 3, 3, 3)); } }
打印结果:
true OneAnimation show {x=1, y=1, width=100, height=100} OneAnimation show {x=2, y=2, width=100, height=100} false oneImage show {x=2, y=2, width=200, height=300} twoImage show {x=3, y=3, width=3, height=3}
相关文章推荐
- BEANUTIL 对象转JSON
- Java继承类的初始化先后顺序
- 逆序数
- bootstrap 响应式布局
- 程序员的自我修养-----Java开发的必须知道的几个注意点
- 程序员的自我修养-----Java开发的必须知道的几个注意点
- POJ 1321-棋盘问题-简单搜索DFS
- python-re使用举例
- BEANUTIL 对象转JSON
- HTML5基础学习笔记(十五)
- 精通 Oracle+Python,第 9 部分:Jython 和 IronPython — 在 Python 中使用 JDBC 和 ODP.NET
- KOA controller 提取公共函数
- PO、VO、BO、DTO、POJO、DAO之间的关系
- 自己生成网络后台接口并利用charles模拟Http请求和响应
- Tomcat能承受的极限压力测试
- C语言算法知识(201607)-小结
- linux 基础学习入门 2
- spring知识总结
- mac Android Studio安装Genymotion
- 如何在 Ubuntu 上搭建网桥