设计模式《享元模式》

引言

  按照以前的惯例,先放出上一节讲的组合模式,这节我们来说说享元模式。

示例地址

  Demo

类图

设计模式《享元模式》_第1张图片
image

定义

  使用共享对象可有效地支持大量的细粒度的对象。

使用场景

  1. 系统中存在大量的相似对象。
  2. 细粒度的对象都具备较接近的外部状态,而且内部状态与环境无关,也就是说对象没有特定身份。
  3. 需要缓冲池的场景。

享元模式的各种角色

  1. Flyweight:享元对象接口
  2. ConcreteFlyweight:具体的享元对象
  3. FlyweightFactory:享元工厂,负责管理享元对象池和创建享元对象

享元模式中的概念

  享元对象能做到共享的关键是区分内部状态(Internal State)和外部状态(External State)。其中:
  1. 内部状态是存储在享元对象内部并且不会随环境改变而改变的状态,因此内部状态可以共享。
  2. 外部状态是随环境改变而改变的、不可以共享的状态。享元对象的外部状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候再传入到享元对象内部。一个外部状态与另一个外部状态之间是相互独立的。

单纯享元模式

  单纯享元模式中,所有的具体享元类都是可以共享的,不存在非共享具体享元类。

1. 享元角色接口
/**
 * 享元角色接口
 *
 * @author [email protected]
 * @created 2018/8/6  下午3:49.
 */
public interface Flyweight {
    //外部状态
    public void operation(String state);
}
2. 享元角色的实现类
/**
 * 享元角色的实现类
 *
 * @author [email protected]
 * @created 2018/8/6  下午3:50.
 */
public class ConcreteFlyweight implements Flyweight {
    private String intrinsicState = null;

    public ConcreteFlyweight(String intrinsicState) {
        this.intrinsicState = intrinsicState;
    }

    @Override
    public void operation(String state) {
        // 内部状态
        System.out.println("Intrinsic State = " + this.intrinsicState);
        // 外部状态
        System.out.println("Extrinsic State = " + state);
    }
}
3. 享元角色工厂
/**
 * 享元角色工厂
 *
 * @author [email protected]
 * @created 2018/8/6  下午3:54.
 */
public class FlyweighttFactory {
    private Map mFlyweightMap = new HashMap<>();

    public Flyweight factory(String state) {
        //先从已有的缓存列表中查询对象是否已存在
        Flyweight flyweight = mFlyweightMap.get(state);
        if (flyweight == null) {
            //如果对象不存在,则重新创建一个新的Flyweight对象
            flyweight = new ConcreteFlyweight(state);
            //将新生成的对象放入缓存列表中
            mFlyweightMap.put(state, flyweight);
        }
        //返回对象
        return flyweight;
    }
}
4. Client
  FlyweighttFactory factory=new FlyweighttFactory();

  Flyweight flyweightA = factory.factory("A");
  flyweightA.operation("First Call , A State");
  System.out.println(flyweightA);

  Flyweight flyweightB = factory.factory("B");
  flyweightA.operation("Second Call , B State");
  System.out.println(flyweightB);


  Flyweight flyweightC = factory.factory("C");
  flyweightA.operation("Third Call , C State");
  System.out.println(flyweightC);

复合享元模式

  将一些单纯享元对象使用组合模式加以组合,还可以形成复合享元对象,这样的复合享元对象本身不能共享,但是它们可以分解成单纯享元对象,而后者则可以共享。
  通过复合享元模式,可以确保复合享元类CompositeConcreteFlyweight中所包含的每个单纯享元类ConcreteFlyweight都具有相同的外部状态,而这些单纯享元的内部状态往往可以不同。如果希望为多个内部状态不同的享元对象设置相同的外部状态,可以考虑使用复合享元模式。

1. 享元角色接口
/**
 * 享元角色接口
 *
 * @author [email protected]
 * @created 2018/8/6  下午3:49.
 */
public interface Flyweight {
    //外部状态
    public void operation(String state);
}
2. 享元角色的实现类
/**
 * 享元角色的实现类
 *
 * @author [email protected]
 * @created 2018/8/6  下午3:50.
 */
public class ConcreteFlyweight implements Flyweight {
    private String intrinsicState = null;

    public ConcreteFlyweight(String intrinsicState) {
        this.intrinsicState = intrinsicState;
    }

    @Override
    public void operation(String state) {
        // 内部状态
        System.out.println("Intrinsic State = " + this.intrinsicState);
        // 外部状态
        System.out.println("Extrinsic State = " + state);
    }
}
3. 复合享元角色类
/**
 * 复合享元角色类
 *
 * @author [email protected]
 * @created 2018/8/6  下午4:54.
 */
public class ConcreteCompositeFlyweight implements Flyweight {

    private Map mStringFlyweightMap = new HashMap<>();


    public void add(String key, Flyweight flyweight) {
        mStringFlyweightMap.put(key, flyweight);
    }

    @Override
    public void operation(String state) {
        Flyweight flyweight = null;
        for (Object o : mStringFlyweightMap.keySet()) {
            flyweight = mStringFlyweightMap.get(o);
            flyweight.operation(state);
            System.out.println(flyweight);
        }

    }
}
4. 享元模式工厂
/**
 * 享元模式工厂
 *
 * @author [email protected]
 * @created 2018/8/6  下午5:34.
 */
