當前位置:成語大全網 - 新華字典 - 在Unity3D中基於訂閱者模式怎樣實現事件機制

在Unity3D中基於訂閱者模式怎樣實現事件機制

我們知道通過在Unity3D中通過GetComponent就可以獲得某個模塊的實例,進而引用這個實例完成相關任務的調用。可是顯然這種方法,就像我們隨身帶著現金去和不同的人進行交易,每次交易的時候都需要我們考慮現金的支入和支出問題,從安全性和耦合度兩個方面進行考慮,這種方法在面對復雜的系統設計的時候,非常容易造成模塊間的相互依賴,即會增加不同模塊間的耦合度。為了解決這個問題,大家開始考慮單例模式,因為單例模式能夠保證在全局內有壹個唯壹的實例,所以這種方式可以有效地降低模塊間的直接引用。單例模式就像是我們在銀行內辦理了壹個唯壹的賬戶,這樣我們在交易的時候只需要通過這個賬戶來進行控制資金的流向就可以了。單例模式確保了各個模塊間的獨立性,可是單例模式更多的是壹種主動行為,即我們在需要的時候主動去調用這個模塊,單例模式存在的問題是無法解決被調用方的反饋問題,除非被調用方主動地去調用調用方的模塊實例。說到這裏我們好像看到了壹種新的模式,這就是我們下面要提到的事件機制。

訂閱者模式和事件機制

首先這裏要提到壹種稱為“訂閱者模式”的設計模式,這種設計模式在《大話設計模式》這本書中稱為“觀察者模式”或者“發布-訂閱(Publish/Subscribe)模式”,我們這裏暫且叫做“訂閱者模式”吧!該模式定義了壹種壹對多的依賴關系,讓多個觀察者對象同時監聽某壹個主題對象。這個對象在狀態發生變化時會通知所有觀察者對象,使它們能夠自動更新自己。針對這個模式,我們可以考慮事件機制的實現,事件機制可以理解為在壹個事件中心(Subject)保存有對所有事件(Observer)的引用,事件中心負責對這些事件進行分發,這樣每個事件就可以通過回調函數的方式進行更新,這樣就實現了壹個事件機制。下面給出基本的代碼實現:

using System;

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

namespace UniEventDispatcher

{

/// <summary>

/// 定義事件分發委托

/// </summary>

public delegate void OnNotification(Notification notific);

/// <summary>

///通知中心

/// </summary>

public class NotificationCenter

{

/// <summary>

/// 通知中心單例

/// </summary>

private static NotificationCenter instance=null;

public static NotificationCenter Get()

{

if(instance == null){

instance = new NotificationCenter();

return instance;

}

return instance;

}

/// <summary>

/// 存儲事件的字典

/// </summary>

private Dictionary<string,OnNotification> eventListeners

= new Dictionary<string, OnNotification>();

/// <summary>

/// 註冊事件

/// </summary>

/// <param name="eventKey">事件Key</param>

/// <param name="eventListener">事件監聽器</param>

public void AddEventListener(string eventKey,OnNotification eventListener)

{

if(!eventListeners.ContainsKey(eventKey)){

eventListeners.Add(eventKey,eventListener);

}

}

/// <summary>

/// 移除事件

/// </summary>

/// <param name="eventKey">事件Key</param>

public void RemoveEventListener(string eventKey)

{

if(!eventListeners.ContainsKey(eventKey))

return;

eventListeners[eventKey] =null;

eventListeners.Remove(eventKey);

}

/// <summary>

/// 分發事件

/// </summary>

/// <param name="eventKey">事件Key</param>

/// <param name="notific">通知</param>

public void DispatchEvent(string eventKey,Notification notific)

{

if (!eventListeners.ContainsKey(eventKey))

return;

eventListeners[eventKey](notific);

}

/// <summary>

/// 分發事件

/// </summary>

/// <param name="eventKey">事件Key</param>

/// <param name="sender">發送者</param>

/// <param name="param">通知內容</param>

public void DispatchEvent(string eventKey, GameObject sender, object param)

{

if(!eventListeners.ContainsKey(eventKey))

return;

eventListeners[eventKey](new Notification(sender,param));

}

/// <summary>

/// 分發事件

/// </summary>

/// <param name="eventKey">事件Key</param>

/// <param name="param">通知內容</param>

public void DispatchEvent(string eventKey,object param)

{

if(!eventListeners.ContainsKey(eventKey))

return;

eventListeners[eventKey](new Notification(param));

}

/// <summary>

/// 是否存在指定事件的監聽器

/// </summary>

public Boolean HasEventListener(string eventKey)

{

return eventListeners.ContainsKey(eventKey);

}

}

}

註意到在這個“通知中心”中,我們首先實現了單例模式,這樣我們可以通過Get方法來獲取該“通知中心”的唯壹實例,其次這裏利用壹個字典來存儲對所有事件的引用,這樣保證外部可以通過AddEventListener和RemoveEventListener這兩個方法來進行事件的添加和移除,對於添加的事件引用我們可以通過DispatchEvent方法來分發壹個事件,事件的回調函數采用委托來實現,註意到這個委托需要壹個Notification類型,對該類型簡單定義如下:

using System;

using UnityEngine;

namespace UniEventDispatcher

{

public class Notification

{

/// <summary>

/// 通知發送者

/// </summary>

public GameObject sender;

/// <summary>

/// 通知內容

/// 備註:在發送消息時需要裝箱、解析消息時需要拆箱

/// 所以這是壹個糟糕的設計,需要註意。

/// </summary>

public object param;

/// <summary>

/// 構造函數

/// </summary>

/// <param name="sender">通知發送者</param>

/// <param name="param">通知內容</param>

public Notification(GameObject sender, object param)

{

this.sender = sender;

this.param = param;

}

/// <summary>

/// 構造函數

/// </summary>

/// <param name="param"></param>

public Notification(object param)

{

this.sender = null;

this.param = param;

}

/// <summary>

/// 實現ToString方法

/// </summary>

/// <returns></returns>

public override string ToString()

{

return string.Format("sender={0},param={1}", this.sender, this.param);

}

}

}