1. 程式人生 > 實用技巧 >設計模式:享元模式

設計模式:享元模式

定義

享元模式(Flyweight Pattern)主要用於減少建立物件的數量,以減少記憶體佔用和提高效能。這種型別的設計模式屬於結構型模式,它提供了減少物件數量從而改善應用所需的物件結構的方式。

適用場景:

  • 應用於系統底層開發。以解決系統性能問題
  • 系統有大量相似物件,需要緩衝池場景

具體場景

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

  • 抽象享元角色(IFlyweight):享元物件抽象基類或者介面,同時定義出物件的外部狀態和內部狀態的介面或實現。
  • 具體享元角色:實現抽象享元角色,該角色的內部狀態處理應該與環境無關,不會出現一個操作改變內部狀態,同時改變外部狀態的情況。
  • 享元工廠:負責管理享元物件池和建立享元物件。

以英雄聯盟兵線為例:有四種小兵,前排兵,後排兵,炮車,超級兵,每個兵種都有自己的顏色(忽略因時間變化等級升高的影響和大龍buff加持的影響等等),可以用享元模式簡單實現一下前排兵,後排兵的建立(跑車和超級兵省略)。

小兵介面


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();
    }
}

uml類圖:

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);
    }

一個為true,一個為false???是不是意想不到。

打上斷點,除錯:

可以得出,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的原始碼:

實際上,jdk在Long類中也有這樣的處理。