zoukankan      html  css  js  c++  java
  • 设计模式~享元模式

    享元模式是对象的结构模式。

    享元模式以共享的方式高效的支持大量的细粒度对象。

    享元对象能做到共享的关键是区分内蕴状态(Internal State)和外蕴状态(External)。

    一个内蕴状态是存储在享元对象内部的,并且是不会随环境改变而有所不同的。因此,一个享元可以具有内蕴状态并可以共享。

    一个外蕴状态是随环境改变而改变的、不可以共享的状态。享元对象的外蕴状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候再传入享元对象内部。

    外蕴状态不可以影响享元对象的内蕴状态。也就是说,它们是相互独立的。

    享元模式的种类

    • 单纯享元模式
    • 复合享元模式

    单纯享元模式

     角色如下:

    • 抽象享元角色此角色是所有具体享元类的超类,为这些类规定出需要实现的公共接口。
    • 具体享元角色(ConcreteFlyweight): 实现抽象享元角色所规定的接口。如果有内蕴状态的话,必须负责为内蕴状态提供存储空间。享元对象的内蕴状态必须于对象所处的周围环境无关,从而使得享元对象可以在系统内共享。
    • 享元工厂角色(FlyweightFactory): 本角色负责创建和管理享元角色。本角色必须保证享元对象可以被系统适当的共享。
    • 客户端角色本角色需要维护一个对所有享元对象的引用。本角色需要自行 存储所有享元对象的外蕴状态。

    示意源码

     抽象享元角色 Flyweight

    public abstract class Flyweight {
    
        //一个示意性方法,参数state是外蕴状态
        public abstract void operation(String state);
    }

    具体享元角色 ConcreteFlyweight

    public class ConcreteFlyweight extends Flyweight {
    
        private Character intrinsicState = null;
        /**
         * 构造函数,内蕴状态作为参量传入
         * @param state
         */
        public ConcreteFlyweight(Character state){
            this.intrinsicState = state;
        }
        /**
         * 外蕴状态作为参量传入方法中,改变方法的行为,
         * 但是并不改变对象的内蕴状态
         */
        @Override
        public void operation(String state) {
            // TODO Auto-generated method stub
            System.out.println("
    Instrinsic State = "+intrinsicState + ".Extrinsic State = "+state);
        }
    
    }

    享元工厂角色 FlyweightFactory

    public class FlyweightFactory {
    
        private HashMap flies = new HashMap();
        private Flyweight InkFlyweight;
        public FlyweightFactory(){}
        
        public Flyweight factory(Character state){
            if(flies.containsKey(state)){
                return (Flyweight)flies.get(state);
            }else{
                Flyweight fly = new ConcreteFlyweight(state);
                flies.put(state, fly);
                return fly;
            }
        }
        /**
         * 辅助方法
         */
        public void checkFlyweight(){
            Flyweight fly;
            int i =0;
            System.out.println("
    ====checkFlyweight()=====");
            for(Iterator it = flies.entrySet().iterator();it.hasNext();){
                Map.Entry e = (Map.Entry)it.next();
                System.out.println("Item "+ (++i)+":"+e.getKey());
            }
            System.out.println("======checkFlyweight()======");
        }
    }

    客户端调用

    public class Client {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
    
            //创建一个享元工厂对象
            FlyweightFactory factory = new FlyweightFactory();
            //向享元工厂对象请求一个内蕴状态为‘a’的享元对象
            Flyweight fly = factory.factory(new Character('a'));
            //以参量方式传入一个外蕴状态
            fly.operation("First Call");
            //向享元工厂对象请求一个内蕴状态为‘b’的享元对象
            fly = factory.factory(new Character('b'));
            //以参量方式传入一个外蕴状态
            fly.operation("Second Call");
            fly = factory.factory(new Character('a'));
            fly.operation("Third Call");
            
            
            factory.checkFlyweight();
        }
    
    }

    虽然上面申请了三个享元对象,但是实际上创建的享元对象只有两个,这就是共享的含义。

    复合享元模式

    在单纯享元模式中,所有的享元对象都是单纯享元对象,也就是说都是可以直接共享的。

    而对于将一些单纯享元使用合成模式加以复合,形成的复合享元模式而言,这样的复合享元对象本身不能共享,但是它们可以分解成单纯享元对象,而后者可以共享。

     涉及的角色:

    • 抽象享元角色:此角色是所有的具体享元类的超类,为这些类规定出需要实现的公共接口。
    • 具体享元角色(ConcreteFlyweight): 实现抽象享元角色所规定的接口。如果有内蕴状态,必须为内蕴状态提供存储空间。并且享元对象的内蕴状态必须与对象所处的周围环境无关,从而使享元对象可以在系统内共享。
    • 复合享元角色(UnsharableFlyweight): 复合享元角色所代表的对象时不可以共享的,但是一个复合享元对象可以分解成多个本身是单纯享元对象的组合。
    • 享元工厂(FlyweightFactory): 本角色负责创建和管理享元角色。本角色必须保证享元对象可以被系统适当的共享。
    • 客户端角色:本角色需要自行存储所有享元对象的外蕴状态。

    示意性源码:

    抽象享元角色

    public abstract class Flyweight {
    
        //一个示意性方法,参数state是外蕴状态
        public abstract void operation(String state);
    }

    具体享元角色

    public class ConcreteFlyweight extends Flyweight {
    
        private Character intrinsicState = null;
        /**
         * 构造函数,内蕴状态作为参量传入
         * @param state
         */
        public ConcreteFlyweight(Character state){
            this.intrinsicState = state;
        }
        /**
         * 外蕴状态作为参量传入方法中,改变方法的行为,
         * 但是并不改变对象的内蕴状态
         */
        @Override
        public void operation(String state) {
            // TODO Auto-generated method stub
            System.out.println("
    Instrinsic State = "+intrinsicState + ".Extrinsic State = "+state);
        }
    }

    具体复合享元角色

    public class ConcreteCompositeFlyweight extends Flyweight {
    
        private HashMap flies = new HashMap(10);
        private Flyweight flyweight;
        public ConcreteCompositeFlyweight(){
            
        }
        // 增加一个新的单纯享元对象到聚集中
        public void add(Character key,Flyweight fly){
            flies.put(key, fly);
        }
        //外蕴状态作为参量传入到方法中
        @Override
        public void operation(String extrinsicState) {
            Flyweight fly = null;
            for(Iterator it = flies.entrySet().iterator();it.hasNext();){
                Map.Entry e = (Map.Entry)it.next();
                fly = (Flyweight)e.getValue();
                fly.operation(extrinsicState);
            }
        }
    }

    享元工厂角色

    public class FlyweightFactory {
    
        private HashMap flies = new HashMap();
        public FlyweightFactory(){}
        
        public Flyweight factory(Character state){
            if(flies.containsKey(state)){
                return (Flyweight)flies.get(state);
            }else{
                Flyweight fly = new ConcreteFlyweight(state);
                flies.put(state, fly);
                return fly;
            }
        }
        
        public Flyweight factory(String compositeState){
            ConcreteCompositeFlyweight compositeFly = new ConcreteCompositeFlyweight();
            int length = compositeState.length();
            Character state = null;
            for(int i=0;i<length;i++){
                state = new Character(compositeState.charAt(i));
                System.out.println("factory("+state+")");
                compositeFly.add(state, this.factory(state));
            }
            return compositeFly;
        }
        /**
         * 辅助方法
         */
        public void checkFlyweight(){
            Flyweight fly;
            int i =0;
            System.out.println("
    ====checkFlyweight()=====");
            for(Iterator it = flies.entrySet().iterator();it.hasNext();){
                Map.Entry e = (Map.Entry)it.next();
                System.out.println("Item "+ (++i)+":"+e.getKey());
            }
            System.out.println("======checkFlyweight()======");
        }
    }

    客户端

    public class Client {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
    
            //创建一个享元工厂对象
            FlyweightFactory factory = new FlyweightFactory();
            Flyweight fly = factory.factory("aba");
            fly.operation("Composite Call");
            
            
            factory.checkFlyweight();
        }
    
    }

    享元模式应当在什么情况下使用

    1. 一个系统有大量的对象
    2. 这些对象耗费大量的内存
    3. 这些对象的状态中的大部分都可以外部化。
    4. 这些对象可以按照内蕴状态分成很多的组,当把外蕴对象从对象中剔除时,每一个组都可以仅用一个对象代替。
    5. 软件系统不依赖这些对象的身份,换言之,这些对象可以是不可分辨的。

    满足以上的这些条件的系统可以使用享元对象。

    享元模式的优点和缺点

    享元模式的优点在于它大幅度的降低内存中对象的数量。

    但是,它要做到这一点付出的代价也是很高的:

    • 享元模式使得系统更加复杂。为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化。
    • 享元模式将享元对象的状态外部化,而读取外部状态使得运行时间稍变长。

    享元模式的应用

      享元模式在编辑器系统中大量使用。一个文本编译器往往会提供很多中字体,而通常的做法是将每一个字母做出一个享元对象。

    享元对象的内蕴状态就是这个字母,而字母在文本中的位置和字母风格等其他信息则是外蕴状态。比如,字母a可能出现在文本的很多地方,虽然这些字母a的位置和字母风格不同,但是所有这些地方使用的都是同一个字母对象。

    这样一来,字母对象就可以在整个系统中共享。

      在java语言中,String类型就使用了享元模式。String 对象是不变对象,一旦创建出来就不能改变。

    如果需要改变一个字符串的值,就只好创建一个新的String对象。在JVM内部,String对象都是共享的。

    如果一个系统中有两个String对象所包含的字符串相同的话,JVM实际上只创建一个String对象提供给两个引用,从而实现String对象的共享。String的intern()方法给出这个字符串在共享池中的唯一实例。

  • 相关阅读:
    VGA实验 :逻辑分析仪
    VGA实验:点亮屏幕
    ASP.NET 4‎.0 生成 eurl.axd Http异常错误的处理方法 (汗IIS 怎这么多莫名其妙的问题)
    腾讯检测 IP 省市的接口
    hql 中cast 方法的使用
    asp.net 防止外部提交数据(转)
    为 SQL Server 启用缓存通知
    net中前台 javascript与后台c#函数相互调用
    const 与 readonle 的异同
    获取页面执行时间的几种方法(asp.net转)
  • 原文地址:https://www.cnblogs.com/Vincent-yuan/p/13551050.html
Copyright © 2011-2022 走看看