观察者模式和委托事件
观察者模式
定义
观察者模式(Observer Pattern)——在对象之间定义了一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象会收到通知并自动更新。
具体的说目的是 “类A想在类B变化的时候得到消息,从而跟着变化”
最简单的方式,双向关联:
类A知道类B,并在一定时期将自身告诉类B
类B持有类A的对象,并在自身变化的时候调用类A的方法
这种方式不好的地方在于造成了双向关联,但类A想知道类B,类B其实并不想知道类A
观察者模式是这样的
类B不再关心是类A还是类C、类D等等需要监听,类B只知道有类需要被通知消息,他把这些类叫做观察者,于是类B持有一个“观察者队列”,并提供一个注册的方法
类A继承观察者接口,在需要的时候注册自己,并实现接口中被通知的方法
类B在自身变化时依次通知队列中的所有人
UML
代码
首先定义一个观察者接口,有一个接收通知的方法
namespace DesignModel.ObserverPattern
{
public interface IObserver
{
void Notify();
}
}
然后是我们被观察的类
namespace DesignModel.ObserverPattern
{
public class Subject
{
//观察者队列
private List<IObserver> container = new List<IObserver>();
//观察者注册方法
public void RegisterObserver(IObserver observer)
{
if (observer != null && !container.Contains(observer))
{
container.Add(observer);
}
}
//观察者移除方法
public void RemoveObserver(IObserver observer)
{
if (container.Contains(observer))
{
container.Remove(observer);
}
}
//通知
private void NotifyObserver()
{
foreach (var item in container)
{
item.Notify();
}
}
//在某种情况下发出通知
public void OnClick()
{
NotifyObserver();
}
}
}
以及观察者的实现
namespace DesignModel.ObserverPattern
{
public class ConcreteObserver :IObserver
{
private Subject subject;
public ConcreteObserver(Subject subject)
{
this.subject = subject;
//向subject注册自己
subject.RegisterObserver(this);
}
public void Notify()
{
Debug.Log("收到通知!");
}
}
}
简单测试
using DesignModel.ObserverPattern;
public class Client_Observer_Example1 : MonoBehaviour
{
void Start ()
{
Subject subject = new Subject();
ConcreteObserver observer = new ConcreteObserver(subject);
subject.OnClick();
}
}
观察者模式的总结
优点:
1.将被观察者的依赖从具体的类变为依赖抽象,从而使得被观察者不认识任何一个观察者而仅知道观察者接口,降低了耦合
2.观察者模式可以支持广播,使得被观察者可以通知到每一个观察者
缺点:
1.需要注意观察者与被观察者之间不能有循环依赖,否则容易导致程序卡住
2.如果有太多的观察者,则通知每一个观察者需要耗费大量时间
委托事件
定义
C#为我们提供了更方便的通知模式,即委托和事件
当一个类作为被观察者,有通知观察者的需要的情况下,他只需要定义一个委托事件,并在需要时调用委托事件即可
作为观察者,需要知道被观察者,并将自己的处理通知的方法添加到事件中去即可
委托
delegate字段定义委托,相当于一个委托类型定义
事件
event字段定义事件,相当于一个委托类型的回调函数对象
不使用event字段,依然可以定义delegate委托类型的对象,但这是不安全的,被event字段标记的属性对象,只能通过±来添加或移除注册函数,而不能被赋值
代码
定义被观察者
public class NotifySender
{
//委托
public delegate void TestHandler();
//事件
public event TestHandler testEvent;
public void TestEvent()
{
if (testEvent != null)
testEvent();
}
}
观察者
public class NotifyListener
{
private NotifySender sender;
public NotifyListener(NotifySender sender)
{
this.sender = sender;
//注册事件
this.sender.testEvent += Notify;
}
private void Notify()
{
Debug.Log("收到通知!");
}
}
客户端
public class Client_Observer_Delegate_Event:MonoBehaviour
{
private void Start()
{
NotifySender sender = new NotifySender();
NotifyListener listener = new NotifyListener(sender);
sender.TestEvent();
}
}
消息中心
利用委托事件实现一个消息中心,方便页面之间通信
数据模型和常量
namespace DesignModel.ObserverPattern
{
public class NotificationModel
{
public string name;
public object[] param;
}
public class NotifyConst
{
public const string TEST = "test";
public const string TEST_PARAM = "test_param";
}
}
消息中心
namespace DesignModel.ObserverPattern
{
public class NotificationCenter
{
//单例
private static NotificationCenter instance;
public static NotificationCenter Instance
{
get
{
if (instance == null)
instance = new NotificationCenter();
return instance;
}
}
//委托
public delegate void NotifyHandler(NotificationModel model);
//消息字典
private Dictionary<string, NotifyHandler> notifications;
private NotificationCenter()
{
notifications = new Dictionary<string, NotifyHandler>();
}
/// <summary>
/// 注册监听
/// </summary>
/// <param name="notifyName">要监听的消息名称</param>
/// <param name="handler">监听函数</param>
public void RegisterNotification(string notifyName, NotifyHandler handler)
{
if (!notifications.ContainsKey(notifyName))
{
NotifyHandler notification = null;
notifications.Add(notifyName, notification);
}
notifications[notifyName] += handler;
}
/// <summary>
/// 移除监听
/// </summary>
/// <param name="notifyName">移除的消息名称</param>
/// <param name="handler">要移除的监听函数</param>
public void RemoveNotification(string notifyName,NotifyHandler handler)
{
if (notifications.ContainsKey(notifyName))
{
notifications[notifyName] -= handler;
if (notifications[notifyName] == null)
{
notifications.Remove(notifyName);
}
}
}
/// <summary>
/// 发送消息
/// </summary>
/// <param name="notifyName">消息名称</param>
public void SendNotification(string notifyName)
{
SendNotification(notifyName, null);
}
/// <summary>
/// 发送消息
/// </summary>
/// <param name="notifyName">消息名称</param>
/// <param name="objs">参数列表</param>
public void SendNotification(string notifyName, params object[] objs)
{
if (notifications.ContainsKey(notifyName))
{
NotificationModel model = new NotificationModel();
model.name = notifyName;
model.param = objs;
notifications[notifyName](model);
}
}
}
}
使用测试
//监听类
public class Client_Observer_Listener : MonoBehaviour
{
private void Awake()
{
RegiserNotify();
}
//注册监听
private void RegiserNotify()
{
NotificationCenter.Instance.RegisterNotification(NotifyConst.TEST, NotifyMethod);
NotificationCenter.Instance.RegisterNotification(NotifyConst.TEST_PARAM, NotifyMethod);
}
//监听函数
private void NotifyMethod(NotificationModel model)
{
switch(model.name)
{
case NotifyConst.TEST:
{
ShowMessage(model);
}
break;
case NotifyConst.TEST_PARAM:
{
ShowMessage(model);
}
break;
}
}
private void ShowMessage(NotificationModel model)
{
Debug.Log(model.name + ":");
if (model.param != null)
{
for (int i = 0; i < model.param.Length; i++)
{
Debug.Log(model.param[i]);
}
}
}
}
//发送消息的类
public class Client_Observer_Sender : MonoBehaviour
{
void Start ()
{
//无参消息
NotificationCenter.Instance.SendNotification(NotifyConst.TEST);
//带参消息
NotificationCenter.Instance.SendNotification(NotifyConst.TEST_PARAM, "param 1", "param 2");
}
}
我们可以将注册过程抽象为一个接口,或者用模板方法模式在抽象中完成注册过程,从而简化使用
消息中心可以将监听对象和监听者完全解藕