1. 程式人生 > 實用技巧 >設計模式之享元(flyweight)模式

設計模式之享元(flyweight)模式

設計模式之享元(flyweight)模式

在我看來所謂的享元模式就是有就用,沒有就創建出來再用,比如說執行緒池,在啟動執行緒時就是這樣的。因此享元模式的實踐就是各種池技術。這樣可以避免頻繁的建立銷燬資源,到達資源複用的效果。當然了,在《研磨設計模式》這本書中呢,講的就比較多了,在這本書中闡述的享元模式是這樣的:

  • 1.變與不變
    享元模式設計的重點就在於分離變與不變。把一個物件的狀態分成內部狀態和外部狀態,內部狀態是不變的,外部狀態是可變的。然後通過共享不變的部分,達到減少物件數量並節約記憶體的目的。在享元物件需要的時候,可以從外部傳入外部狀態給共享的物件,共享物件會在功能處理的時候,使用自己內部的狀態和這些外部的狀態。
    事實上,分離變與不變是軟體設計上最基本的方式之一,比如預留介面,為什麼在這個地方要預留介面,一個常見的原因就是這裡存在變化,可能在今後需要擴充套件或者是改變已有的實現,因此預留介面作為“可插入性的保證”。

  • 2.共享與不共享
    在享元模式中,享元物件又有共享與不共享之分,這種情況通常出現在和組合模式合用的情況,通常共享的是葉子物件,一般不共享的部分是由共享部分組合而成的,由於所有細粒度的葉子物件都已經快取了,那麼快取組合物件就沒有什麼意義了。這在後面將給大家一個示例。

  • 3.內部狀態和外部狀態
    享元模式的內部狀態,通常指的是包含在享元物件內部的、物件本身的狀態,是獨立於使用享元的場景的資訊,一般建立後就不再變化的狀態,因此可以共享。
    外部狀態指的是享元物件之外的狀態,取決於使用享元的場景,會根據使用場景而變化,因此不可共享。如果享元物件需要這些外部狀態的話,可以從外部傳遞到享元物件中,比如通過方法的引數來傳遞。
    也就是說享元模式真正快取和共享的資料是享元的內部狀態,而外部狀態是不應該被快取共享的。
    還有一點,內部狀態和外部狀態是獨立的,外部狀態的變化不應該影響到內部狀態。

  • 4.例項池
    在享元模式中,為了建立和管理共享的享元部分,引入了享元工廠。享元工廠中般都包含有享元物件的例項池,享元物件就是快取在這個例項池中的。

  • Flyweight:享元介面,通過這個介面 Flyweight可以接受並作用於外部狀態。通過這個介面傳入外部的狀態,在享元物件的方法處理中可能會使用這些外部的資料。

  • Concreteflyweight:具體的享元實現物件,必須是可共享的,需要封裝 Flyweight的內部狀態。

  • FlyweightFactory:享元工廠,主要用來建立並管理共享的享元物件,並對外提供訪問共享享元的介面。

  • Client:享元客戶端,主要的工作是維持一個對 Flyweight的引用,計算或儲存享元物件的外部狀態,當然這裡可以訪問共享和不共享的 Flyweight物件

具體程式碼

Flyweight 享元介面

public interface Flyweight {
}

ConcreteFlyweight 具體的可共享的類

public class ConcreteFlyweight implements Flyweight {
    int state;

    public ConcreteFlyweight(int state) {
        this.state = state;
    }
}

FlyweightFactory 用來產生享元的工廠

public class FlyweightFactory {
    HashMap<Integer, Flyweight> map = new HashMap<>();
    private static final FlyweightFactory flyweightFactory = new FlyweightFactory();

    private FlyweightFactory() {
    }

    public static FlyweightFactory getInstance() {
        return flyweightFactory;
    }

    public Flyweight getFlyweight(int type) {
        if (map.containsKey(type)) {
            return map.get(type);
        }
        ConcreteFlyweight concreteFlyweight = new ConcreteFlyweight(type);
        map.put(type, concreteFlyweight);
        return concreteFlyweight;
    }

}

Client 具體的使用享元使用者

public class Client {
    public static void main(String[] args) {
        FlyweightFactory flyweightFactory = FlyweightFactory.getInstance();
        Flyweight flyweight1 = flyweightFactory.getFlyweight(1);
        Flyweight flyweight = flyweightFactory.getFlyweight(1);
        System.out.println(flyweight1 == flyweight);
    }
}

在示例程式中,我們瞭解到共享例項可以減少記憶體使用量。一般來說,共享例項可以減少所需資源的使用量。這裡的資源指的是計算機中的資源,而記憶體是資源中的一種。
寸間也是一種資源。使用new關鍵字生成例項會花費時間。通過 Flyweight/模式共享例項可以減少使用new關鍵字生成例項的次數。這樣,就可以提高程式執行速度。
檔案控制代碼(檔案描述符)和視窗控制代碼等也都是一種資源。在作業系統中,可以同時使用的檔案控制代碼和視窗控制代碼是有限制的。因此,如果不共享例項,應用程式在執行時很容易就會達到資源極限而導致崩潰。

from《圖解設計模式》