欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

设计模式-享元模式(FlyWeight)

程序员文章站 2022-06-28 16:37:45
一、概念 享元模式是对象的结构模式,它以共享的方式高效的支持大量的细粒度对象,减少对象的数量,并达到节约内存的目的。 享元对象能够做到共享的关键,主要是区分了内部状态和外部状态,内部状态是对象是在建立时就已确定了,且它不随环境的改变而有所不同,所以这些内部状态就可以共享,而外部状态是会随着环境的变化 ......

一、概念

  享元模式是对象的结构模式,它以共享的方式高效的支持大量的细粒度对象,减少对象的数量,并达到节约内存的目的。

   享元对象能够做到共享的关键,主要是区分了内部状态和外部状态,内部状态是对象是在建立时就已确定了,且它不随环境的改变而有所不同,所以这些内部状态就可以共享,而外部状态是会随着环境的变化而会改变的,不可以共享。所以外部状态必须由客户端保存,当需要时可以传给享元对象。

二、模式动机

   当一个系统对于同一个对象类型,有大量的对象实例 ,且这些对象实例里面的状态大部分都可以外部化,而对一些不可变的相同内部状态一组实例,就可以用一个对象代替。这样就可以减少对角的数量,从而达到节约内存目的。

三、模式的结构

  设计模式-享元模式(FlyWeight)

  角色分析:

    FlyWeight:享元接口,通过这个接口Flyweight可以接受并作用于外部状态。通过这个接口传入外部的状态,在享元对象的方法处理过程中可能会使用到这些数据。

    ConcreteFlyWeight:具体的享元对象,这些对象必须是可以共享的,需要封装Flyweight的内部状态。

    UnsharedConcreteFlyweight:非共享的享元实例对象,并非所有的Flyweight实现对象都需要共享,非共享的享元实现对象通常是共享的享元实例对象的组合。

    FlyWeightFactory:主要用来创建并管理共享享元对象,并对外提供访问共享享元对象的接口,它内部往往有一个共享享元对象的实例池,通过这个实例池来实现享元对象的共享。

    Client:享元客户端,主要的工作是维持一个对Flyweight对象的引用,通过FlyWeightFactory获取享元对象,并将客户端存储的外部状态作用于享元对象。

 

  代码样例如下:

  

设计模式-享元模式(FlyWeight)
package flyweight.sample;

/**
 * 抽像享元角色,所有具体享元角色的超类,通过这个角色享元接收并作用于外部状态
* @ClassName: FlyWeight 
* @author beteman6988
* @date 2018年3月31日 上午7:50:48 
*
 */
public interface FlyWeight {

    /**
     * 具体业务逻辑
    * @Title: operation 
    * @param @param extrinsicSate  外部状态 
    * @return void    
    * @throws
     */
    public void operation(String extrinsicSate);

}


/**
 * 享元对象:可以共享的享元对象
* @ClassName: ConcreteFlyWeight 
* @author beteman6988
* @date 2018年3月31日 上午7:56:13 
*
 */
public class ConcreteFlyWeight implements FlyWeight {

    private String intrinsicState ; //不依赖于环境改变而改变的内部状态
    
    
    /**
     * 构造方法,传入享元对象的内部状态
     * @param intrinsicState:内部状态
     */
    public ConcreteFlyWeight(String intrinsicState) {
        super();
        this.intrinsicState = intrinsicState;
    }

    /**
     * 具体业务逻辑
    * @Title: operation 
    * @param @param extrinsicSate  外部状态 
    * @return void    
    * @throws
     */
    @Override
    public void operation(String extrinsicSate) {
        //具体业务逻辑
    }

}



import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
 * 不需要共享的复合享元对象
 * 
 * @ClassName: UnsharedConcreteFlyWeight
 * @author beteman6988
 * @date 2018年3月31日 上午8:05:55
 *
 */
public class UnsharedConcreteFlyWeight implements FlyWeight {

    //享元集合 ,主键:代表共享享元的主键   值:共享享元主键代表的共享享元
    private Map<String,FlyWeight> flies = new HashMap<String,FlyWeight>();
    
    /**
     * 往复合享元对象中添加共享享元
    * @Title: add 
    * @param @param intrinsticState:内部状态(不一定必须是内部状态,只要是代表该共享享元的主键即可)
    * @param @param oneFlyWeight   
    * @return void    
    * @throws
     */
    public void add(String intrinsticState,FlyWeight oneFlyWeight) {
        this.flies.put(intrinsticState,oneFlyWeight);
    }

    /**
     * 作用于所有无享元的具体业务逻辑
    * @Title: operation 
    * @param @param extrinsicSate  外部状态 
    * @return void    
    * @throws
     */
    @Override
    public void operation(String extrinsicSate) {
        //具体业务逻辑
    }
    
