享元模式

享元模式

標籤 : Java與設計模式


內存屬於稀缺資源, 不能隨便浪費. 如果有很多相同/相似的對象, 我們可以通過享元節省內存.


內部狀態 vs. 外部狀態

享元模式(Flyweight): 運用共享技術有效地重用大量細粒度的對象.

  • 享元對象能做到共享的關鍵是區分了內部狀態外部狀態:
    此處輸入圖片的描述
    • 在享元對象內部並且不會隨環境改變而改變的共享部分, 可稱之爲享元對象的內部狀態.
    • 隨環境改變而改變的、不可以共享的狀態是外部狀態.

在設計開發中,有時需要生產大量細粒度對象來表徵數據, 如果這些對象除個別參數外基本相同, 此時如果能把那些參數移到類實例外面, 在方法調用時將其傳入, 就可以通過共享大幅度減少類實例數目.


模式實現

案例: 圍棋設計

有下棋經驗的同學都知道一盤棋的棋子大小、材質、顏色(黑/白)往往都是確定的, 而圍棋落子的位置卻不一定(看水平高低了O(∩_∩)O!), 因此我們可以將棋子位置從棋子對象中剝離, 然後讓棋子對象共享大小、材質、顏色屬性, 並在調用時將位置傳入, 就可大大減少棋子對象的數量:
此處輸入圖片的描述


Flyweight

所有具體享元類的超類或接口, 通過該接口, Flyweight可以接受並作用於外部狀態:

/**
 * @author jifang
 * @since 16/8/26 上午10:27.
 */
public interface Flyweight {
    void operation(Location location);
}

ConcreteFlyweight

實現Flyweight接口, 併爲內部狀態增加存儲空間:

class GoFlyweight implements Flyweight {

    private String color;

    private double radius;

    private String material;

    public GoFlyweight(String color, double radius, String material) {
        this.color = color;
        this.radius = radius;
        this.material = material;
    }

    public String getColor() {
        return color;
    }

    public double getRadius() {
        return radius;
    }

    public String getMaterial() {
        return material;
    }

    @Override
    public void operation(Location location) {
        System.out.println("[" + color + "]棋 [" + material + "]材質 半徑[" + radius + "]CM 落在" + location);
    }
}

UnsharedConcreteFlyweight

指不需要共享的Flyweight子類, 因爲Flyweight接口共享成爲可能, 但它並不強制共享. UnsharedConcreteFlyweight用於解決那些不需要共享對象的問題:

class Location {

    private int locX;

    private int locY;

    public Location() {
    }

    public Location(int locX, int locY) {
        this.locX = locX;
        this.locY = locY;
    }

    public int getLocX() {
        return locX;
    }

    public void setLocX(int locX) {
        this.locX = locX;
    }

    public int getLocY() {
        return locY;
    }

    public void setLocY(int locY) {
        this.locY = locY;
    }


    @Override
    public String toString() {
        return "{" +
                "locX=" + locX +
                ", locY=" + locY +
                '}';
    }
}
  • FlyweightFactory
    享元工廠,用來創建並管理Flyweight對象,作用是確保合理地共享Flyweight, 當用戶請求一個Flyweight時, FlyweightFactory提供一個共享實例:
public class FlyweightFactory {

    private static Map<String, GoFlyweight> map = new ConcurrentHashMap<>();

    public static GoFlyweight getGoFlyweight(String color) {
        GoFlyweight flyweight = map.get(color);
        if (flyweight == null) {
            flyweight = new GoFlyweight(color, 1.1, "陶瓷");
            map.put(color, flyweight);
        }
        return flyweight;
    }
}

小結

享元模式可以極大減少內存中對象的數量: 相同/相似對象只保留一份, 節約資源, 提高性能. 且將外部狀態剝離, 使外部狀態相對獨立, 不影響內部狀態. 但相比原先的設計, 增加了實現複雜度, 且讀取外部狀態使得運行時間變長(時間換空間).

  • 場景
    如果一個應用使用了大量對象從而造成很大的存儲開銷時;
    如果對象的有大量外部狀態, 且剝離外部狀態就可用相對較少的共享對象取代很多實例時;
    • ‘池’化資源, 如: 線程池、數據庫連接池.
    • String類設計.

參考
《JAVA與模式》之享元模式
C#設計模式(12)——享元模式(Flyweight Pattern)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章