享元設計模式(Flyweight Design Pattern)通過共用技術實現相同或相似對象的重用,節省記憶體,前提是享元對象是不可變對象。 ...
簡介
古代的活字印刷術就有點像享元模式,活字印刷就是將每個字模做出來,再印刷時再選取需要的字模到印刷板上,這樣就構成了一頁書的印刷板。這樣的活字印刷大大提升了效率,減少了印刷板的空間。
在享元模式中,存儲共用實例對象的地方稱為享元池(Flyweight Pool)。類比到上述的活字印刷術中,放置字模的地方就是享元池。
享元模式能做到共用的關鍵是區分了內部狀態和外部狀態,它們的簡單釋義是:
- 內部狀態:存儲在享元對象內部且不跟隨環境變化而改變的狀態,內部狀態可以在對象之間共用
- 外部狀態:通常由客戶端保存,當需要時再傳入享元對象中的狀態,會跟隨環境變化而改變,不可以在對象之間共用。
典型示例
一個典型的享元工廠類的代碼示例如下:
public class FlyweightFactory {
// 使用 HashMap 定義享元池
private final HashMap<String, Flyweight> flyweights = new HashMap<>();
public Flyweight getFlyweight(String key) {
// 如果對象存在,直接從享元池中獲取
if (flyweights.containsKey(key)) {
return (Flyweight) flyweights.get(key);
} else {
// 如果對象不存在,先創建一個新的對象添加到享元池中,然後返回
Flyweight fw = new ConcreteFlyweight();
flyweights.put(key, fw);
return fw;
}
}
}
一個典型的享元抽象類的代碼示例如下:
public abstract class Flyweight {
// 內部狀態作為成員對象,同一個享元對象其內部狀態是一致的
protected Object intrinsicState;
public Flyweight(Object intrinsicState) {
this.intrinsicState = intrinsicState;
}
public void operation(Object extrinsicState) {
// 外部狀態在使用時由外部提供,每一次都可以不同
}
}
通常,實際會根據業務情況定義具體享元類,代碼示例如下:
public class ConcreteFlyweight extends Flyweight {
public ConcreteFlyweight(Object intrinsicState) {
super(intrinsicState);
}
@Override
public void operation(Object extrinsicState) {
// 結合內部狀態 intrinsicState 以及方法參數傳入的 extrinsicState 完成具體邏輯
}
}
總結
優點
享元模式的主要優點如下:
- 極大地減少記憶體中對象的數量,節約了系統資源,提高了系統性能
- 享元模式的外部狀態相對獨立,而且不會影響其內部狀態,從而使得享元對象可以在不同的環境中被共用
缺點
享元模式的主要缺點如下:
- 享元模式需要分離出內部狀態和外部狀態,從而使得系統變得複雜,這使得程式的邏輯複雜化
- 為了使對象可以共用,享元對象需要將部分狀態外部化,而讀取外部狀態將使得運行時間變長
適用場景
享元模式的適用場景如下:
- 一個系統有大量相同或相似的對象,造成記憶體大量耗費
源碼
在 Java 類庫中的 String
類就使用了享元模式,使用字面量創建的對象是共用的,而不會重新為此分配記憶體空間。