設計模式(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中新增查詢失敗的警告。