设计模式新说 - 通知模式

通知模式 Notification Pattern

原观察者模式 Observer Pattern

记忆点

  1. 如何实现一场轰轰烈烈的婚礼通知:通知主体(新婚夫妇),被通知人(亲朋好友好友),如何注册再遍历通知;

  2. 总结通知主体的几个常用方法:新增、删除、遍历通知;

  3. 体会被通知人实现同一个接口,如何方便了通知主体的遍历;

  4. 体会通知列表list起的作用;

  5. 是否能感受到其中的化同步为异步;

  6. 为什么可能命名为通知模式会更好。

用代码还原一场婚礼通知

《设计模式新说 - 通知模式》

婚礼通知(其实所有的通知都类似)需要具备:

  1. 谁通知:新婚夫妇准备举办婚礼,自然是新婚夫妇通知大家,于是通知的主体(Subject)是新婚夫妇;

  2. 通知谁:被通知的对象(Observer),婚礼通知中亲朋好友是被通知的对象;

  3. 什么时候通知:例子中新婚夫妇举办婚礼了才会通知大家,从未举办到举办正是这是个状态的改变才会触发通知的发生,而这个状态是否改变也正是所有人关注的焦点。

通知的例子在生活中数不胜数:

  1. 订阅报纸:消费者向报社订阅报纸(订阅),有新报纸刊发了(状态改变),会逐个分发到各个订阅的家庭(通知/发布);

  2. 微信公众号:微信用户关注了某公众号(订阅),有新内容了(状态改变),公众号会将内容分发到所有订阅者微信上(通知/发布);

  3. RSS博客订阅:用户关注博客的内容是否更新,于是将自己邮箱提供给博客(订阅),博客更新(状态改变),博客将新内容分发到所有订阅者的邮箱里(通知/发布);

  4. 开会:大伙在倾听领导讲话(订阅),领导没说一句话(状态改变),声音就广播到所有人的耳朵里(通知、发布);

《设计模式新说 - 通知模式》

对通知过程的抽象:

  1. 注册;

  2. 状态改变;

  3. 通知;

以下尝试使用代码简单描述下婚礼通知现场。

// 1. 首先得有一对新婚夫妇,他们是通知主体
NewlyMarriedCouple couple;

// 2. 有若干亲朋好友,被通知人
People futeng;
People qxm;
People zongdi;
...

// 3. 注册到通知名单里面
couple.add2List(qxm);
couple.add2List(zongdi);
couple.add2List(futeng);

// 4. 举办婚礼了,于是通知名单里面的所有人来参加婚礼
couple.notify();

// 5. 通知呢就是遍历名单里面的所有人
public void notify() {
    for (People guest: namelist) {
        ...
    }
}

// 6. 让这些亲朋好友都实现同一个接口IAttend2Wedding
Public class People implements IAttend2Wedding {
    attend2Wedding() { // go to wedding}
}

// 7. 回到之前的遍历通知方法,大家都实现了同一个接口,即具备了相同的能力,直接调用
public void notify() {
    for (People guest: namelist) {
        guest.attend2Wedding();
    }
}

// 如果宾客们没有实现同一个接口,而是大家都有各自的参加婚礼的方法,那么通知主体无疑需要调用每个人的方法来分别调用,类似
guestA.goToWedding();
guestB.attendToWedding();
guestC.weddingTime();
...

// 相当于对每个宾客都登门拜访,怎么会有群发短信来的有效呢(仅就完成通知的目的而言)
// 仔细体会接口的规范能力
// 这个模式的亮点也就在于,被通知的人都实现了同一个接口,拥有同一个方法。这就存在了,大家把自己注册进通知者list,通知者一个遍历list就实现通知功能的可能。否则,如果每个被通知人的调用方法都不一样,那么就需要分别调用。这也就是接口存在的强大意义,两个字,规范。

回味下婚礼通知中的两个重点过程:

  1. 亲朋好友要求这对新婚夫妇将自己添加进通知名单里面,这样新婚夫妇结婚的时候就可以通知他们来参加婚礼;

  2. 新婚夫妇办婚礼了,于是通知大家来参加,通知就是遍历名单,逐个邀请他们过来。

抽象一下:

  1. 亲朋好友将自己注册进新婚夫妇的通知名单里;

  2. 新婚夫妇遍历名单,调用每个人参加婚礼的方法,完成通知;

再抽象一下:

  1. 注册;

  2. 通知;

至此

  1. 我们实现一场轰轰烈烈的婚礼通知,从通知主体(新婚夫妇),被通知人(亲朋好友好友),如何注册,如何遍历通知,何时触发通知(状态改变,即要举办婚礼了)都有涉及;

  2. 对于通知主体除了常用的新增通知人、遍历通知,常见的还会有删除通知人的方法,类似于订阅博客总得给我取消订阅的权利吧;

  3. 体会到所有被通知人实现同一个接口,大大方便了通知主体的遍历,体会到了接口的规范意义;

  4. 体会到每个通知模式的实现都有个小小订阅list;

为什么不建议称之为观察者模式

通知模式是站在通知主体(新婚夫妇)的角度考虑,通知主体状态改变,才会遍历list,发起通知。所以通知大体上是个异步的过程。

如果站在被通知人的角度考虑呢,如果不允许通知主体异步通知呢?

那被通知人似乎只能选择不停的去观察自己感兴趣的通知主体的某状态是否改变了。

《设计模式新说 - 通知模式》

想象下古代观察岗的士兵,彻夜不停的观察烽火台是否起烟的场景。

观察者是对目标主体某状态变化感兴趣的人(观察士兵与烽火狼烟的关系),你不主动观察,狼烟起了就无法通知到你了,所以大体主动观察是个主动与同步的过程。

如果用代码实现主动观察是否起狼烟的过程:

// 1. 有一个观察员
Observer observer;

// 2. 有一个烽火狼烟
File file;

// 3. 观察员一直处于观察状态,该线程就做这一件事了,也就阻塞了自己
observer.watching(file);

//... 阻塞自己,后续代码不执行了

// 4. 有另外一个观察员
Observer observer2;
// 5. 也一直处于观察状态,阻塞
observer2.watching(file);

... 所有其他观察员,都只能阻塞自己才能实现一直观察的状态

这是没有异步通知的时代,浪费了多少人力物力。
想象下如何使用异步通知来实现,敌情通知给各州的功能。

至此可以体会到,通知模式是如何化同步为异步的了。

最为关键的来了:

  1. 站在观察者角度解决问题,是同步的解决方案,有其局限性。

  2. 最初的GoF编著的《设计模式》所命名的观察者模式分明是使用异步通知的方式来解决这个观察者的问题。

  3. 解决观察者的问题,为什么还命名为观察者模式呢?

命名是非常关键的,名字肩负了交流和记忆提取的责任。
而我们认为『通知模式』更能体现该设计模式的用意,更方便联系实际场景,和记忆提取。

其实《设计模式》书作者也提供了其他命名

Observer Also Known As
Dependents, Publish-Subscribe

场景与输出

Socket编程中的同步阻塞

(coming soon…)

JDK中的通知者模式的实现

(coming soon…)

代码实现样例

(coming soon…)

参考

Design Patterns : Elements of Reusable Object-Oriented Software

    原文作者:设计模式
    原文地址: https://segmentfault.com/a/1190000004440615
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