观察者模式又叫发布-订阅(Publish/Subscribe)模式
动机(Motivation)
在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系”——一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。
使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。
意图(Intent)
定义对象间的一种一对多的依赖关系,让多个观察者对象以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
结构(Structure)
1.Subject类,它把所有对观察者对象的引用保存在一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
2.ConcreteSubject类:具体主题,将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。
3.Observer类:抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己
4.ConcreteObserver类:具体观察者,实现抽象观察者角色所要求的更新接口,以便使自身的状态与主题的状态相协调
代码实现
实例:小张在上班时间看股票行情,小刘在看NBA直播,为了不让老板回来的时候发现,摆脱前台的秘书,当老板回来的时候通风报信。有一次,秘书因为被老板叫去没有高速小张和小刘,不幸地,正在看股票的小张被老板发现了。
在这个实例当中,老板和秘书都是通知者(被老板发现可以看做老板亲自通知),小张和小刘是观察者,根据通知,小张和小刘决定是否采取行动。
通知者接口:Subject类,抽象主题提供一个接口,可以增加和删除观察者对象。
interface Subject
{
void Attach(Observer observer);
void Detach(Observer observer);
void Notify();
string SubjectState
{
get;
set;
}
}
具体的通知者类:ConcreteSubject类具体主题,在具体主题的内部状态改变时,给所有登记过的观察者发出通知。(本例中,具体的通知者类可能是前台,也可能是老板,他们也许有各自的一些方法,但是对于通知类来说,他们是一样的,所以它们都去实现通知者这个接口)
class Boss : Subject
{
//同事列表
private IList<Observer> observers = new List<Observer>();
private string action;
//增加
public void Attach(Observer observer)
{
observers.Add(observer);
}
//减少
public void Detach(Observer observer)
{
observers.Remove(observer);
}
//通知
public void Notify()
{
foreach (Observer o in observers)
{
o.Update();
}
}
//老板状态
public string SubjectState
{
get { return action; }
set { action = value; }
}
}
前台秘书类同老板类,代码省略。
抽象观察者:Observer类者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己
abstract class Observer
{
protected string name;
protected Subject sub;
public Observer(string name, Subject sub)
{
this.name = name;
this.sub = sub;
}
public abstract void Update();
}
看股票的同时和看NBA的同事: ConcreteObserver类,具体观察者,实现抽象观察者角色所要求的更新接口。(本例中,NBAObserver和 StockObserve实现Observer接口,并且根据通知采取行动关闭股票行情,继续工作或关闭NBA直播,继续工作)
//看股票的同事
class StockObserver:Observer
{
public StockObserver(string name, Subject sub) : base(name, sub) { }
public override void Update()
{
Console.WriteLine("{0}{1}关闭股票行情,继续工作!",sub.SubjectState,name);
}
}
//看NBA的同事
class NBAObserver : Observer
{
//关闭NBA直播,继续工作
}
客户端代码
实例化出来三个对象:张老板,看股票的同事小张,看NBA直播的同事小刘
Boss zhangBoss = new Boss();
StockObserver colleague1 = new StockObserver("同事小张", zhangBoss);
NBAObserver colleague2 = new NBAObserver("同事小刘", zhangBoss);
增加两个观察者对象:同事小张和同事小刘,小张被老板发现了,从观察者中删除
zhangBoss.Attach(colleague1);
zhangBoss.Attach(colleague2);
zhangBoss.Detach(colleague1);
老板回来发出通知
zhangBoss.SubjectState = "老板我回来了!";
zhangBoss.Notify();
运行结果
observer模式的几个要点
1.使用面向对象的抽象,Observer模式使得我们可以独立地改变目标和观察者,从而使二者之间的依赖关系达到松耦合。
2.目标发送通知时,无需指定观察者,通知(可以携带通知信息作为参数)会自动传播。观察者自己决定是否需要订阅通知,目标对象对此一无所知。
3.在C#的event中,委托充当了抽象的Observer接口,而提供事件的对象充当了目标对象。委托是比抽象Observer接口更为松耦合的设计。