观察者模式(Observer Pattern),定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
别名
依赖(Dependents),发布-订阅(Publish/Subscribe)
结构
参与者
Subject(目标)
——目标知道它的观察者。可以有任意多个观察者观察同一个目标。
——提供注册和删除观察者对象的接口。
Observer(观察者)
——为那些在目标发生改变时需获得通知的对象定义一个更新接口。
ConcreteSubject(具体目标)
——将有关状态存入各CreateObserver对象。
——当它的状态发生改变,向它的各个观察者发出通知。
ConcreteObject(具体观察者)
——维护一个指向ConcreteSubject对象的引用。
——存储有关状态,这些状态应与目标的状态保持一致。
——实现Observer的更新接口以使自身状态与目标的状态保持一致。
协作
代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyObserver
{
class Subject
{
IList<Observer> observers = new List<Observer>();
public void Attach(Observer o)
{
observers.Add(o);
}
public void Detach(Observer o)
{
observers.Remove(o);
}
public void Nofity()
{
foreach (Observer o in observers)
{
o.Update();
}
}
}
class ConcreteSubject : Subject
{
private string subjectState;
public string SubjectState
{
get { return subjectState; }
set { subjectState = value; }
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyObserver
{
interface Observer
{
void Update();
}
class ConcreteObserver : Observer
{
private string name;
private string observerState;
// ConcreteSubject对象的引用
private ConcreteSubject cs;
public ConcreteObserver(ConcreteSubject cs, string name)
{
this.name = name;
this.cs = cs;
}
public void Update()
{
// 获取目标状态,用于自身状态的更新
observerState = cs.SubjectState;
Console.WriteLine("观察值{0}的新状态是{1}", name, observerState);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyObserver
{
class Program
{
static void Main(string[] args)
{
ConcreteSubject s = new ConcreteSubject();
s.Attach(new ConcreteObserver(s, "X"));
s.Attach(new ConcreteObserver(s, "Y"));
s.Attach(new ConcreteObserver(s, "Z"));
s.SubjectState = "ABC";
s.Nofity();
Console.ReadKey();
}
}
}
效果
1) 目标和观察者间的抽象耦合
2)支持广播通信
3)意外的更新
实现
1) 创建目标到其观察者之间的映射 一个目标对象跟踪它应通知的观察者的最简单的方法是显式地在目标中保存对它们的引用。然而,当目标很多而观察者较少时,这样存储可能代价太高。一个解决办法是用时间换空间,用一个关联查找机制 (例如一个hash表来维护目标到观察者的映射。这样一个没有观察者的目标就不产生存储开销。但另一方面,这一方法增加了访问观察者的开销。
2)谁触发更新 Notify不总是由目标对象调用,它也可被一个观察者或其它对象调用。
3)在发出通知前确保目标的状态自身是一致的。
4)避免特定于观察者的更新协议—推/拉模型(push model/pull model)
5) 显式地指定感兴趣的改变 你可以扩展目标的注册接口 ,让各观察者注册为仅对特定事件感兴趣,以提高更新的效率。
void Subject::Attach(Observer*, Aspect& interest);
void Observer::Update(Subject*, Aspect& interest);
All is well!,下期事件与委托