一、观察者模式介绍

1. 解决的问题

主要解决将一个对象的状态改变通知给其他对象的问题。

2. 定义

观察者模式是一种行为设计模式,允许你定义一种订阅机制,可在对象事件发生时通知多个 “观察” 该对象的其他对象。

3. 应用场景

  • 当一个对象的改变需要改变其他对象,或实际对象时是事先未知的或动态变化时,可使用观察者模式。
  • 当应用中的一些对象必须观察其他对象时,可使用观察者模式,但仅能在有限时间内或特定情况下使用。

二、观察者模式优缺点

1. 优点

  • 开闭原则:无需修改发布者代码就能引入新的订阅者(如果是发布者接口,则可轻松引入发布者类)。
  • 可以在运行时建立与对象之间的联系。

2. 缺点

  • 订阅者的通知顺序是随机的。

三、观察者模式应用实例:您的快递已到达蜂站,请及时签收

1. 实例场景

现在大部分快递都不会交付到手上了,除非我们特意选了上门服务。

一般都是买完两三天,收到条短信:您好,您的快递已到蜂站,请凭取件码K16888取件,取件时间09:30-21:00,请及时取件。

刚看完短信,APP也跳出一则通知:您的快递已被蜂站签收,请及时取件。

今天,就以快递到达,同时收到APP和短信通知为例,介绍一下观察者模式。

2. 观察者模式实现

2.1 工程结构
observer-pattern
└─ src├─ main│  └─ java│    └─ org.design.pattern.observer│       ├─ model│       │    ├─ ExpressPackage.java│       │    ├─ Courier.java│       │    └─ User.java│       ├─ subscriber│       │    ├─ ExpressEventSubscriber.java│       │    └─ impl│       │     ├─ AppExpressEventListener.java│       │        └─ SmsExpressEventSubscriber.java│       ├─ publisher│       │    └─ ExpressEventPublisher.java│       └─ service│            ├─ ExpressService.java│            └─ impl│                 └─ ExpressServiceImpl.java└─ test└─ java└─ org.design.pattern.observer.test└─ ExpressEventTest.java
2.2 代码实现
2.2.1 实体类

快递实体类

/*** 快递*/
@Getter
@Setter
@AllArgsConstructor
public class Express {/*** 快递id*/private String id;/*** 收件人名称*/private String recipientName;/*** 收件人打电话*/private String recipientPhone;/*** 收件人地址*/private String recipientAddress;
}
2.2.2 发布者

快递事件发布者

/*** 快递事件发布者*/
public class ExpressEventPublisher {/*** 快递事件订阅者列表*/private List<ExpressEventSubscriber> subscriberList = new ArrayList<>();/*** 订阅快递事件** @param expressEventSubscriber 订阅者*/public void subscribe(ExpressEventSubscriber expressEventSubscriber) {subscriberList.add(expressEventSubscriber);}/*** 取消订阅快递事件** @param expressEventSubscriber 订阅者*/public void unsubscribe(ExpressEventSubscriber expressEventSubscriber) {subscriberList.remove(expressEventSubscriber);}/*** 通知订阅者快递到达** @param express 快递*/public void notifySubscribersArrive(Express express) {subscriberList.forEach(expressEventSubscriber -> expressEventSubscriber.arrive(express));}
}
2.2.3 订阅者

快递事件订阅者接口

/*** 快递事件订阅者接口*/
public interface ExpressEventSubscriber {/*** 快递到达** @param express 快递*/void arrive(Express express);
}

App-快递事件订阅者

/*** App-快递事件订阅者*/
@Slf4j
public class AppExpressEventListener implements ExpressEventSubscriber {/*** 快递到达** @param express 快递*/@Overridepublic void arrive(Express express) {log.info("App 通知:您的包裹{}已达到蜂站!", express.getId());}
}

短信-快递事件订阅者

/*** 短信-快递事件订阅者*/
@Slf4j
public class SmsExpressEventSubscriber implements ExpressEventSubscriber {/*** 快递到达** @param express 快递*/@Overridepublic void arrive(Express express) {log.info("短信通知:您的包裹{}已达到蜂站!", express.getId());}
}
2.2.4 服务类

快递服务接口

/*** 快递服务接口*/
public interface ExpressService {/*** 快递已到达** @param express 快递*/void arrive(Express express);
}

快递服务实现类

