运用共享技术有效地支持大量细粒度对象.
享元:把内部状态共享出来
重点在于分离变与不变。
把一个对象的状态分为内部状态和外部状态,内部状态不变,外部状态可变。
内部状态就是在对象里通过内部方法管理,而外部信息可以在通过外部删除或者保存。
享元模式所涉及的角色有抽象享元角色、具体(单纯)享元角色、复合享元角色、享员工厂角色,以及客户端角色等。
抽象享元角色(Flyweight):此角色是所有的具体享元类的超类,为这些类规定出需要实现的公共接口或抽象类。那些需要外蕴状态(External State)的操作可以通过方法的参数传入。抽象享元的接口使得享元变得可能,但是并不强制子类实行共享,因此并非所有的享元对象都是可以共享的。
具体享元(ConcreteFlyweight)角色:实现抽象享元角色所规定的接口。如果有内蕴状态的话,必须负责为内蕴状态提供存储空间。享元对象的内蕴状态必须与对象所处的周围环境无关,从而使得享元对象可以在系统内共享。有时候具体享元角色又叫做单纯具体享元角色,因为复合享元角色是由单纯具体享元角色通过复合而成的。
复合享元(UnsharableFlyweight)角色:复合享元角色所代表的对象是不可以共享的,但是一个复合享元对象可以分解成为多个本身是单纯享元对象的组合。复合享元角色又称做不可共享的享元对象。这个角色一般很少使用。
享元工厂(FlyweightFactoiy)角色:本角色负责创建和管理享元角色。本角色必须保证享元对象可以被系统适当地共享。当一个客户端对象请求一个享元对象的时候,享元工厂角色需要检查系统中是否已经有一个符合要求的享元对象,如果已经有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有一个适当的享元对象的话,享元工厂角色就应当创建一个新的合适的享元对象。
客户端(Client)角色:本角色还需要自行存储所有享元对象的外蕴状态。
package demo19.flyweight.example2; /*** * 享元接口,通过这个接口享元可以接受并作用于外部状态 */ public interface Flyweight { /** * 示例操作,传入外部状态 * * @param extrinsicState * 示例参数,外部状态 */ public void operation(String extrinsicState); } ************************************************************************************************** package demo19.flyweight.example2; /** * 享元对象 */ public class ConcreteFlyweight implements Flyweight { /** * 示例,描述内部状态 */ private String intrinsicState; /** * 构造方法,传入享元对象的内部状态的数据 * * @param state * 享元对象的内部状态的数据 */ public ConcreteFlyweight(String state) { this.intrinsicState = state; } public void operation(String extrinsicState) { // 具体的功能处理,可能会用到享元内部、外部的状态 } } ************************************************************************************************** package demo19.flyweight.example2; /** * 不需要共享的Flyweight对象, 通常是将被共享的享元对象作为子节点,组合出来的对象 */ public class UnsharedConcreteFlyweight implements Flyweight { /** * 示例,描述对象的状态 */ private String allState; public void operation(String extrinsicState) { // 具体的功能处理 } } ************************************************************************************************** package demo19.flyweight.example2; import java.util.HashMap; import java.util.Map; /** * 享元工厂 */ public class FlyweightFactory { /** * 缓存多个flyweight对象,这里只是示意一下 */ private Map<String, Flyweight> fsMap = new HashMap<String, Flyweight>(); /** * 获取key对应的享元对象 * * @param key * 获取享元对象的key,只是示意 * @return key对应的享元对象 */ public Flyweight getFlyweight(String key) { // 这个方法里面基本的实现步骤如下: // 1:先从缓存里面查找,是否存在key对应的Flyweight对象 Flyweight f = fsMap.get(key); // 2:如果存在,就返回相对应的Flyweight对象 if (f == null) { // 3:如果不存在 // 3.1:创建一个新的Flyweight对象 f = new ConcreteFlyweight(key); // 3.2:把这个新的Flyweight对象添加到缓存里面 fsMap.put(key, f); // 3.3:然后返回这个新的Flyweight对象 } return f; } } ************************************************************************************************** package demo19.flyweight.example2; /** * Client对象,通常会维持一个对flyweight的引用, 计算或存储一个或多个flyweight的外部状态 */ public class Client { // 具体的功能处理 }
分离与共享
优点:减少对象数量,节省内存空间
缺点:维护共享对象,需要额外开销
1.7.1如果一个程序大量使用了细粒度对象
1.7.2如果对象的大多数状态都可以变为外部状态(比如可以通过计算得到),或是从外部传入等,可以使用享元模式进行内部状态和外部状态的分离。
比如围棋有300颗棋子,用一般的设计模式,创建一个类,每个棋子都用一个对象的话,那就会非常麻烦,并且各自定义各自在棋盘的位置.....等等,而使用 亨元模式 来实现的话,就用两个对象 :一个黑,一个白。这样就可以了,至于棋子的方位不同,那只是对象的不同的外部表现形式或者说是外部状态。这样三百多个对象就减到了两个对象。享元模式以共享的方式高效地支持大量的细粒度对象,说的再具体一些是将所有具有相同状态的对象指向同一个引用,从而解决了系统在创建大量对象时所带来的内存压力。
示例代码如下
package demo19.flyweight.example1; /** * 抽象棋子的接口 * * @author 王玉超 * @date 2013-7-31 */ public interface IChessFlyWeight { public String getColor(); public void show(Point point); }
package demo19.flyweight.example1; /** * 具体棋子对象的实现类 * * @author 王玉超 * @date 2013-7-31 */ public class ConcreteChessFlyWeight implements IChessFlyWeight { private String color; public String getColor() { return color; } public ConcreteChessFlyWeight(String color) { this.color = color; } public void show(Point point) { System.out.println("This Chess is " + color + ",Point X:" + point.getX() + " Y:" + point.getY()); } }
package demo19.flyweight.example1; /** * 棋子外部动态变动的属性 * * @author 王玉超 * @date 2013-7-31 */ public class Point { private int x; private int y; public Point(int x, int y) { this.x = x; this.y = y; } public int getX() { return x; } public int getY() { return y; } @Override public String toString() { return "Point [x=" + x + ", y=" + y + "]"; } }
package demo19.flyweight.example1; import java.util.HashMap; import java.util.Map; /** * 享元模型对象工厂 * * @author 王玉超 * @date 2013-7-31 */ public class ChessFlyWeightFactory { private Map<String, IChessFlyWeight> chessMap = new HashMap<String, IChessFlyWeight>(); public IChessFlyWeight getChess(String key) { if (!chessMap.containsKey(key)) { chessMap.put(key, new ConcreteChessFlyWeight(key)); } return chessMap.get(key); } }
package demo19.flyweight.example1; public class Client { public static void main(String[] args) { ChessFlyWeightFactory factory = new ChessFlyWeightFactory(); Point point; String white = "白"; String black = "黑"; point = new Point(0, 0); factory.getChess(white).show(point); point = new Point(10, 10); factory.getChess(white).show(point); point = new Point(100, 100); factory.getChess(black).show(point); point = new Point(200, 200); factory.getChess(black).show(point); } }
代码仔细对应示例代码以后,发现没有【不需要共享的Flyweight对象】实际上,就是Point类,只不过,Point类没有继承Flyweight接口而已。那个不需要共享的对象一般很少用。