1. 程式人生 > >設計模式(C#)——組合模式

設計模式(C#)——組合模式

推薦閱讀:

      遊戲通常包含許多檢視。主檢視中顯示角色。有一個子檢視,顯示玩家的積分。有一個子檢視,顯示遊戲中剩下的時間。
      可維護性應該是遊戲開發過程中的主要關注點。每個檢視不應具有不同的函式名稱或不同的訪問點。相反,你想要為每個檢視提供一個統一的訪問點,即相同的函式呼叫應該既能夠訪問主檢視也能夠訪問子檢視。這種統一的接入點可以使用組合設計模式。

遊戲開發中常用的設計模式之一組合模式

      組合模式(Composite Pattern),又叫部分整體模式,是用於把一組相似的物件當作一個單一的物件。
      組合模式依據樹形結構來組合物件,用來表示部分以及整體層次。它使我們樹型結構的問題中,模糊了簡單元素和複雜元素的概念,客戶程式可以像處理簡單元素一樣來處理複雜元素,從而使得客戶程式與複雜元素的內部結構解耦。
      組合模式使得使用者對單個物件和組合物件的使用具有一致性。

組合模式的要素:

1、抽象構件(Component ):它是所有葉子構件和容器構件的共同父類,裡面聲明瞭葉子構件和容器構件的所有方法;
2、容器構件(Composite):定義有枝節點行為,用來儲存子部件,在Component介面中實現與子部件有關操作,如增加(Add)和刪除(Delete)等。
3、葉子構件(Leaf):在組合中表示葉子結點物件,葉子結點沒有子結點,對於從父類中繼承過來的容器構件的方法,由於它不能實現,可以丟擲異常;

      此模式將每個檢視放置在樹狀結構中,從而為每個檢視提供統一的訪問點。取代了需要用不同的函式來訪問不同的子檢視,組合模式可以用相同的函式訪問任何檢視。繼續使用上面的列子,使用組合模式來分解遊戲中的檢視如下:
在這裡插入圖片描述


      下面舉一個Unity中元件的層級結構的例子來幫助大家更加清除的學習組合模式。在Unity中每個GameObject物件都有一個Transform元件,這個元件提供了幾個和遊戲物件分層操作有關的方法和變數。

變數:

childCount:代表子元件數量
parent:代表父元件中的Transform物件引用

方法:

DetachChildren:解除所有子元件與本身的關聯
Find: 尋找子元件
GetChild: 使用Index的方式取回子元件
IsChildOf: 判斷某個Transform物件是否為其子元件
SetParent:設定某個Transform物件為其父元件

      再仔細分析,則可以將Unity3D的Transform類當成是一個通用類,因為它並不明顯得可以察覺出其下又被再分成“目錄節點”或是單純的“單的終端節點”

      其實應該說,Transform類完全符合組合模式的需求:“讓客戶端在操作各個物件或元件時是一致的”。
      因此對於場景上所有的遊戲物件GameObject,可以不管它們最終代表的什麼,對於所有操作都能正確反應。
下面介紹下如何使用程式碼實現:
1.建立抽象構件(IComponent):

public abstract class IComponent 
{
    protected string m_Value;

    public abstract void Operation();

    public virtual void Add(IComponent theComponent) {}
    public virtual void Remove(IComponent theComponent) { }
    public virtual IComponent GetChild(int index) 
    {
        return null;
    }
}

2、容器構件(Composite):

//節點類
public class Composite : IComponent 
{
    List<IComponent> m_Childs = new List<IComponent>();

    public Composite(string Value) 
    {
        m_Value = Value;
    }

    public override void Operation()
    {
        foreach (IComponent theComponent in m_Childs) 
        {
            theComponent.Operation();
        }
    }

    public override void Add(IComponent theComponent)
    {
        m_Childs.Add(theComponent);
    }

    public override void Remove(IComponent theComponent)
    {
        m_Childs.Remove(theComponent);
    }

    public override IComponent GetChild(int index)
    {
        return m_Childs[index];
    }
}

3、葉子構件(Leaf)

//葉子類
public class Leaf : IComponent 
{
    public Leaf(string Value) 
    {
        m_Value = Value;
    }

    public override void Operation(){}
}

4.使用組合模式:

//測試類
public class TestComposite 
{
    void UnitTest() 
    {
        IComponent theRoot = new Composite("Root");

        theRoot.Add(new Leaf("Leaf1"));
        theRoot.Add(new Leaf("Leaf2"));

        IComponent theChild1 = new Composite("Child1");
        theChild1.Add(new Leaf("Child1.Leaf1"));
        theChild1.Add(new Leaf("Child1.Leaf2"));
        theRoot.Add(theChild1);

        IComponent theChild2 = new Composite("Child2");
        theChild2.Add(new Leaf("Child2.Leaf1"));
        theChild2.Add(new Leaf("Child2.Leaf2"));
        theChild2.Add(new Leaf("Child2.Leaf3"));
        theRoot.Add(theChild2);

        theRoot.Operation();
    }
}

總結

優點:

介面與功能分離,更具移植性
工作切分更容易,當指令碼移除,就可以讓UI設計交由美術和企劃組裝
介面更改不影響專案:只要維持元件名稱不變,介面的更改就不容易影響到遊戲現有程式功能的執行。

缺點:

元件名稱重複,如果沒有將層級切分好,就容易出現該問題,在工具名稱新增警告可以解決。
元件更名不易:元件名需要通過字串來查詢,介面元件一旦不能獲取,則會出現null值,和不正確的場景,應對的方法同樣是在UnityTool中新增查詢失敗的警告。