/*** 快递服务实现类*/
@Slf4j
public class ExpressServiceImpl implements ExpressService {/*** 快递事件发布者*/private final ExpressEventPublisher expressEventPublisher;/*** 构造方法** @param expressEventPublisher 快递事件发布者*/public ExpressServiceImpl(ExpressEventPublisher expressEventPublisher) {this.expressEventPublisher = expressEventPublisher;}/*** 快递到达** @param express 快递*/@Overridepublic void arrive(Express express) {log.info("{}的快递已到达蜂站!", express.getRecipientName());expressEventPublisher.notifySubscribersArrive(express);}
}
2.3 测试验证
2.3.1 测试验证类
/*** 快递事件测试类*/
public class ExpressEventTest {@Testpublic void test() {// 快递初始化Express express = new Express("1", "yiyufxst", "13245678910", "xx路");// 订阅者初始化ExpressEventSubscriber appExpressEventListener = new AppExpressEventListener();ExpressEventSubscriber smsExpressEventSubscriber = new SmsExpressEventSubscriber();// 发布者初始化ExpressEventPublisher expressEventPublisher = new ExpressEventPublisher();// 订阅快递事件expressEventPublisher.subscribe(appExpressEventListener);expressEventPublisher.subscribe(smsExpressEventSubscriber);// 快递服务类初始化ExpressService expressService = new ExpressServiceImpl(expressEventPublisher);// 快递到达expressService.arrive(express);// App取消订阅快递事件expressEventPublisher.unsubscribe(appExpressEventListener);// 模拟新快递express.setId("2");// 快递到达expressService.arrive(express);}
}
2.3.2 测试结果
14:50:01.090 [main] INFO  o.d.p.o.s.impl.ExpressServiceImpl - yiyufxst的快递已到达蜂站!
14:50:01.093 [main] INFO  o.d.p.o.s.i.AppExpressEventListener - App 通知:您的包裹1已达到蜂站!
14:50:01.093 [main] INFO  o.d.p.o.s.i.SmsExpressEventSubscriber - 短信通知:您的包裹1已达到蜂站!
14:50:01.093 [main] INFO  o.d.p.o.s.impl.ExpressServiceImpl - yiyufxst的快递已到达蜂站!
14:50:01.093 [main] INFO  o.d.p.o.s.i.SmsExpressEventSubscriber - 短信通知:您的包裹2已达到蜂站!Process finished with exit code 0

四、观察者模式结构

  1. 发布者(Publisher)会对其他对象发送值得关注的事件。事件会在发布者自身状态改变或执行特定行为后发生。发布者中包含一个允许新订阅者加入或当前订阅者离开列表的订阅架构。

    当新事件发生时,发送者会便利订阅者列表并调用每个订阅者对象的通知方法。

  2. 订阅者(Subscriber)接口声明了通知接口。在绝大多数情况下,该接口仅包含一个更新方法。该方法可以拥有多个参数,以便发布者能在更新时传递时间的详细信息。

  3. 具体订阅者(Concrete Subscribers)可以执行一些操作来回应发布者的通知。所有订阅者类实现了同样的接口,因此发布者无需与具体类耦合。

    订阅者通常需要一些上下文信息来正确地处理更新消息。因此发布者会将一些上下文数据作为通知方法的参数进行传递。发布者也可将自身作为参数传递,使订阅者直接获取所需的数据。

  4. 客户端(Client)会分别创建发布者和订阅者对象,然后为订阅者注册发布者更新。

设计模式并不难学,其本身就是多年经验提炼出的开发指导思想,关键在于多加练习,带着使用设计模式的思想去优化代码,就能构建出更合理的代码。

源码地址:https://github.com/yiyufxst/design-pattern-java

参考资料:
小博哥重学设计模式:https://github.com/fuzhengwei/itstack-demo-design
深入设计模式:https://refactoringguru.cn/design-patterns/catalog

初学 Java 设计模式(十九):实战观察者模式 「您的快递已到达蜂站,请及时签收」相关推荐

  1. Java设计模式(十九):解释器设计模式

    1. 应用场景 给定一个语言后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器.客户端可以使用这个解释器来解释这个语言中的句子.(如:360读取lua脚本,这个细节的实现就是解释器模式) ...

  2. java第十九次学习笔记

    java第十九次学习笔记 异常 java第十九次学习笔记 前言 一.try catch 二.多个异常如何处理 前言 斯人若彩虹遇上方知有 一.try catch package Demo01;impo ...

  3. 【三】Java 设计模式学习记录:观察者模式

