1. 前文汇总

「补课」进行时:设计模式系列

2. 观察者模式

2.1 定义

观察者模式(Observer Pattern)也叫做发布订阅模式(Publish/subscribe),它是一个在项目中经常使用的模式,其定义如下:

Define a one-to-many dependency between objects so that when oneobject changes state,all its dependents are notified and updatedautomatically.(定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。)

2.2 通用类图

  • Subject 被观察者: 定义被观察者必须实现的职责,它必须能够动态地增加、取消观察者。它一般是抽象类或者是实现类,仅仅完成作为被观察者必须实现的职责:管理观察者并通知观察者。
  • ConcreteSubject 具体的被观察者: 定义被观察者自己的业务逻辑,同时定义对哪些事件进行通知。
  • Observer 观察者: 观察者接收到消息后,即进行update(更新方法)操作,对接收到的信息进行处理。
  • ConcreteObserver 具体的观察者: 每个观察在接收到消息后的处理反应是不同,各个观察者有自己的处理逻辑。

2.3 通用代码

Subject 被观察者:

public abstract class Subject {// 定义一个观察者数组private Vector<Observer> obsVector = new Vector<>();// 添加一个观察者public void addObserver(Observer obsVector) {this.obsVector.add(obsVector);}// 删除一个观察者public void delObserver(Observer observer) {this.obsVector.remove(observer);}// 通知所有观察者public void notifyObservers() {for (Observer obs : this.obsVector) {obs.update();}}
}

ConcreteSubject 具体的被观察者:

