一、观察者模式总览
观察者模式定义了对象之间的一对多依赖,让多个观察者对象同时监听一个主题对象,当主题对象改变状态时,它的所有依赖者都会收到通知并自动更新。
UML类图如下:
Subject为主题抽象对象,Observer为观察者抽象对象,主题对象中规定了“订阅”、“取消订阅”、“通知”等必要的方法。所以这是多个观察者依赖同一个主题对象,是一种多对一的依赖。
至于实现了主题和观察者接口的实体对象,实体观察者之所以要关联实体主题,是因为实体观察者要从实体主题那里拿到需要的数据。
观察者公开了Update()方法让主题在需要的时候调用,从而通知观察者改变状态。另一方面,观察者关联了主题,可以从主题拿到需要的数据。
使用这种结构,使主题与观察者之间的耦合降到最低,观察者可以随时对主题订阅或取消订阅,而且一个系统的观察者可以同时成为另一个系统的主题。
关于数据的传递,站在观察者的角度来看,有“推(push)”和“拉(pull)”两种模式。
push模式,是在主题调用Update通知观察者时,把数据全部传递给观察者,这是一种从上至下的模式,简单暴力,屏蔽了不同观察者之间的个性,所有观察者接收同样的通知和数据。
pull模式,主题调用Update只是起到通知的作用,具体获取数据的方式由各个观察者实体决定,相对push模式,这种模式更加灵活。
二、C#中的观察者模式
C#中用到观察者模式的机制有:事件、IObsverableIObserver接口
1.事件:
如下为简单的示例代码,可以模仿了观察者模式的命名
//主题
class Subject6
{
public delegate void NotifyHandler(int param);
public event NotifyHandler Notify;
public void SetParam(int i)
{
Notify(i);
}
}
//观察者class Observer6
{
string name;
public Observer6(string name)
{
this.name = name;
}public void Update(int i)
{
Console.WriteLine(name + "--" + i);
}
}//控制台调用代码
static void Main(string[] args)
{
Subject6 s6 = new Subject6();
s6.Notify += new Observer6("A").Update;
s6.Notify += new Observer6("B").Update;s6.SetParam(1);
s6.SetParam(2);
Console.ReadKey();
}运行结果:
A--1
B--1
A--2
B--22.IObsverableIObserver接口
MSDN用了GPS的例子https://msdn.microsoft.com/zh-cn/library/dd990377(v=vs.100).aspx
类图如下
//控制台调用代码
static void Main(string[] args)
{LocationTracker provider = new LocationTracker();
LocationReporter reporter1 = new LocationReporter("GPS1");
LocationReporter reporter2 = new LocationReporter("GPS2");
reporter1.Subscribe(provider);
reporter2.Subscribe(provider);provider.TrackLocation(new Location(1, 2));
provider.TrackLocation(new Location(2,4));
provider.EndTransmission();
Console.ReadKey();
}Unsubscriber是LocationTracker的内部类,在订阅的时候返回给订阅者Observer,Unsubscriber用于取消订阅,这样就相当于把取消订阅的权利交给了订阅者自身。
此外,与前面的几种实现方式不同的是,观察者主动订阅主题(reporter1.Subscribe(provider)),而不是主题添加观察者。reporter1.Subscribe的细节是这样的:
public virtual void Subscribe(IObservable<Location> provider) { if (provider != null) unsubscriber = provider.Subscribe(this); }相当于多做了一层封装。