Flyweight 定義:避免大量擁有相同內容的小型物件開銷(如記憶體耗盡),透過共享單一元類別(Meta-class)實現。
物件導向原則雖主張「萬物皆物件」,但若每個字元皆建立物件(如文字處理軟體),數千字即產生數千物件,嚴重消耗記憶體。須「求同存異」,萃取物件群共同點設計元類別封裝可共享部分(內部狀態,intrinsic state),而依賴應用情境(Context)且不可共享的部分為外部狀態(extrinsic state)。
簡言之:先建立原始模型,再依不同場合產生具特徵的具體模型。Flyweight 模式常搭配 Factory 模式:Flyweight Factory 維護 Flyweight Pool 存放內部狀態物件。此模式能顯著提升程式效能與執行速度,適用場合廣泛(如資料庫讀取大量重複字串,可存入 Flyweight Pool)。
public interface Flyweight {
public void operation(ExtrinsicState state);
}
public interface ExtrinsicState { }
ConcreteFlyweight 必須可共享,儲存的狀態皆為內部狀態,與應用環境無關。
public class ConcreteFlyweight implements Flyweight {
private IntrinsicState state;
public void operation(ExtrinsicState state) {
// 具體操作邏輯
}
}
並非所有子類別都需共享。
public class UnsharedConcreteFlyweight implements Flyweight {
public void operation(ExtrinsicState state) { }
}
管理 Flyweight Pool,請求時優先查找池中現有物件,否則建立新物件加入池中並回傳。
public class FlyweightFactory {
private Hashtable flyweights = new Hashtable();
public Flyweight getFlyweight(Object key) {
Flyweight flyweight = (Flyweight) flyweights.get(key);
if (flyweight == null) {
flyweight = new ConcreteFlyweight();
flyweights.put(key, flyweight);
}
return flyweight;
}
}
FlyweightFactory factory = new FlyweightFactory();
Flyweight fly1 = factory.getFlyweight("Fred");
Flyweight fly2 = factory.getFlyweight("Wilma");
表面看似單純 Factory 用法,奧妙在 Factory 內部共享機制。
以唱片 CD 為例,XML 儲存多張 CD 資料:發行年份、歌手、曲目。歌手姓名常重複(同一歌手多張專輯),適合作為可共享的 ConcreteFlyweight;年份與曲目為不可共享部分。
public class CD {
private String title;
private int year;
private Artist artist; // 共享物件
// getter/setter...
}
public class Artist { // ConcreteFlyweight
private String name; // 內部狀態
String getName() { return name; }
Artist(String n) { name = n; } // immutable
}
public class ArtistFactory {
Hashtable pool = new Hashtable();
Artist getArtist(String key) {
Artist result = (Artist) pool.get(key);
if (result == null) {
result = new Artist(key);
pool.put(key, result);
}
return result;
}
}
當 CD 數量達數千甚至更多,Flyweight 模式節省記憶體效果顯著,共享物件越多節省越大。