public class ConcreteSubject extends Subject {public void doSomething() {// 具体的业务super.notifyObservers();}
}

Observer 观察者:

public interface Observer {void update();
}

ConcreteObserver 具体的观察者:

public class ConcreteObserver implements Observer{@Overridepublic void update() {System.out.println("进行消息处理");}
}

测试场景类:

public class Test {public static void main(String[] args) {// 创建一个被观察者ConcreteSubject subject = new ConcreteSubject();// 创建一个观察者Observer observer = new ConcreteObserver();// 观察者观察被观察者subject.addObserver(observer);// 观察者开始活动了subject.doSomething();}
}

3. 一个案例

观察者模式是设计模式中的超级模式,有关他的应用随处可见。

就比如说微信公众号,我每天推送一篇博文内容,订阅的用户都能够在我发布推送之后及时接收到推送,方便地在手机端进行阅读。

订阅者接口(观察者)

public interface Subscriber {void receive(String publisher, String articleName);
}

微信客户端(具体观察者)

public class WeChatClient implements Subscriber {private String username;public WeChatClient(String username) {this.username = username;}@Overridepublic void receive(String publisher, String articleName) {System.out.println(String.format("用户<%s> 接收到 <%s>微信公众号 的推送,文章标题为 <%s>", username, publisher, articleName));}
}

一个微信客户端(具体观察者)

public class Publisher {private List<Subscriber> subscribers;private boolean pubStatus = false;public Publisher() {subscribers = new ArrayList<>();}protected void subscribe(Subscriber subscriber) {this.subscribers.add(subscriber);}protected void unsubscribe(Subscriber subscriber) {if (this.subscribers.contains(subscriber)) {this.subscribers.remove(subscriber);}}protected void notifySubscribers(String publisher, String articleName) {if (this.pubStatus == false) {return;}for (Subscriber subscriber : this.subscribers) {subscriber.receive(publisher, articleName);}this.clearPubStatus();}protected void setPubStatus() {this.pubStatus = true;}protected void clearPubStatus() {this.pubStatus = false;}
}

具体目标

public class WeChatAccounts extends Publisher {private String name;public WeChatAccounts(String name) {this.name = name;}public void publishArticles(String articleName, String content) {System.out.println(String.format("\n<%s>微信公众号 发布了一篇推送,文章名称为 <%s>,内容为 <%s> ", this.name, articleName, content));setPubStatus();notifySubscribers(this.name, articleName);}
}

测试类

public class Test {public static void main(String[] args) {WeChatAccounts accounts = new WeChatAccounts("极客挖掘机");WeChatClient user1 = new WeChatClient("张三");WeChatClient user2 = new WeChatClient("李四");WeChatClient user3 = new WeChatClient("王五");accounts.subscribe(user1);accounts.subscribe(user2);accounts.subscribe(user3);accounts.publishArticles("设计模式 | 观察者模式及典型应用", "观察者模式的内容...");accounts.unsubscribe(user1);accounts.publishArticles("设计模式 | 单例模式及典型应用", "单例模式的内容....");}
}

测试结果

<极客挖掘机>微信公众号 发布了一篇推送,文章名称为 <设计模式 | 观察者模式及典型应用>,内容为 <观察者模式的内容...>
用户<张三> 接收到 <极客挖掘机>微信公众号 的推送,文章标题为 <设计模式 | 观察者模式及典型应用>
用户<李四> 接收到 <极客挖掘机>微信公众号 的推送,文章标题为 <设计模式 | 观察者模式及典型应用>
用户<王五> 接收到 <极客挖掘机>微信公众号 的推送,文章标题为 <设计模式 | 观察者模式及典型应用><极客挖掘机>微信公众号 发布了一篇推送,文章名称为 <设计模式 | 单例模式及典型应用>,内容为 <单例模式的内容....>
用户<李四> 接收到 <极客挖掘机>微信公众号 的推送,文章标题为 <设计模式 | 单例模式及典型应用>
用户<王五> 接收到 <极客挖掘机>微信公众号 的推送,文章标题为 <设计模式 | 单例模式及典型应用>

4. JDK 对的观察者模式的支持

观察者模式在 Java 语言中的地位非常重要。在 JDK 的 java.util 包中,提供了 Observable 类以及 Observer 接口,它们构成了JDK对观察者模式的支持。

java.util.Observer 接口中,仅有一个 update(Observable o, Object arg) 方法,当观察目标发生变化时被调用:

public interface Observer {void update(Observable o, Object arg);
}

java.util.Observable 类则为目标类:

public class Observable {private boolean changed = false;private Vector<Observer> obs;public Observable() {obs = new Vector<>();}// 用于注册新的观察者对象到向量中public synchronized void addObserver(Observer o) {if (o == null)throw new NullPointerException();if (!obs.contains(o)) {obs.addElement(o);}}// 用于删除向量中的某一个观察者对象public synchronized void deleteObserver(Observer o) {obs.removeElement(o);}public void notifyObservers() {notifyObservers(null);}// 通知方法,用于在方法内部循环调用向量中每一个观察者的update()方法public void notifyObservers(Object arg) {Object[] arrLocal;synchronized (this) {if (!changed)return;arrLocal = obs.toArray();clearChanged();}for (int i = arrLocal.length-1; i>=0; i--)((Observer)arrLocal[i]).update(this, arg);}// 用于清空向量,即删除向量中所有观察者对象public synchronized void deleteObservers() {obs.removeAllElements();}// 该方法被调用后会设置一个boolean类型的内部标记变量changed的值为true,表示观察目标对象的状态发生了变化protected synchronized void setChanged() {changed = true;}// 用于将changed变量的值设为false,表示对象状态不再发生改变或者已经通知了所有的观察者对象,调用了它们的update()方法protected synchronized void clearChanged() {changed = false;}// 返回对象状态是否改变public synchronized boolean hasChanged() {return changed;}// 返回向量中观察者的数量public synchronized int countObservers() {return obs.size();}
}

相比较我们自己的示例 Publisherjava.util.Observer 中多了并发和 NPE 方面的考虑 。

使用 JDK 对观察者模式的支持,改写一下上面的示例:

增加一个通知类 WechatNotice ,用于推送通知的传递:

public class WechatNotice {private String publisher;private String articleName;// 省略 get/set
}

然后改写 WeChatClientWeChatAccounts ,分别实现 JDK 的 Observer 接口和继承 Observable 类:

public class WeChatClient implements Observer {private String username;public WeChatClient(String username) {this.username = username;}@Overridepublic void update(Observable o, Object arg) {WechatNotice notice = (WechatNotice) arg;System.out.println(String.format("用户<%s> 接收到 <%s>微信公众号 的推送,文章标题为 <%s>", username, notice.getPublisher(), notice.getArticleName()));}
}public class WeChatAccounts extends Observable {private String name;public WeChatAccounts(String name) {this.name = name;}public void publishArticles(String articleName, String content) {System.out.println(String.format("\n<%s>微信公众号 发布了一篇推送,文章名称为 <%s>,内容为 <%s> ", this.name, articleName, content));setChanged();notifyObservers(new WechatNotice(this.name, articleName));}
}

最后是一个测试类:

public class Test {public static void main(String[] args) {WeChatAccounts accounts = new WeChatAccounts("极客挖掘机");WeChatClient user1 = new WeChatClient("张三");WeChatClient user2 = new WeChatClient("李四");WeChatClient user3 = new WeChatClient("王五");accounts.addObserver(user1);accounts.addObserver(user2);accounts.addObserver(user3);accounts.publishArticles("设计模式 | 观察者模式及典型应用", "观察者模式的内容...");accounts.deleteObserver(user1);accounts.publishArticles("设计模式 | 单例模式及典型应用", "单例模式的内容....");}
}

测试结果:

<极客挖掘机>微信公众号 发布了一篇推送,文章名称为 <设计模式 | 观察者模式及典型应用>,内容为 <观察者模式的内容...>
用户<王五> 接收到 <极客挖掘机>微信公众号 的推送,文章标题为 <设计模式 | 观察者模式及典型应用>
用户<李四> 接收到 <极客挖掘机>微信公众号 的推送,文章标题为 <设计模式 | 观察者模式及典型应用>
用户<张三> 接收到 <极客挖掘机>微信公众号 的推送,文章标题为 <设计模式 | 观察者模式及典型应用><极客挖掘机>微信公众号 发布了一篇推送,文章名称为 <设计模式 | 单例模式及典型应用>,内容为 <单例模式的内容....>
用户<王五> 接收到 <极客挖掘机>微信公众号 的推送,文章标题为 <设计模式 | 单例模式及典型应用>
用户<李四> 接收到 <极客挖掘机>微信公众号 的推送,文章标题为 <设计模式 | 单例模式及典型应用>

和前面的示例结果完全一致。

您的扫码关注,是对小编坚持原创的最大鼓励:)

「补课」进行时:设计模式(15)——观察者模式相关推荐