    文章目录 一.观察者模式(行为型模式) 1.1 场景 1.2 普通解决方案 1.3 观察者模式定义 1.4 观察者模式原理 二.代码实现 2.1 代码结构 2.2 上代码 2.3 扩展性 三. 框架应 ...

  4. 装饰模式——初学JAVA设计模式

    文章目录 一.基本概念 模式角色 二.简单实例 实例一.变形金刚 类图 代码实现 结果截图 实例二.喜羊羊与灰太狼 类图 方式一:半透明模式 代码实现 结果截图 方式二:半透明模式+透明模式 代码实现 ...

  5. 初学 Java 设计模式(十五):实战命令模式 「扫码点餐」

    一.命令模式介绍 1. 解决的问题 主要解决在系统中,行为请求者和行为实现者紧耦合的问题. 2. 定义 命令模式是一种行为设计模式,它可将请求转换为一个包含与请求相关的所有信息的独立对象.这个转换会根 ...

  6. java 观察者模式_重学 Java 设计模式:实战观察者模式「模拟类似小客车指标摇号过程,监听消息通知用户中签场景」...

    一.前言 知道的越多不知道的就越多 编程开发这条路上的知识是无穷无尽的,就像以前你敢说精通Java,到后来学到越来越多只想写了解Java,过了几年现在可能想说懂一点点Java.当视野和格局的扩大,会让 ...

  7. 初学 Java 设计模式(十一):实战外观模式 「类型转换器」

    一.外观模式介绍 1. 解决的问题 主要解决访问复杂系统的内部子系统的复杂度问题,简化客户端与其子系统的接口. 2. 定义 外观模式是一种结构型设计模式,能为程序库.框架或其他复杂类提供一个简单的接口 ...

  8. 初学 Java 设计模式(五):实战原型模式 「英雄联盟齐天大圣-真假猴王」

    一.原型模式介绍 1. 解决的问题 主要解决的问题就是创建重复对象,这部分对象内容本身比较复杂,生成过程可能从库中或者RPC接口中获取数据的耗时较长,因此采用克隆的方式节省时间. 2. 定义 原型模式 ...

  9. 初学 Java 设计模式(三):实战抽象工厂方法模式 「QQ 厘米秀装扮」

    一.抽象工厂方法模式介绍 1. 解决的问题 通过接口的选择,解决在系统产品存在多个产品族,而系统仅消费某一族的产品的问题. 2. 定义 抽象工厂模式是一个围绕超级工厂创建其他工厂的模式,即抽象工厂是一 ...

最新文章

  1. 嵌入式系统学习笔记之五-uboot常用命令之补充
  2. Boost--Graph
  3. 清华校友总会AI大数据专委会(筹)第一次理事会顺利召开
  4. windows十大必禁服务
  5. print_r php encode,详细介绍PHP在调试时echo print() print_r() var_dump()的区别分享
  6. 关于LDD3 setconsole.c Alesssandro Rubini 的邮件回复
  7. dropdownlist 实现无限级树形菜单
  8. 通往大神之路,百度Java面试题前200页。
  9. python3.5中import sqlite3报错:ImportError: No module named _sqlite3
  10. 剑指offer(61)序列化二叉树
  11. app上架,iCloud存储的内容过多问题。
  12. 万和计算机学院,万和亿的换算(万和亿的换算计算器)
  13. cocos入门2:骨骼动画
  14. c语言身份证号码验证
  15. html新浪短域名api,新浪短网址API接口
  16. 经典r-k法 matlab,解微分方程欧拉法,R-K法及其MATLAB实例
  17. D99、大佬都在学C/C++ - D系列总纲
  18. 计算机excel实验总结,计算机概论excel实验报告.doc
  19. 因为「Web3.0」,推特创始人被自己的投资人拉黑了
  20. 2019年7月28日 恶心人

热门文章

  1. mac释放“其他”内存空间的解决方法
  2. 2022.7.9 决定命运的战争
  3. php5.4 zend guard loader,debian(php5.4)安装 Zend Guard Loader 小记
  4. CUDA C 编程权威指南 Grossman 第9章 多GPU编程
  5. ISAKMP - 认证
  6. 安全、可靠、稳定的企业IM,WorkPlus是政企工作的首选
  7. OpenCV学习(76)
  8. mysql绿化,MySQL 的绿化与使用
  9. 村庄规划优秀典型案例(一等奖)
  10. 职场面试技巧有哪些?