设计模式:享元模式

定义

享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。

适用场景:

  • 应用于系统底层开发。以解决系统性能问题
  • 系统有大量相似对象,需要缓冲池场景

具体场景

享元模式主要包含三种角色:

  • 抽象享元角色(IFlyweight):享元对象抽象基类或者接口,同时定义出对象的外部状态和内部状态的接口或实现。
  • 具体享元角色:实现抽象享元角色,该角色的内部状态处理应该与环境无关,不会出现一个操作改变内部状态,同时改变外部状态的情况。
  • 享元工厂:负责管理享元对象池和创建享元对象。

以英雄联盟兵线为例:有四种小兵,前排兵,后排兵,炮车,超级兵,每个兵种都有自己的颜色(忽略因时间变化等级升高的影响和大龙buff加持的影响等等),可以用享元模式简单实现一下前排兵,后排兵的创建(跑车和超级兵省略)。

image-20201227205856586

小兵接口


public interface Minion {
    String getType();
    String getColour();
    default void attack(){
        System.out.println(getColour()+"色方的"+getType()+"正在攻击....");
    }
}

近战兵:

//近战兵
public class MeleeMinion implements Minion{

    private String colour;

    MeleeMinion(String colour) {
        this.colour = colour;
    }

    @Override
    public String getType() {
        return "近战兵";
    }

    @Override
    public String getColour() {
        return colour;
    }
}

远程兵:

//远程兵
public class CasterMinion implements Minion{

    private String colour;

    CasterMinion(String colour) {
        this.colour = colour;
    }

    @Override
    public String getType() {
        return "远程兵";
    }

    @Override
    public String getColour() {
        return this.colour;
    }

}

享元工厂:

class MinionFactory {

    private static Map<String, Minion> minionPool = new ConcurrentHashMap<>();

    //因为内部状态的不变性,所以缓存起来作为主键
    static Minion getMinion(String type, String colour){
        Minion minion;
        if("远程兵".equals(type)){
            if(!minionPool.containsKey(colour)){
                minion = new CasterMinion(colour);
                minionPool.put(colour,minion);
            }
        }else if("近战兵".equals(type)){
            if(!minionPool.containsKey(colour)){
                minion = new MeleeMinion(colour);
                minionPool.put(colour,minion);
            }
        }
        minion = minionPool.get(colour);
        return minion;
    }
}

测试:

public class Test {
    public static void main(String[] args) {
        Minion minion = MinionFactory.getMinion("近战兵", "蓝");
        minion.attack();
        Minion minion2 = MinionFactory.getMinion("近战兵", "蓝");
        minion2.attack();


        System.out.println(minion == minion2);

        Minion minion3 = MinionFactory.getMinion("远程兵", "红");
        minion3.attack();
    }
}

image-20201227212136992

uml类图:

image-20201227212234985

Integer(Long)中的享元模式

先测试以下代码:

    public static void main(String[] args) {
        Integer a = 100;
        Integer b = 100;
        System.out.println(a==b);

        Integer c = 200;
        Integer d = 200;
        System.out.println(c==d);
    }

image-20201227212453028

一个为true,一个为false???是不是意想不到。

打上断点,调试:

image-20201227212917521

可以得出,a和b是同一个对象,c和d不是同一个对象,进入Integer源代码,我们可以注意到这样一段代码:

    /**
     * Returns an {@code Integer} instance representing the specified
     * {@code int} value.  If a new {@code Integer} instance is not
     * required, this method should generally be used in preference to
     * the constructor {@link #Integer(int)}, as this method is likely
     * to yield significantly better space and time performance by
     * caching frequently requested values.
     *
     * This method will always cache values in the range -128 to 127,
     * inclusive, and may cache other values outside of this range.
     *
     * @param  i an {@code int} value.
     * @return an {@code Integer} instance representing {@code i}.
     * @since  1.5
     */
    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

Integer的valueOf方法:当值大于等于low或者小于等于high时会调用IntegerCache.cache()方法,否则直接new一个新的Integer,我们继续看IntegerCache的源码:

image-20201227214053541

实际上,jdk中的Long类也有这样的处理。

IntegerCache内部类上有这样的注释

    /**
     * Cache to support the object identity semantics of autoboxing for values between
     * -128 and 127 (inclusive) as required by JLS.
     *
     * The cache is initialized on first usage.  The size of the cache
     * may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
     * During VM initialization, java.lang.Integer.IntegerCache.high property
     * may be set and saved in the private system properties in the
     * sun.misc.VM class.
     */

大致意思我们可以通过设置AutoBoxCacheMax这个参数来控制包装类的最大缓存大小。

我们设置上参数,再运行

-XX:AutoBoxCacheMax=300

image-20201227214812340

image-20201227214848425

原文地址:https://www.cnblogs.com/wwjj4811/p/14198907.html