    public String toString() {
        for(Iterator it=flies.entrySet().iterator();it.hasNext();) {
            Map.Entry entry=(Entry) it.next();
            System.out.println(entry.getValue());
        }
        return null;
    }
    
}


import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 提供共享享元对象或复合享元对象的工厂
 * 
 * @ClassName: FlyWeightFactory
 * @author beteman6988
 * @date 2018年3月31日 上午8:24:14
 *
 */
public class FlyWeightFactory {
    // 共享享元的实例池 ,主键:代表共享享元的主键       值:共享享元主键代表的共享享元
    private Map<String, FlyWeight> flies = new HashMap<String, FlyWeight>();

    /**
     * 返回共享享元对象的工厂方法
    * @Title: factory 
    * @param @param intrinsticState :内部状态
    * @param @return   
    * @return FlyWeight :共享享元实例  
    * @throws
     */
    public FlyWeight factory(String intrinsticState) {
        // 从实例池获取需要的享元对象
        FlyWeight flyWeight = this.flies.get(intrinsticState);

        if (null == flyWeight) { // 如果获取不到则创建享元对象
            flyWeight = new ConcreteFlyWeight(intrinsticState);
            this.flies.put(intrinsticState, flyWeight); // 将创建的享元对象添加到实例池
        }
        return flyWeight;
    }

    /**
     * 返回不共享的复合享元实例
    * @Title: factory 
    * @param @param intrinsticStates
    * @param @return   
    * @return FlyWeight    
    * @throws
     */
    public FlyWeight factory(List<String> intrinsticStates) {
        UnsharedConcreteFlyWeight unsharedFlyWeight=new UnsharedConcreteFlyWeight(); //创建不共享的复合享元
        for(String intrinsticState:intrinsticStates) {
            unsharedFlyWeight.add(intrinsticState, this.factory(intrinsticState));
        }
        return unsharedFlyWeight;
    }
    
    
}



import java.util.ArrayList;
import java.util.List;

public class Client {

    public static void main(String[] args) {
        FlyWeightFactory factory =new FlyWeightFactory();
        
        List  list=new ArrayList();
        list.add("a");
        list.add("a");
        
        FlyWeight three=factory.factory(list);
        System.out.println(three);
        
        FlyWeight one=factory.factory("a");
        FlyWeight two=factory.factory("a");
        System.out.println(one);
        System.out.println(two);
                
    }
}
View Code

代码运行结果如下:

flyweight.sample.ConcreteFlyWeight@15db9742
null
flyweight.sample.ConcreteFlyWeight@15db9742
flyweight.sample.ConcreteFlyWeight@15db9742

通过结果可以看出,三个对象的hashcode相同,说明是同一个对象。

 

四、模式样例

   通过分析Integer.valueOf(int i)来分析JDK(JDK1,8.0.131)享元设计模式,首先分析Integer这个类是否可以做为一个享元的实现类,如下图:

设计模式-享元模式(FlyWeight)

  可以看出这个类的内部状态为final的value,且这个值是通过构造函数传入,因为它是final int,所以它的值是不随环境的改变而受到影响的,所以Integer类可以做为享元对象的实现类来使用。

   再来看valueOf(int i)函数:

设计模式-享元模式(FlyWeight)

   可以看出如果传入的值>=IntegerCache.low且<=IntegerCache.high ,那么就直接从IntegerCache.cache[i + (-IntegerCache.low)]里直接去取对象,而不是新建对象,从而实现对象的共享。

  再来看一下IntegerCache这个类,如下图:

设计模式-享元模式(FlyWeight)

     可以看出它是Integer类的一个静态私有内部类,它有一个static final Integer cache[]的成员,且这成员变量在类加载时通过静态初始化块(语句2)将256(从上面的代码可以看出 是从 -128到127  共256个Integer对象)个Integer对象初始化到了cache[]数组里面,只要Integer类通过静态方法public static Integer valueOf(int i) 就可以直接从这个cache[]里面取到对应int值的Integer对象(前提是int i值的范围也是-128到127 ,但是这个缓存数组的大小也是可以改变的,通过语句3 ,在JDK的参数里面设置-Djava.lang.Integer.IntegerCache.high=整数值,就可以改变大小,但是这个值不能小于127,如果小于127会取缺省的127 )。

 

五、与其它模式的关系

  享元模式与单例模式:这两个模式可以组合使用。通常情况下,享元模式中的享元工厂可以实现成为单例模式。

        享元模式与组合模式:这两个模式也是可以组合使用的。在享元模式中,存在不需要共享的实现,这些不需要共享的享元通常是对共享的享元对象的组合对象。也就是说,享元模式通常会和组合模式组合使用,来实现更复杂的对象层次结构。