2013年7月27日 星期六

設計模式:享元模式 (Flyweight Pattern)

享元模式 (Flyweight Pattern),以下程式碼以 C# 為例

說明:
物件之間,若有共同的部分可以共享,則將可共用的部分獨立為共享物件,
不能共享的部份外部化,使用時再將外部化的部分傳給共享物件。
這樣做的優點是減少記憶體使用量。缺點是程式邏輯可能變得較複雜。

範例:
黑白棋,在棋盤上放 3 顆黑棋, 3 顆白棋。一般情況,可能會實例化 6 顆獨立的棋子物件,每一個物件都有:黑棋或白棋、X作標、Y作標,三個資料。享元模式則是將共用的資料獨立為共用物件,下面範例,假設"黑棋"兩個字是黑棋共用的,"白棋"兩個字是白棋共用的,故規劃成共享物件;X、Y座標每個棋子都不一樣,所以獨立為外部資料,使用時再傳給共享物件。如此便可減少記憶體中的重覆資料。

希望達成如下的效果
static void Main(string[] args)
{
    ChessFlyweightFactory f = new ChessFlyweightFactory();

    ChessFlyweight a1 = f.GetChessFlyweight("黑棋"); // 取得黑棋共享物件
    a1.Display(1, 1); // 提供座標資料(非共享資料)
    ChessFlyweight a2 = f.GetChessFlyweight("黑棋"); // 取得黑棋共享物件
    a2.Display(1, 2); // 提供座標資料(非共享資料)
    ChessFlyweight a3 = f.GetChessFlyweight("黑棋"); // 取得黑棋共享物件
    a3.Display(1, 3); // 提供座標資料(非共享資料)
    ChessFlyweight b1 = f.GetChessFlyweight("白棋"); // 取得白棋共享物件
    b1.Display(2, 1); // 提供座標資料(非共享資料)
    ChessFlyweight b2 = f.GetChessFlyweight("白棋"); // 取得白棋共享物件
    b1.Display(2, 2); // 提供座標資料(非共享資料)
    ChessFlyweight b3 = f.GetChessFlyweight("白棋"); // 取得白棋共享物件
    b1.Display(2, 3); // 提供座標資料(非共享資料)

    Console.WriteLine("ChessFlyweight物件數量:{0}", f.GetChessFlyweightCount());

    Console.Read();
}
執行結果: (6顆棋,只有2個棋子物件)
黑棋(1,1)
黑棋(1,2)
黑棋(1,3)
白棋(2,1)
白棋(2,2)
白棋(2,3)
ChessFlyweight物件數量:2

實現重點在於,區分共享資料和不可以共享的資料。共享資料做成共享物件,不可共享資料則使用時再傳給共享物件。實例化共享物件時,則由享元工廠把關,判斷目前始否已經實例化過該共享物件,若有則直接回傳現存的共享物件。

其餘程式碼
// 棋子享元工廠,回傳棋子物件
class ChessFlyweightFactory
{
    private Hashtable chessFlyweight = new Hashtable();


    public ChessFlyweight GetChessFlyweight(string key)
    {
        if (!chessFlyweight.ContainsKey(key))
        {
            chessFlyweight.Add(key, new ConcreteChessFlyweight(key));
        }
        return (ChessFlyweight)chessFlyweight[key];
    }

    // 取得目前棋子物件數量
    public int GetChessFlyweightCount()
    {
        return chessFlyweight.Count;
    }
}


// 棋子享元抽像物件
abstract class ChessFlyweight
{
    protected string name; // 共享資料
    public ChessFlyweight(string name)
    {
        this.name = name;
    }
    public abstract void Display(int x, int y);
}

// 棋子享元(共享物件)
class ConcreteChessFlyweight : ChessFlyweight
{
    public ConcreteChessFlyweight(string name)
        : base(name)
    {
    }

    // X、Y座標,非共享資料
    public override void Display(int x, int y)
    {
        Console.WriteLine("{0}({1},{2})", this.name, x, y);
    }
}

/*
// 不能共享的的享元(UnsharableFlyweight)(複合享元),一般應該用不到
class UnsharedConcreteChessFlyweight : ChessFlyweight
{
    public UnsharedConcreteChessFlyweight(string name)
        : base(name)
    {
    }

    public override void Display(int x, int y)
    {
        Console.WriteLine("不共用的物件:{0}({1},{2})", this.name, x, y);
    }
}
*/

相關連結:設計模式整理列表

沒有留言:

張貼留言