  1. 「补课」进行时:设计模式(5)——从 LOL 中学习代理模式

    1. 前文汇总 「补课」进行时:设计模式系列 2. 从 LOL 中学习代理模式 我是一个很喜欢玩游戏的人,虽然平时玩游戏的时间并不多,但我也是一个忠实的 LOL 的爱好者,就是段位有点惨不忍睹,常年倔 ...

  2. 「补课」进行时:设计模式(6)——郭靖大侠带你学原型模式

    1. 前文汇总 「补课」进行时:设计模式系列 2. 找工作 这一天,郭靖大侠因为在桃花岛调戏侍女被黄蓉打出了桃花岛,这下可玩大了,从桃花岛被赶出来吃啥喝啥啊,得赶紧找份工作,西北风可喝不饱肚子哇~~~ ...

  3. 「补课」进行时:设计模式(11)——游戏中的策略模式

    1. 前文汇总 「补课」进行时:设计模式系列 2. 游戏中的策略模式 我是一个很喜欢玩游戏的人,周末在家打打游戏是真的很开心. 回想起来当年上大学的往昔峥嵘岁月,那时候基本上是一个人在玩游戏,背后围着 ...

  4. 「补课」进行时:设计模式(20)——解释器模式

    1. 前文汇总 「补课」进行时:设计模式系列 2. 解释器模式 解释器模式这个模式和前面的访问者模式比较像,当然,我说的比较像是难的比较像,以及使用率是真的比较低,基本上没有使用的场景,访问者模式还有 ...

