最近想深入研究下响应式编程, 作为基础很有必要来把观察者模式撸一遍; 一开始我是觉得很 easy, 然后就直接开撸了, 撸着撸着发现撸不动了. 因为我突然不太明白这个模式了, 说好的观察者, 到底发布 - 订阅的两者执行者谁才是观察者? 又或者说还有其他角色? 但是根据JAVA 与模式一书中的结构, 并没有额外的角色出现.
思考中...., 好吧想不出来...., 跑步去...
跑步时我给自己罗列了几个问题:
这里先抛出定义: GOF 给观察者模式如下定义: 定义对象间的一种一对多的依赖关系, 当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新.
既然是对象状态发生变更, 那么到底是谁的状态发生了变更, 又导致了谁被通知.
观察者模式既然又可以称之为 "发布 - 订阅模式", 那么对应起来, 观察者到底承当了 "发布" 的角色还是 "订阅" 的角色. 就是说观察者到底是主动的还是被动的?
被观察者又干了什么事? 它是主动的还是被动的角色?
这里由于一些定式思维, 总会觉得既然是 "被观察者", 那么这个 "被" 字就是不是就表明 "被观察者" 是被动接受变更的一方, 也就是接受通知的一方呢?
之前我也是走到这个胡同里了, 程序写完总觉得哪里不对; 回过头看, 还是自己太年轻, 没有 get 到哪些大佬们的点.
先来看程序; 这里用掘金来打个比方, 我的博客 glmmaper 作为被观察者, 也就是发布者. 掘金小伙伴们作为观察者, 也就是订阅者.
具体逻辑: 小伙伴们 (订阅者) 关注 (订阅) 了我的博客 (发布者), 如果我发布了一篇文章(状态变更), 就会通知(推送消息) 所有关注我的小伙伴.
- package com.glmapper.designmode.observor;
- /**
- * @description: 抽象主题接口
- * @email: <a href="glmapper_2018@163.com"></a>
- * @author: 磊叔
- * @date: 18/4/22
- */
- public interface Subject {
- /**
- * 新增关注者
- * @param observer 关注的小伙伴
- */
- void addFocusObserver(Observer observer);
- /**
- * 取消关注
- * @param observer 取消关注的小伙伴
- */
- void removeFocusObserver(Observer observer);
- /**
- * 通知机制, 通知机制由相关事件来触发, 比如说发布文章
- * @param blogName 博客名
- * @param articleName 文章名
- */
- void notifyObservers(String blogName,String articleName);
- }
三个方法, 一个是博客主页增加了一个关注者; 一个是博客主页有小伙伴取消的关注(对于博客来说就是移除一个关注者, 这里不知道是否也会觉得别扭? 明明你取消的关注, 为啥说成是我移除你, 也就是不让你关注了, 还能这么玩? 这里肯定是需要在引入其他的一些辅助机制, 比如说你在客户端发起了一个取消关注的请求, 后端处理的时候掘金的工程师们就是在我的关注列表中将你移除的, 嗯, 这么一想确实是我不让你关注了.....); 最后一个方法是发起一个通知. 下面是一个具体的博客, 比如说是 glmapper;
- package com.glmapper.designmode.observor;
- import java.util.ArrayList;
- import java.util.List;
- /**
- * @description: 这个是具体发布者, 这里比喻成我的博客 glmapper
- * @email: <a href="glmapper_2018@163.com"></a>
- * @author: 磊叔
- * @date: 18/4/22
- */
- public class ConcreteSubject implements Subject {
- /** 我的当前关注列表 */
- List<Observer> Observers = new ArrayList<>();
- /** 我的博客名 : 求关注 */
- private static final String blogName = "glmapper";
- @Override
- public void addFocusObserver(Observer observer) {
- Observers.add(observer);
- }
- @Override
- public void removeFocusObserver(Observer observer) {
- Observers.remove(observer);
- }
- @Override
- public void notifyObservers(String blogName,String articleName) {
- for (Observer observer:Observers) {
- observer.update(blogName,articleName);
- }
- }
- /**
- * 这里是发布文章, 触发通知事件
- */
- public void publishArticle(String articleName){
- notifyObservers(blogName,articleName);
- }
- }
前面提到, 通知事件肯定是由于某些状态发生变更了, 才会进行通知, 这里就可以比方为我发布了一篇博客, 然后通知你(这里只能假如你关注了). 再来看观察者:
- package com.glmapper.designmode.observor;
- /**
- * @description: 订阅者抽象接口
- * @email: <a href="glmapper_2018@163.com"></a>
- * @author: 磊叔
- * @date: 18/4/22
- */
- public interface Observer {
- /**
- * 调用此方法会更新状态, 做出相应的动作
- * @param blogName
- * @param articleName
- */
- void update(String blogName,String articleName);
- }
抽象订阅者, 有一个 update 方法, 通知你去做出相应的动作, 具体动作每个观察者都可能不同.
- package com.glmapper.designmode.observor;
- /**
- * @description: 这个是具体订阅者, 这里可以比喻成博客关注者,
- * 收到变更信息之后需要做出相应的动作
- * @email: <a href="glmapper_2018@163.com"></a>
- * @author: 磊叔
- * @date: 18/4/22
- */
- public class ConcreteObserver implements Observer {
- @Override
- public void update(String blogName,String articleName) {
- System.out.println(blogName+"发布了新的文章, 文章名为:"+articleName);
- read(articleName);
- }
- private void read(String articleName){
- System.out.println("即将阅读"+articleName+"这篇文章");
- }
- }
上面是一个具体的关注者, 加入说就是你. 博客更新之后发了一个通知给你(掘金 app 推送的消息), 然后你点了一下, 这个也是一种动作. 例子中举的是 read, 就是关注者做出阅读的动作.
看下最后的运行结果:
- package com.glmapper.designmode.observor;
- /**
- * @description: [描述文本]
- * @email: <a href="glmapper_2018@163.com"></a>
- * @author: 磊叔
- * @date: 18/4/22
- */
- public class MyMainIndex{
- public static void main(String[] args) {
- // 博客主体
- ConcreteSubject subject = new ConcreteSubject();
- // 关注者: handSome 是帅气的意思
- Observer handSome = new ConcreteObserver();
- // 增加一个关注者
- subject.addFocusObserver(handSome);
- // 发一篇文章
- subject.publishArticle("设计模式 - 观察者模式");
- }
- }
glmapper 发布了新的文章, 文章名为: 设计模式 - 观察者模式
即将阅读 设计模式 - 观察者模式 这篇文章
酒桶说: 啊, 欢乐时光总是短暂的
所以作为积累, 还是需要将一些基本的概念来罗列一下的.
主要角色:
抽象主题角色(Subject: 主题角色将所有对观察者对象的引用保存在一个集合中, 每个主题可以有任意多个观察者. 抽象主题提供了增加和删除等观察者对象的接口.
抽象观察者角色(Observer): 为所有的具体观察者定义一个接口, 在观察的主题发生改变时更新自己.
具体主题角色(ConcreteSubject)(1 个): 存储相关状态到具体观察者对象, 当具体主题的内部状态改变时, 给所有登记过的观察者发出通知. 具体主题角色通常用一个具体子类实现.
具体观察者角色(ConcretedObserver)(多个): 存储一个具体主题对象, 存储相关状态, 实现抽象观察者角色所要求的更新接口, 以使得其自身状态和主题的状态保持一致.
具体关系:
抽象主题 (Subject)(接口)--> 被具体主题 (ConcreteSubject) 角色 (1 个) 实现
抽象观察者 (Observer)(接口)--> 被具体观察者 (ConcretedObserver) 角色 (N 个) 实现
观察者对象载入主题方法, 并在主题方法中调用观察者对象实现的接口方法 update 来让自己发生变更响应.
一些场景:
当对一个对象的的改动会引发其他对象的变动时, 而且你无法预测有多少个对象需要被改动.
当一个对象需要有能力通知其他对象, 且不需要了解这些对象是什么类型时.
基于发布订阅的具体实现例子还是很多的, 比较典型的就是这种订阅一个博客, 然后博客更新推送; 还有微信公众号, 服务号这些.
到这里我们再回过头来看一开始留下的几个问题:
被观察者的状态发生变更, 然后 "主动通知" 观察者, 并不是说, 观察者主动去获取通知.
被观察者是消息发布者, 观察者是消息订阅者; 观察者是被动接受者.
被观察者的作用就是存储当前的观察者列表, 然后提供一些通知机制来告诉观察者自己发生了状态变更, 是主动者.
OK, 观察者模式就撸到这里, 也欢迎小伙伴们提出自己珍贵的意见; 有写的不当之处烦请及时提出.
播报: 菜鸟成长系列又开始更新了....
来源: https://juejin.im/post/5adc90126fb9a07abf721a9b