享元模式,又稱蠅量級模式 (Flyweight 是拳擊術語,指最輕量的級別,蠅量級),單看文字可能有點難理解,但簡單來說就是共享元件(資源)。試想一個情節:公司裡每個人都會有員工資訊,也都會有名片。假設名片是個類別時,對於每個員工,都需要 new 出名片的實體,而實際上名片裡也只是內容不同而已。當員工越多時,就需要 new 出越多名片物件,造成記憶體浪費,這時候就適合使用享元模式。以下為享元模式的正式定義及類別圖:
使用享元模式的最大優點,就是可以降低物件生成個數,以減少記憶體使用,且物件可被集中於一處來管理。假如你的系統中會生成很多物件,且物件本身的某些部份可以不用區分出差異(如範例中的名片不用特別區分是哪張名片),就可以使用此模式來分出共享元件與外部資源。而這個模式也是有缺點的,為了分出內外部資源,會造成程式架構較複雜,也因為大家都共用共享元件,無法有差異化,不同的行為。
參考資料
深入淺出設計模式(Head First Design Patterns)
Chapter 10 享元模式(Flyweight Pattern)
享元(Flyweight)模式
Flyweight 模式
共享物件,用來儘可能減少記憶體使用量以及分享資訊給儘可能多的相似物件。
Use sharing to support large numbers of fine-grained objects efficiently.
Use sharing to support large numbers of fine-grained objects efficiently.
- Flyweight:所有具象 Flyweight 子類別的共同介面
- ConcreteFlyweight:Flyweight 的具象子類別,就是要共享的元件類別。共享元件本身有內部的資料或狀態的話,都是在這裡維護。
- FlyweightFactory:負責創建與管理共享元件 (如例子中的名片)。通常會確認共享元件是否已存在,存在就直接給 Client 用,否則就創建新的共享元件。
- Client:取得共享元件並使用的角色,通常會結合外部資訊來使用共享元件 (如例子中的員工資訊)
// 名片是共享元件 // 提供一個介面讓子類別實作 public abstract class NameCard { // 名片要顯示的資訊 protected String mId; protected String mDept; protected String mTel; public abstract void show(Employee e); } public class GeneralNameCard extends NameCard { @Override public void show(Employee e) { // 從外部資源取得資料 mId = e.getId(); mDept = e.getDept(); mTel = e.getTel(); // 顯示名片內容 System.out.println(...) } } public class ManagerNameCard extends NameCard { // 管理階層的名片會多顯示職稱 // 假設這是共享元件自己要管理的內部資源 // 這邊只是範例, 把值寫死 protected String mTitle = "Manager"; @Override public void show(Employee e) { // 從外部資源取得資料 mId = e.getId(); mDept = e.getDept(); mTel = e.getTel(); // 顯示名片內容 // 會多顯示職稱 System.out.println(...) } } // 創建與管理共享元件的類別 public class NameCardFactory { // 範例用 Map 來儲存已創建的共享元件 // 當然可以有不同的實作 private static Map<Font, WeakReference<NameCard>> mNameCards = new WeakHashMap<Integer, WeakReference<NameCards>>(); public static final int TYPE_GENERAL = 0; public static final int TYPE_MANAGER = 1; public static NameCard getNameCard(int employeeType) { if(employeeType == TYPE_GENERAL) { return getGeneralNameCard(); } else if(employeeType == TYPE_MANAGER) { return getManagerNameCard(); } else { // Error hanlding. } } // 確認共享元件是否存在 // 不存在就創建並保留 private static NameCard getGeneralNameCard() { if(mNameCards.get(TYPE_GENERAL) == null) { mNameCards.put(TYPE_GENERAL, new GeneralNameCard()); } return mNameCards.get(TYPE_GENERAL).get(); } private static NameCard getManagerNameCard() { if(mNameCards.get(TYPE_MANAGER) == null) { mNameCards.put(TYPE_MANAGER, new GeneralNameCard()); } return mNameCards.get(TYPE_MANAGER).get(); } } public class Main { public static void main(String[] args) { // 假設 Employee 相關類別已存在 Employee generalEmployee1 = new GeneralEmployee(...); // 取得共享元件 NameCard card1 = NameCardFactory.getGeneralNameCard(); card1.display(generalEmployee1); Employee generalEmployee2 = new GeneralEmployee(...); // 取得共享元件 NameCard card2 = NameCardFactory.getGeneralNameCard(); // 這邊的結果會是 true System.out.println(card1 == card1); } }在 Java 實際應用中,最簡單且容易了解的應該就是 String 了,當你用以下的方式取得 String:
String str1 = "Hello world"; String str2 = "Hello world"; System.out.println(str1 == str2);在Java中,會維護一個String Pool,對於一些可以共享的字串物件,會先在String Pool中查找是否存在相同的String內容(字元相同),如果有就直接傳回,而不是直接創造一個新的String物件,因此上面的結果會顯示 true。
使用享元模式的最大優點,就是可以降低物件生成個數,以減少記憶體使用,且物件可被集中於一處來管理。假如你的系統中會生成很多物件,且物件本身的某些部份可以不用區分出差異(如範例中的名片不用特別區分是哪張名片),就可以使用此模式來分出共享元件與外部資源。而這個模式也是有缺點的,為了分出內外部資源,會造成程式架構較複雜,也因為大家都共用共享元件,無法有差異化,不同的行為。
參考資料
深入淺出設計模式(Head First Design Patterns)
Chapter 10 享元模式(Flyweight Pattern)
享元(Flyweight)模式
Flyweight 模式
留言
張貼留言