观察者(Observer)模式,又称依赖(Dependents)模式,发布-订阅(Publish-Subscribe)模式,或者监听者(Listener)模式。它定义了对象间的一种一对多的依赖关系,当对象的状态发生改变时,所有依赖于它的对象都会得到通知并且自动更新。
IOS里的定义了NSNotification来实现观察者模式(NSNotificationCenter是观察者模式和中介者模式的融合,而且还是个单例,它负责集中管理通知者和观察者)。
而C#里定义了delegate和event这种广播委托/事件来实现观察者模式(参考C#语法小知识(四)委托delegate和C#语法小知识(九)事件),这种方式非常易用且好用。但是我们不妨把这些放在一边,自己来探究观察者模式的实现。
首先我们定义一个观察者的接口:
public interface IObserver{
void ReceiveEvent (string eventName, object obj);
}
和通知者的类:
public class Notification
{
protected Dictionary<string, List<IObserver>> _observers = new Dictionary<string, List<IObserver>>();
public virtual void AddObserver(IObserver obs, string eventName)
{
if (!_observers.ContainsKey (eventName)) {
_observers [eventName] = new List<IObserver> ();
}
_observers [eventName].Add (obs);
}
public virtual void RemoveObserver(IObserver obs, string eventName)
{
if (!_observers.ContainsKey (eventName)) {
return;
}
_observers [eventName].Remove (obs);
}
public virtual void RemoveAllEvents(IObserver obs)
{
foreach (var kv in _observers) {
kv.Value.Remove (obs);
}
}
public virtual void PostNotification(string eventName, object obj)
{
if (!_observers.ContainsKey (eventName)) {
return;
}
List<IObserver> obsList = _observers [eventName];
foreach (IObserver obs in obsList) {
obs.ReceiveEvent (eventName, obj);
}
}
}
我们不妨定义一个邮箱的类作为实例:
public class MailBox : Notification
{
public void ReceiveMail(string mail)
{
PostNotification ("MAIL_RECEIVED", mail);
}
}
收到邮件的时候,发送事件出去。添加一个继承自IObserver的邮箱观察者:
public class MailObserver : IObserver
{
public void ReceiveEvent (string eventName, object obj)
{
Console.WriteLine ("[" + eventName + "]" + obj);
}
}
使用:
MailBox mb = new MailBox ();
MailObserver mo1 = new MailObserver ();
MailObserver mo2 = new MailObserver ();
mb.AddObserver (mo1, "MAIL_RECEIVED");
mb.AddObserver (mo2, "MAIL_RECEIVED");
mb.ReceiveMail ("Hello");
这样邮箱观察者就可以收到“MAIL_RECEIVED”事件,并处理邮件内容了。
如果你觉得让邮箱继承Notification这种方式不好,那么我们也可以实现一个IOS的NSNotificationCenter风格的通知中心:
public sealed class NotificationCenter
{
private static NotificationCenter _center = new NotificationCenter();
public static NotificationCenter defaultCenter
{
get {
return _center;
}
}
Dictionary<object, Notification> _notifications = new Dictionary<object, Notification>();
public void AddObserver(object notifier, IObserver obs, string eventName)
{
if (!_notifications.ContainsKey (notifier)) {
_notifications [notifier] = new Notification();
}
_notifications [notifier].AddObserver (obs, eventName);
}
public void RemoveObserver(object notifier, IObserver obs, string eventName)
{
if (!_notifications.ContainsKey (notifier)) {
return;
}
_notifications [notifier].RemoveObserver(obs, eventName);
}
public void RemoveAllEvents(object notifier, IObserver obs)
{
if (!_notifications.ContainsKey (notifier)) {
return;
}
_notifications [notifier].RemoveAllEvents (obs);
}
public void RemoveAllObservers(object notifier)
{
_notifications [notifier] = null;
}
public void PostNotification(object notifier, string eventName, object obj)
{
if (!_notifications.ContainsKey (notifier)) {
return;
}
_notifications [notifier].PostNotification (eventName, obj);
}
}
在MailBox的ReceiveMail方法里添加:
NotificationCenter.defaultCenter.PostNotification (this, "MAIL_RECEIVED", mail);
使用:
NotificationCenter.defaultCenter.AddObserver (mb, mo1, "MAIL_RECEIVED");
NotificationCenter.defaultCenter.AddObserver (mb, mo2, "MAIL_RECEIVED");
mb.ReceiveMail ("World");
当然,对于C#来讲最简单的还是delegate或者event:
public class NewMailBox
{
public delegate void MailReceived(object msg);
public MailReceived mailReceivedDelegate;
public void ReceiveMail(string mail)
{
mailReceivedDelegate.Invoke (mail);
}
}
public class NewMailObserver
{
public void ReceiveEvent (object obj)
{
Console.WriteLine ("[Mail received]" + obj);
}
}
使用新的邮箱和邮件观察者,还可以使用Lamda表达式:
NewMailBox nmb = new NewMailBox ();
NewMailObserver nmo = new NewMailObserver ();
nmb.mailReceivedDelegate = nmo.ReceiveEvent;
nmb.mailReceivedDelegate += (object mail) => {
Console.WriteLine (mail);
};
nmb.ReceiveMail ("New");
观察者模式的优点:
1、实现了目标和观察者的抽象耦合
2、支持广播通信
缺点:因为一个观察者并不知道其他观察者的存在,它可能对改变目标的最终代价一无所知,在目标上一个看似无害的操作可能会引起一系列观察者以及依赖于观察者的对象的更新。并且如果依赖准则定义或维护不当,容易引起错误的更新,并且难以排错。
而且维护观察者的时候需要额外注意,当观察者需要析构的时候,要提前移除监听,否则会出错。