享元模式,又稱蠅量級模式 (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 模式

留言
張貼留言