美文网首页
设计模式《享元模式》

设计模式《享元模式》

作者: 天道__ | 来源:发表于2018-08-07 17:56 被阅读0次

    引言

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

    示例地址

      Demo

    类图

    image

    定义

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

    使用场景

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

    享元模式的各种角色

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

    享元模式中的概念

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

    单纯享元模式

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

    1. 享元角色接口
    /**
     * 享元角色接口
     *
     * @author 512573717@qq.com
     * @created 2018/8/6  下午3:49.
     */
    public interface Flyweight {
        //外部状态
        public void operation(String state);
    }
    
    2. 享元角色的实现类
    /**
     * 享元角色的实现类
     *
     * @author 512573717@qq.com
     * @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 512573717@qq.com
     * @created 2018/8/6  下午3:54.
     */
    public class FlyweighttFactory {
        private Map<String, Flyweight> 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 512573717@qq.com
     * @created 2018/8/6  下午3:49.
     */
    public interface Flyweight {
        //外部状态
        public void operation(String state);
    }
    
    2. 享元角色的实现类
    /**
     * 享元角色的实现类
     *
     * @author 512573717@qq.com
     * @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 512573717@qq.com
     * @created 2018/8/6  下午4:54.
     */
    public class ConcreteCompositeFlyweight implements Flyweight {
    
        private Map<String, Flyweight> 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 512573717@qq.com
     * @created 2018/8/6  下午5:34.
     */
    public class FlyweightFactory {
    
        private Map<String, Flyweight> mStringFlyweightMap = new HashMap<String, Flyweight>();
    
        /**
         * 复合享元工厂方法
         *
         * @param compositeState
         * @return
         */
        public Flyweight factory(List<String> 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<String> compositeState = new ArrayList<String>();
    
      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 512573717@qq.com
    
     * @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 512573717@qq.com
     
     * @created 2018/8/7  下午3:58.
     * 
     */
    public class BlackQiZi extends  QiZi {
        @Override
        public String getColor() {
            return "黑色";
        }
    }
    
    3. 白子
    /**
     *  白色的棋子
     * 
     * @author 512573717@qq.com
     
     * @created 2018/8/7  下午3:58.
     * 
     */
    public class WhiteQiZi extends  QiZi{
        @Override
        public String getColor() {
            return "白色";
        }
    }
    
    4. 外部状态 坐标
    /**
     * 外部状态
     * 
     * @author 512573717@qq.com
     
     * @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 512573717@qq.com
     
     * @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. 为了使对象可以共享,享元模式需要将享元对象的部分状态外部化,而读取外部状态将使得运行时间变长。

    相关文章

      网友评论

          本文标题:设计模式《享元模式》

          本文链接:https://www.haomeiwen.com/subject/qmbwvftx.html