组件模式代码实践(C#版本)
此测试代码包含3个基本类:
class EntityBase:实体基类
class ComponentBase:组件基类
class Event:消息类
组合模式的关键在于:(1)实体通过组件实现功能,组件依赖于实体而存在;(2)通过事件驱动组件的各种行为。
EntityBase代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace TestComponentModel { class EntityBase { // 组件列表,未实现RTTI,故用string做索引 // 如果有RTTI,可以直接存储List<ComponentBase>,并且大多是这么做的 Dictionary<string, ComponentBase> ComponentArray = new Dictionary<string, ComponentBase>(); // 通过此函数将消息分发给各个Component,各个Component各自处理此消息 // 通过DisptachEvetn,每个Compoent都会收到此消息,但是否处理此消息,由各组件自己决定 public virtual bool DispatchEvent(Event evt) { bool bHandled = false; foreach (ComponentBase comp in ComponentArray.Values) { bool ret = comp.OnEvent(evt); bHandled = (bHandled || ret); } if (bHandled == false) Console.WriteLine("Receive an unhandled event: id = " + evt.EventID.ToString()); return bHandled; } public bool AddComponent(string compName, ComponentBase compObject) { if (compObject == null) { Console.Write("AddComponent Failed, Component.Name=" + compName); return false; } ComponentArray.Add(compName, compObject); compObject.OnAttachToEntity(this); OnComponentAttached(compObject); return true; } public bool RemoveComponent(string compName) { if (!ComponentArray.ContainsKey(compName)) { Console.Write("RemoveComponent Failed, Component.Name=" + compName); return false; } ComponentArray[compName].OnDetachFromEntity(this); OnComponentDetached(ComponentArray[compName]); ComponentArray.Remove(compName); return true; } public virtual void OnComponentAttached(ComponentBase compObject) { } public virtual void OnComponentDetached(ComponentBase compObject) { } } }
ComponentBase代码:
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; namespace TestComponentModel { class ComponentBase { protected delegate void EventHandleFunction(Event evt); private EntityBase Owner = null; // 事件映射表 private Dictionary<int, EventHandleFunction> EventHandlerMap = new Dictionary<int, EventHandleFunction>(); public virtual bool OnEvent(Event evt) { EventHandleFunction function = null; if(EventHandlerMap.TryGetValue(evt.EventID, out function) == false) { return false; } if (function == null) return false; function(evt); return true; } public virtual void OnAttachToEntity(EntityBase ety) { Owner = ety; } public virtual void OnDetachFromEntity(EntityBase ety) { Owner = null; } // 注册事件处理函数,同一事件注册两次,则后面的覆盖前面的 protected void RegisterEvent(int eventID, EventHandleFunction handlerFunc) { if (EventHandlerMap.ContainsKey(eventID)) EventHandlerMap.Remove(eventID); EventHandlerMap.Add(eventID, handlerFunc); } protected void UnregiterEvent(int eventID) { if (EventHandlerMap.ContainsKey(eventID)) EventHandlerMap.Remove(eventID); } } }
Event代码:
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; namespace TestComponentModel { class Event { public int EventID = -1; public ArrayList EventArgs = new ArrayList(); public void PushUserData<T>(T data) { EventArgs.Add(data); } public T GetUserData<T>(int index) { if (index >= EventArgs.Count) { Console.Write("GetUserData Error!"); return default(T); } return (T)EventArgs[index]; } } }
测试代码包括Main.cs、一个继承自ComponentBase的组件TestComponent和一个事件定义的类EventDefines。
EventDefines代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace TestComponentModel { public enum EventDefines { Event_01 = 0, Event_02, Event_03, } }
TestComponent代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace TestComponentModel { class TestComponent : ComponentBase { public override void OnAttachToEntity(EntityBase ety) { base.OnAttachToEntity(ety); // 注册事件响应函数 RegisterEvent((int)EventDefines.Event_01, DealEvent01); RegisterEvent((int)EventDefines.Event_03, DealEvent03); } public override void OnDetachFromEntity(EntityBase ety) { base.OnDetachFromEntity(ety); } private void DealEvent01(Event evt) { string evtData = evt.GetUserData<string>(0); Console.WriteLine("TestComponent Handle Event: ID = " + evt.EventID.ToString()); Console.WriteLine("TestComponent Handle Event: Data = " + evtData); } private void DealEvent03(Event evt) { float evtData = evt.GetUserData<float>(0); Console.WriteLine("TestComponent Handle Event: ID = " + evt.EventID.ToString()); Console.WriteLine("TestComponent Handle Event: Data = " + evtData); } } }
Main.cs代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace TestComponentModel { class Program { static void Main(string[] args) { // 构造一个EntityBase对象 EntityBase ety = new EntityBase(); // 添加TestComponent组件,可以移到EntityBase的继承类的构造函数中 TestComponent testComp = new TestComponent(); ety.AddComponent("TestComponent", testComp); // 发送消息Event_01 Event evt1 = new Event(); evt1.EventID = (int)EventDefines.Event_01; evt1.PushUserData<string>("SomeData For Event01"); ety.DispatchEvent(evt1); // 发送消息Event_02 Event evt2 = new Event(); evt2.EventID = (int)EventDefines.Event_02; evt2.PushUserData<string>("SomeData For Event02"); ety.DispatchEvent(evt2); // 发送消息Event_03 Event evt3 = new Event(); evt3.EventID = (int)EventDefines.Event_03; evt3.PushUserData<float>(0.123f); ety.DispatchEvent(evt3); Console.Read(); } } }
运行结果截图:
组合模式是实用性很强的一种设计模式,目前在各种游戏代码中广泛存在,熟练运用此模式可以使代码简洁高效、可读易修改并且查错方便。
本例子所提供的代码只是测试代码,若想应用于项目,需根据实际情况进行一些修改。不过,改来改去,思想始终不变。
PS:所谓模式,即代码的组织方式,模式源于实践和总结。同时,模式也是一种思想,是人们对于程序对象的一种认识和剖析...