public class FlyweightFactory {

    private Map mStringFlyweightMap = new HashMap();

    /**
     * 复合享元工厂方法
     *
     * @param compositeState
     * @return
     */
    public Flyweight factory(List compositeState) {
        ConcreteCompositeFlyweight concreteCompositeFlyweight = new ConcreteCompositeFlyweight();

        for (String state : compositeState) {
            concreteCompositeFlyweight.add(state, this.factory(state));
        }

        return concreteCompositeFlyweight;
    }

    /**
     * 单纯享元工厂方法
     *
     * @param state
     * @return
     */
    public Flyweight factory(String state) {
        //先从已有的缓存列表中查询对象是否已存在
        Flyweight flyweight = mStringFlyweightMap.get(state);
        if (flyweight == null) {
            //如果对象不存在,则重新创建一个新的Flyweight对象
            flyweight = new ConcreteFlyweight(state);
            //将新生成的对象放入缓存列表中
            mStringFlyweightMap.put(state, flyweight);
        }
        //返回对象
        return flyweight;
    }
}
5. Client
  List compositeState = new ArrayList();

  compositeState.add("A");
  compositeState.add("B");
  compositeState.add("C");
  compositeState.add("B");
  compositeState.add("A");

  FlyweightFactory flyweightFactory = new FlyweightFactory();
  Flyweight compositeFly1 = flyweightFactory.factory(compositeState);
  Flyweight compositeFly2 = flyweightFactory.factory(compositeState);

  compositeFly1.operation("Composite1 Call");
  compositeFly2.operation("Composite2 Call");

  System.out.println("---------------------------------------------");
  System.out.println("复合享元模式是否可以共享对象:" + (compositeFly1 == compositeFly2));
  System.out.println(compositeFly1);
  System.out.println(compositeFly2);

享元模式实例应用

  上高中的时候,我们玩个一个游戏,叫做“五子棋”。里面有黑白2种棋子。如果我们按照一般的写法,有100个棋子我们需要创建100个对象,那么10000个呢,内存估计直接OOM了。下面我们使用享元模式来解决问题。

1. 棋子接口
/**
 *  棋子接口
 *
 * @author [email protected]

 * @created 2018/8/7  下午3:55.
 *
 */
public abstract class QiZi {
    public abstract String getColor();

    public void display(Coordinates coordinates) {
        System.out.println("棋子颜色:" + this.getColor() + ",棋子位置:" + coordinates.getX() + "," + coordinates.getY() );
    }
}

2. 黑子
/**
 *  黑色的棋子
 * 
 * @author [email protected]
 
 * @created 2018/8/7  下午3:58.
 * 
 */
public class BlackQiZi extends  QiZi {
    @Override
    public String getColor() {
        return "黑色";
    }
}
3. 白子
/**
 *  白色的棋子
 * 
 * @author [email protected]
 
 * @created 2018/8/7  下午3:58.
 * 
 */
public class WhiteQiZi extends  QiZi{
    @Override
    public String getColor() {
        return "白色";
    }
}
4. 外部状态 坐标
/**
 * 外部状态
 * 
 * @author [email protected]
 
 * @created 2018/8/7  下午4:26.
 * 
 */
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;
    }
}
5. 棋子工厂
/**
 * 生产棋子的工厂
 * 
 * @author [email protected]
 
 * @created 2018/8/7  下午4:02.
 * 
 */
public class QiZiFactory {

    private static QiZiFactory instance = new QiZiFactory();
    private static Hashtable ht; //使用Hashtable来存储享元对象,充当享元池
    private QiZi black,white;

    private QiZiFactory() {
        ht = new Hashtable();
        black = new BlackQiZi();
        ht.put("b",black);
        white = new WhiteQiZi();
        ht.put("w",white);
    }


    //返回享元工厂类的唯一实例
    public static QiZiFactory getInstance() {
        return instance;
    }

    //通过key来获取存储在Hashtable中的享元对象
    public static QiZi getIgoChessman(String color) {
        return (QiZi)ht.get(color);
    }
}
6. Client
 QiZi black1,black2,white1,white2;
 QiZiFactory factory = QiZiFactory.getInstance();

 //通过享元工厂获取三颗黑子
 black1 = factory.getIgoChessman("b");
 black2 = factory.getIgoChessman("b");
 System.out.println("判断两颗黑子是否相同:" + (black1==black2));

 //通过享元工厂获取两颗白子
 white1 = factory.getIgoChessman("w");
 white2 = factory.getIgoChessman("w");
 System.out.println("判断两颗白子是否相同:" + (white1==white2));

 //显示棋子,同时设置棋子的坐标位置
 black1.display(new Coordinates(1,2));
 black2.display(new Coordinates(3,4));
 white1.display(new Coordinates(2,5));
 white2.display(new Coordinates(2,4));
 

享元模式优缺点

优点

  1. 可以极大减少内存中对象的数量,使得相同或相似对象在内存中只保存一份,从而可以节约系统资源,提高系统性能。
  2. 享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。

缺点

  1. 享元模式使得系统变得复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂化。
  2. 为了使对象可以共享,享元模式需要将享元对象的部分状态外部化,而读取外部状态将使得运行时间变长。

你可能感兴趣的:(设计模式《享元模式》)