观察者模式
定义
观察者模式又叫做“发布-订阅(Publish/Subcribe)”模式。
注:有文章会问二者的区别,我个人认为本质是一样的,发布-订阅 是对观察者模式的升级。
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,便它们能够自动更新自己。
动机/目的
将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。而观察者模式的关系对象是主题和观察者,一个主题可以有任意数目的依赖它的观察者,一旦主题的状态发生了改变,所有的观察者都可以得到通知。
何时使用
- 当一个对象的改变需要同时改变其他对象时(一方面依赖于另一方面),且它不知道具体有多少对象有待改变,用观察者模式可以将这两者封装在独立的对象中使它们各自独立的改变和复用。
- 观察者模式所做的工作就是在解除耦合,让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自变化都不会影响到另一边的变化。
- 一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
使用实例
- VS点击“运行”,整个窗体发生的变化。
- 拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。
- 上课铃响,同学们回教室。
缺点
- 观察者要获得被观察对象,然后才能注册。
有时只是要知道某个事件发生了而已,类似网络初始化好了的事件,并不需要获得网络管理对象。 - 观察者和被观察者要继承对象的,在单继承体系里,这是很昂贵的一件事。
类图
代码
主题类,它把所有对观察者对象的引用保存在一个聚集里,每个主题都可以有任何数量的观察者。
public interface ISubject
{
void Attach(IObserver observer);
void Detach(IObserver observer);
void Notify();
string SubjectState { get; set; }
}
具体主题类,在主题状态变更时通知更新
public void Notify()
{
foreach (var item in observers)
{
item.Update();
}
}
观察者,实现更新,便自身的状态与主题的状态相协调。
public class StockObserver : IObserver
{
private string Name { get; set; }
private ISubject Subject { get; set; }
public StockObserver(string name, ISubject subject)
{
Name = name;
Subject = subject;
}
public void Update()
{
Console.WriteLine("{0},{1}关闭股票行情,继续工作!", Subject.SubjectState, Name);
}
}
升级一事件通知
- 事件发生后,通知所有关心这个事件的对象。
- 与观察者模式对比,可理解成所有对象都只依赖事件系统。一半对象观察事件系统,等待特定通知;一半对象状态变化就通过事件系统发出事件。
- 观察者也不依赖被观察对象,他只关心事件,不需要到被观察对象那儿注册自己。
- 被观察者也只是普通对象,状态改变,通过事件系统发出事件就行了。
- 先有观察者模式,再有了事件通知。
只发出通知,不关心观察者
public void Notify()
{
Update();
}
注册更新
NbaObserver nbaObserver = new NbaObserver("小王", boss);
boss.Update += new ObserverDemo.Demo2.EventHandler(nbaObserver.CloseNBATv);
升级二消息队列
- 将消息排成队列,逐步分发通知。
- 与事件通知对比,可理解成事件不是立即通知,而是保存到队列里,稍后通知。
- 这个可以达到时间解耦的效果。Windows的消息循环就是一个应用。多线程情况下,消息队列优先于事件系统。
消息更贴合所谓的“发布 - 订阅”模式。
-
发布者只需告诉Broker,我要发的消息,topic是AAA;
-
订阅者只需告诉Broker,我要订阅topic是AAA的消息;
-
于是,当Broker收到发布者发过来消息,并且topic是AAA时,就会把消息推送给订阅了topic是AAA的订阅者。当然也有可能是订阅者自己过来拉取,看具体实现。
源码
参考资源
- 《大话设计模式》中的第14章
- https://blog.csdn.net/cpongo1/article/details/102472980 中的节选内容