  5. vb.net如何查询电脑麦克风收到声音_EMUI 10.1 跨屏协同实测:这一次把你的手机「搬」进电脑...

    智能手机发展到现在,我们越来越需要手机与其他设备进行互联互通.电脑是我们办公最常用的工具,手机则是生活必需设备,这两者的协同需求,自然也就成为了大多数用户的痛点. Apple 用隔空投送.接力.随航等 ...

  6. hibernate get方法有执行sql但是后台拿不到_「6」进大厂必须掌握的面试题-Hibernate...

    1.什么是Hibernate Framework? 对象关系映射或对象关系管理(ORM)是将应用程序域模型对象映射到关系数据库表的编程技术.Hibernate是基于Java的ORM工具,它提供了一个框 ...

  7. 从首个「数实融合」公益球场,看元宇宙奏响创新「三重奏」

    作者 | 曾响铃 文 | 响铃说 2022年的元宇宙,一半是海水,一半是火焰. 一边是刮起元宇宙热潮的Roblox股价跌去大半,Meta也因元宇宙亏损深陷泥潭.另一边,经过2021年元宇宙概念落地和普 ...

  8. 计算机科学与技术专业为什么要学物理,「物理」一定要好的14个大学专业

    原标题:「物理」一定要好的14个大学专业 在大学里,有不少专业的课程和大学物理紧密相连.可以说大学物理学得好不好,在很大程度上决定了一个人专业课能否学得相对轻松,成绩能否排到专业内中等及以上. 举几个 ...

  9. 「设计模式(二) - 观察者模式」

    「设计模式(二) - 观察者模式」 一.回复TD退订 日常生活中,这种短信无处不在,各种广告,在互联网高速发展的今天,个人信息可以说是透明的.没有任何隐私可言,类似这种通知其实跟我们开发过程使用的观察 ...

最新文章

  1. IOS、Android html5页面输入的表情符号变成了乱码”???“
  2. MongoDB指定类型查询数据
  3. java项目配置常见问题
  4. 主机和虚拟机ping不通的原因
  5. redis之 zadd、zremrangebyscore、zremrangebyrank、zscore、zcard、zcount、zrangebyscore、zinczrevrankzrevrange
  6. 当重复调用addsubview时出现显示重叠问题
  7. 【latex】输入角度符号°
  8. [机器学习-数学] 矩阵求导(分母布局与分子布局),以及常用的矩阵求导公式
  9. 若何在嵌入式Linux及下建造QPF字库
  10. 手机打开电脑端网页_网站建设要把电脑端手机端都做好
  11. centos mysql 绿色版安装_centos7环境下mysql5.7的安装与配置(免安装版)
  12. PR2018安装及错误处理
  13. linux设置用户密码
  14. 电脑时间调到2099年,会发生什么
  15. fopen 参数'rb' 与'rb+'引发的黑色血案
  16. 【软考】高级系统架构设计师学习经验分享
  17. 直流电机 M PWM 调速原理
  18. 还原android系统文件夹,如何从Android的内存中恢复文件-万兴恢复专家
  19. 景联文科技:深度了解语音识别之发音词典及语音数据采集标注
  20. 使用 WordPress快速个人建站指南

热门文章

  1. 音频声音太小怎么调大?
  2. 三星14纳米EUV DDR5 DRAM量产;Amazfit推出三款智能手表;Whale帷幄获5000万美元融资 | 全球TMT...
  3. python 曲线平滑_曲线平滑(smoothing)
  4. 三星S7edge从8.0降到6.0.1,只为流畅的飞一般的感觉
  5. Jenkins自动化打包生成二维码下载链接
  6. 【荣耀开发者服务平台—百亿曝光扶持等你来】智慧服务快应用卡片接入指南(下)
  7. 微贷网java高级开发工程师
  8. LeetCode笔记05:最长公共前缀
  9. 企业生产中,APS系统有哪些具体应用场景?
  10. 慢慢来,等待也是一种美好