本文共 4464 字,大约阅读时间需要 14 分钟。
7种结构型设计模式:桥接模式,适配器模式,装饰模式,组合模式,享元模式,外观模式,代理模式上篇我们说了桥接模式:通过将实现和抽象放在两个不同的类层次中而使他们可以独立改变,桥接模式适用于那些多种情况排列组合发生的场景这篇说说享元模式,顾名思义就是共享对象的一种模式共享对象给我们带来的直接好处就是降低了内存的开销这个模式并不难,但我们还是要把这个模式涉及到的内容都罗列出来
1)享元模式的定义:
享元模式:以共享的方式高效的支持大量细粒度对象的重用
2)享元模式的UML图:
3)享元模式的角色:
FlyWeight抽象享元类: 接口或抽象类,声明公共方法,可向外界提供对象的内部状态,设置外部状态。ConcreteFlyWeight具体享元类: 为内部状态提供成员变量进行存储。UnsharedConcreteFlyWeight非共享享元类: 不能被共享的子类可以设计为非共享享元类。FlyWeightFactory享元工厂类: 创建并管理享元对象,享元池一般设计为键值对。
4)内部状态和外部状态
享元对象能做到共享的关键是区分了内部状态和外部状态。 内部状态: 可共享,不会随环境改变而改变 存储在享元对象内部,构造时通过setter设置外部状态: 不可共享,会随环境改变而改变 需要使用时通过客户端传入享元对象且由客户端保存
1,场景选择和分析
基于享元模式的特点我们选择连连看游戏作为模式场景连连看游戏有以下几适合享元模式的特征: 1,内部状态:连连看游戏包含大量重复的图片内容 2,外部状态:重复图片中的差异点在于所在位置的不同 3,大量重复图片加大了内存的开销,适合使用享元模式
为了讲解模式我们简化了连连看游戏的场景: 1,限制了只有一排图片,单排随机生成10张图片 2,客户端保存图片的位置信息(即外部状态)和对象的对应关系 3,所有图片对象取自享元工厂 4,随机选择两张图片进行比较是否可以消除
2,Demo源码
1)创建一个图片接口,包含获取图片类型(内部状态)和获取图片位置(外部状态)的方法
package com.brave.flyweight;public interface ImageNode { // 获取图片类型-内部状态 void getImageType(); // 获取图片位置-外部状态 void getImageCoordinate(int coordinate);}
2,实现图片接口创建图片的具体享元类
package com.brave.flyweight;public class CImageNode implements ImageNode { private String imageType; // 图片类型 public CImageNode(String imageType) { this.imageType = imageType; } @Override public void getImageType() { System.out.println("图片类型为 : " + imageType); } @Override public void getImageCoordinate(int coordinate) { System.out.println("图片位置为 : " + coordinate); }}
3,创建享元工厂类,管理享元对象的实例集合
这部分我们就直接将实例初始化到工厂集合中,不再去做懒加载了
package com.brave.flyweight;import java.util.Hashtable;/** * 享元工厂类 * 初始化享元工厂,将享元对象装入享元工厂 * @author Brave * */public class ImageNodeFactory { private HashtableimageNodes = new Hashtable (); public ImageNodeFactory(){ imageNodes.put("图片1", new CImageNode("图片1")); imageNodes.put("图片2", new CImageNode("图片2")); imageNodes.put("图片3", new CImageNode("图片3")); imageNodes.put("图片4", new CImageNode("图片4")); } // 获取图片对象 public ImageNode GetImage(String imageType){ return imageNodes.get(imageType); }}
4)客户端:
package com.brave.flyweight;import java.util.Hashtable;public class Client { public static void main(String[] args) { // 初始化享元工厂 ImageNodeFactory imageNodeFactory = new ImageNodeFactory(); // Hashtable保存游戏初始化后的图片位置和对象信息 HashtableimageList = new Hashtable (); // 随机初始化游戏数据1-10的随机数 int randomNumber = 0; for(int i=0; i<10; i++){ randomNumber = (int)(Math.random() * 10); int a = randomNumber/2;//除法 switch(a){ case 0: imageList.put(i, imageNodeFactory.GetImage("图片1")); break; case 1: imageList.put(i, imageNodeFactory.GetImage("图片2")); break; case 2: imageList.put(i, imageNodeFactory.GetImage("图片3")); break; case 3: imageList.put(i, imageNodeFactory.GetImage("图片4")); break; default: imageList.put(i, imageNodeFactory.GetImage("图片1")); } } System.out.println("随机初始化游戏数据完成,开始打印游戏数据"); // 打印游戏数据 for(int i=0; i<10; i++){ // 外部状态 imageList.get(i).getImageCoordinate(i); // 内部状态 imageList.get(i).getImageType(); } // 随机选取两个不同坐标的图进行比较 int A = 0; int B = 0; while(A==B){ A = (int)(Math.random() * 10); B = (int)(Math.random() * 10); } // 判断是否是相同对象 if(imageList.get(A) == imageList.get(B)){ System.out.println("坐标"+A+"和坐标"+B+"的图片相同,可以消除"); }else{ System.out.println("坐标"+A+"和坐标"+B+"的图片不相同,不可以消除"); } }}
5)打印测试日志:
随机初始化游戏数据完成,开始打印游戏数据图片位置为 : 0图片类型为 : 图片2图片位置为 : 1图片类型为 : 图片1图片位置为 : 2图片类型为 : 图片1图片位置为 : 3图片类型为 : 图片1图片位置为 : 4图片类型为 : 图片3图片位置为 : 5图片类型为 : 图片3图片位置为 : 6图片类型为 : 图片1图片位置为 : 7图片类型为 : 图片1图片位置为 : 8图片类型为 : 图片1图片位置为 : 9图片类型为 : 图片1坐标8和坐标9的图片相同,可以消除
优点: 由于实现了对象的共享所以极大的减少内存中对象的数量 相同或相似的对象内存只保持一份,极大的节约资源,提高系统性能 外部状态相对独立,不影响内部变化缺点: 共享对象节省内存,共享内部状态,分离了外部状态 这使得程序的逻辑复杂化,同时也增加了状态维护成本 每次需要对外部的状态进行读取,牺牲了运行时间 很显然,这是一种用时间换空间的做法
JAVA中的String使用了常量池,也就是享元模式,当两个String变量的值都为”abc”时实际使用的是同一个引用
这里我们先标记一下,日后有时间会补充一个了例子