• 设计模式之享元模式


    定义

    运用共享技术有效地支持大量细粒度的对象。如围棋中的黑白棋子,教室中的凳子和桌子,这些对象有很多相似的地方,
    如果将其中相同的地方提取出来共享,就能节省大量的系统资源,这就是享元模式的适用场景。

    结构

    • Flyweight,享元接口,可以接收并作用于外部数据。
    • ConcreteFlyweight,可以共享的享元实现对象,包含自己的内部状态。
    • UnsharedConcreteFlyweight,非共享的享元实现对象,不能共享。
    • FlyweightFactory,享元工厂,用来创建并管理共享的享元对象。

    简单实现

    享元接口

    public interface Flyweight {
    
      /**
       * 传入外部状态
       */
      void operation(String extrinsicState);
    }
    

    可共享的享元实现对象

    public class ConcreteFlyweight implements Flyweight {
    
      /**
       * 内部状态
       */
      private String intrinsicState;
    
      public ConcreteFlyweight(String intrinsicState) {
        this.intrinsicState = intrinsicState;
      }
    
      @Override
      public void operation(String extrinsicState) {
        System.out.println("共享对象的内部状态为:" + intrinsicState);
        System.out.println("共享对象的外部状态为:" + extrinsicState);
      }
    }
    

    不能共享的享元实现对象

    public class UnsharedConcreteFlyweight implements Flyweight {
    
      @Override
      public void operation(String extrinsicState) {
        System.out.println("非共享对象的外部状态为:" + extrinsicState);
      }
    }
    

    享元工厂

    import java.util.HashMap;
    import java.util.Map;
    
    public class FlyweightFactory {
    
      private Map<String, Flyweight> flyweightMap = new HashMap<>();
    
      public Flyweight getFlyweight(String key) {
        if (!flyweightMap.containsKey(key)) {
          flyweightMap.put(key, new ConcreteFlyweight(key));
        }
        return flyweightMap.get(key);
      }
    }
    

    客户端

    public class Client {
    
      public static void main(String[] args) {
        FlyweightFactory flyweightFactory = new FlyweightFactory();
        Flyweight flyweight1 = flyweightFactory.getFlyweight("abc");
        Flyweight flyweight2 = flyweightFactory.getFlyweight("abc");
        System.out.println(flyweight1 == flyweight2);
        flyweight1.operation("aaa");
        flyweight1.operation("bbb");
      }
    
    }
    

    在实际开发中,我们可以简化享元模式,不考虑非共享对象。

    享元模式在JDK和Spring中的实现

    Integer中valueOf()方法

    public final class Integer extends Number implements Comparable<Integer> {
    
        @HotSpotIntrinsicCandidate
        public static Integer valueOf(int i) {
            if (i >= IntegerCache.low && i <= IntegerCache.high)
                return IntegerCache.cache[i + (-IntegerCache.low)];
            return new Integer(i);
        }
    
        private static class IntegerCache {
            static final int low = -128;
            static final int high;
            static final Integer cache[];
    
            static {
                // high value may be configured by property
                int h = 127;
                String integerCacheHighPropValue =
                    VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
                if (integerCacheHighPropValue != null) {
                    try {
                        int i = parseInt(integerCacheHighPropValue);
                        i = Math.max(i, 127);
                        // Maximum array size is Integer.MAX_VALUE
                        h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                    } catch( NumberFormatException nfe) {
                        // If the property cannot be parsed into an int, ignore it.
                    }
                }
                high = h;
    
                cache = new Integer[(high - low) + 1];
                int j = low;
                for(int k = 0; k < cache.length; k++)
                    cache[k] = new Integer(j++);
    
                // range [-128, 127] must be interned (JLS7 5.1.7)
                assert IntegerCache.high >= 127;
            }
    
            private IntegerCache() {}
        }
    

    Integer会缓存-128到127之间的数

    Spring中的ReflectionUtils

    public abstract class ReflectionUtils {
        private static Method[] getDeclaredMethods(Class<?> clazz, boolean defensive) {
    		Assert.notNull(clazz, "Class must not be null");
    		Method[] result = declaredMethodsCache.get(clazz);
    		if (result == null) {
    			try {
    				Method[] declaredMethods = clazz.getDeclaredMethods();
    				List<Method> defaultMethods = findConcreteMethodsOnInterfaces(clazz);
    				if (defaultMethods != null) {
    					result = new Method[declaredMethods.length + defaultMethods.size()];
    					System.arraycopy(declaredMethods, 0, result, 0, declaredMethods.length);
    					int index = declaredMethods.length;
    					for (Method defaultMethod : defaultMethods) {
    						result[index] = defaultMethod;
    						index++;
    					}
    				}
    				else {
    					result = declaredMethods;
    				}
    				declaredMethodsCache.put(clazz, (result.length == 0 ? EMPTY_METHOD_ARRAY : result));
    			}
    			catch (Throwable ex) {
    				throw new IllegalStateException("Failed to introspect Class [" + clazz.getName() +
    						"] from ClassLoader [" + clazz.getClassLoader() + "]", ex);
    			}
    		}
    		return (result.length == 0 || !defensive) ? result : result.clone();
    	}
    }
    

    缓存一个class的所有方法,Spring中很多地方都使用到了缓存。上述两种示例都是享元模式的变形,只有内部状态,没有外部状态,也就不需要考虑非共享对象了。

    总结

    优点

    1. 减少对象数量,节省内存空间。

    缺点

    1. 维护共享对象需要额外开销,如考虑共享对象何时失效,何时清除。

    本质

    享元模式的本质是分离和共享,分离的是对象中变和不变的部分,共享其中不变的部分。

    使用场景

    1. 使用了大量的相似的细粒度对象,占用了大量内存。

    参考

    大战设计模式【19】—— 享元模式
    大战设计模式(第二季)【6】———— 从源码看享元模式
    设计模式(十二)——享元模式(Integer缓冲池源码分析)
    设计模式的征途—12.享元(Flyweight)模式
    享元模式(详解版)
    研磨设计模式-书籍

  • 相关阅读:
    MM1排队系统
    java基本概念
    将博客搬至CSDN
    数据库知识点1
    离散分布
    概率论1--基本概念
    一道经典JS题(关于this)
    ajax请求过程中下载文件在火狐下的兼容问题
    熟悉css/css3颜色属性
    打字机游戏Ⅱ之手速pk
  • 原文地址:https://www.cnblogs.com/strongmore/p/15244637.html
Copyright © 2020-2023  润新知