1、什么是观察者模式?

Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

观察者模式(Observer Design Pattern):在对象之间定义一个一对多的依赖,当一个对象状态改变的时候,所有依赖的对象都会得到通知并自动更新。

说人话:也叫发布订阅模式,能够很好的解耦一个对象改变,自动改变另一个对象这种情况。

2、观察者模式定义

①、Subject 被观察者

定义被观察者必须实现的职责, 它必须能够动态地增加、 取消观察者。 它一般是抽象类或者是实现类, 仅仅完成作为被观察者必须实现的职责: 管理观察者并通知观察者。

②、Observer观察者

观察者接收到消息后, 即进行update(更新方法) 操作, 对接收到的信息进行处理。

③、ConcreteSubject具体的被观察者

定义被观察者自己的业务逻辑, 同时定义对哪些事件进行通知。

④、ConcreteObserver具体的观察者

每个观察在接收到消息后的处理反应是不同, 各个观察者有自己的处理逻辑。

3、观察者模式通用代码

/*** 观察者*/
public interface Observer {// 更新方法void update();
}
/*** 具体观察者*/
public class ConcreteObserver implements Observer{@Overridepublic void update() {System.out.println("接受到信息,并进行处理");}
}
/*** 被观察者*/
public abstract class Subject {// 定义一个被观察者数组private List<Observer> obsList = new ArrayList<>();// 增加一个观察者public void addObserver(Observer observer){obsList.add(observer);}// 删除一个观察者public void delObserver(Observer observer){obsList.remove(observer);}// 通知所有观察者public void notifyObservers(){for (Observer observer : obsList){observer.update();}}
}
/*** 具体被观察者*/
public class ConcreteSubject extends Subject{// 具体的业务public void doSomething(){super.notifyObservers();}
}
public class ObserverClient {public static void main(String[] args) {// 创建一个被观察者ConcreteSubject subject = new ConcreteSubject();// 定义一个观察者Observer observer = new ConcreteObserver();// 观察者观察被观察者subject.addObserver(observer);subject.doSomething();}
}

4、JDK 实现

在 JDK 的 java.util 包下,已经为我们提供了观察者模式的抽象实现,感兴趣的可以看看,内部逻辑其实和我们上面介绍的差不多。

观察者 java.util.Observer

被观察者 java.util.Observable

5、实例

用户进行注册,注册完成之后,会发一封欢迎邮件。

5.1 普通实现

public class UserController {public void register(String userName, String passWord){// 1、根据用户名密码保存在数据库Long userId = saveUser(userName, passWord);// 2、如果上一步有结果则发送一封欢迎邮件if(userId != null){Mail.sendEmail(userId);}}public Long saveUser(String userName, String passWord){return 1L;}
}

上面的注册接口实现了两件事,注册和发送邮件,很明显违反了单一职责原则,但假设这个注册需求是不是经常变动的,这样写也没有什么问题,但是假如需求变动,比如不仅要发送邮件,还得发送短信,那还这样写,那register接口会变得很复杂。

那应该如何简化呢?没错,就是观察者模式。

5.2 观察者模式实现

我们直接套用 JDK 的实现。

import java.util.Observable;/*** 用户登录——被观察者*/
public class UserControllerObservable extends Observable {public void register(String userName, String passWord){// 1、根据用户名密码保存在数据库Long userId = saveUser(userName, passWord);// 2、如果上一步有结果则通知所有观察者if(userId != null){super.setChanged();super.notifyObservers(userName);}}public Long saveUser(String userName, String passWord){return 1L;}}
import java.util.Observable;
import java.util.Observer;/*** 发送邮件——观察者*/
public class MailObserver implements Observer {@Overridepublic void update(Observable o, Object arg) {System.out.println("发送邮件:" + arg + "欢迎你");}
}
/*** 发送手机短信——观察者*/
public class SMSObserver implements Observer {@Overridepublic void update(Observable o, Object arg) {System.out.println("发送短信:" + arg + "欢迎你");}
}

测试:

public class UserClient {public static void main(String[] args) {UserControllerObservable observable = new UserControllerObservable();observable.addObserver(new MailObserver());observable.addObserver(new SMSObserver());observable.register("张三","123");}
}

通过观察者模式改写后,后面用户注册,就算在增加别的操作,我们也只需要增加一个观察者即可,而注册接口 register 不会有任何改动。

5.3 异步模式优化

在回到前面那张图:

注册之后进行的两步操作:发送邮件和发送短信,上面我们通过观察者模式改写之后,虽然流程很清晰,但是我们发现是顺序执行的,但其实这两步操作没有先后顺序,于是,我们可以改成异步模式,增加执行效率。

/*** 发送邮件——观察者*/
public class MailObserver implements Observer {private Executor executor = Executors.newFixedThreadPool(2);@Overridepublic void update(Observable o, Object arg) {executor.execute(new Runnable() {@Overridepublic void run() {System.out.println("发送邮件:" + arg + "欢迎你");}});}
}

5、EventBus

翻译为“事件总线”,它提供了实现观察者模式的骨架代码。我们可以基于此框架,非常容易地在自己的业务场景中实现观察者模式,不需要从零开始开发。其中,Google Guava EventBus 就是一个比较著名的 EventBus 框架,它不仅仅支持异步非阻塞模式,同时也支持同步阻塞模式。

PS:Google Guava 是一个特别好用的工具包,里面的代码也都实现的比较优雅,大家感兴趣的可以研究研究源码。

https://github.com/google/guava

下面我们以上面的例子来说明如何使用 EventBus:

①、导如 Guava 包

<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>30.1.1-jre</version>
</dependency>

②、具体代码如下:

import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.EventBus;import java.util.List;
import java.util.concurrent.Executors;public class UserController {private EventBus eventBus;public UserController(){eventBus = new AsyncEventBus(Executors.newFixedThreadPool(2));}/*** 注意:泛型参数是 Object,而不是接口 Observer* @param observerList*/public void setObserverList(List<Object> observerList){for(Object observer : observerList){eventBus.register(observer);}}public void register(String userName, String passWord){// 1、根据用户名密码保存在数据库Long userId = saveUser(userName, passWord);// 2、如果上一步有结果则通知所有观察者if(userId != null){eventBus.post(userName);}}public Long saveUser(String userName, String passWord){return 1L;}
}
import com.google.common.eventbus.Subscribe;/*** 发送邮件——观察者*/
public class MailObserver{@Subscribepublic void sendMail(String userName) {System.out.println("发送邮件:" + userName + "欢迎你");}
}
import com.google.common.eventbus.Subscribe;/*** 发送手机短信——观察者*/
public class SMSObserver{@Subscribepublic void sendSMS(String userName) {System.out.println("发送短信:" + userName + "欢迎你");}
}

测试:

public class EventBusClient {public static void main(String[] args) {UserController userController = new UserController();List<Object> observerList = new ArrayList<>();observerList.add(new MailObserver());observerList.add(new SMSObserver());userController.setObserverList(observerList);userController.register("张三","123");}
}

利用 EventBus 框架实现的观察者模式,跟从零开始编写的观察者模式相比,从大的流程上来说,实现思路大致一样,都需要定义 Observer,并且通过 register() 函数注册 Observer,也都需要通过调用某个函数(比如,EventBus 中的 post() 函数)来给 Observer 发送消息(在 EventBus 中消息被称作事件 event)。但在实现细节方面,它们又有些区别。基于 EventBus,我们不需要定义 Observer 接口,任意类型的对象都可以注册到 EventBus 中,通过 @Subscribe 注解来标明类中哪个函数可以接收被观察者发送的消息。

6、观察者模式优点

①、观察者和被观察者之间是抽象耦合

不管是增加观察者还是被观察者都非常容易扩展,在系统扩展方面会得心应手。

②、建立一套触发机制

被观察者变化引起观察者自动变化。但是需要注意的是,一个被观察者,多个观察者,Java的消息通知默认是顺序执行的,如果一个观察者卡住,会导致整个流程卡住,这就是同步阻塞。

所以实际开发中没有先后顺序的考虑使用异步,异步非阻塞除了能够实现代码解耦,还能充分利用硬件资源,提高代码的执行效率。

另外还有进程间的观察者模式,通常基于消息队列来实现,用于实现不同进程间的观察者和被观察者之间的交互。

7、观察者模式应用场景

①、关联行为场景。

②、事件多级触发场景。

③、跨系统的消息交换场景, 如消息队列的处理机制。

Java设计模式之(十二)——观察者模式相关推荐

  1. Java设计模式(十二) 策略模式

    策略模式介绍 策略模式定义 策略模式(Strategy Pattern),将各种算法封装到具体的类中,作为一个抽象策略类的子类,使得它们可以互换.客户端可以自行决定使用哪种算法. 策略模式类图 策略模 ...

  2. 【每天一个java设计模式(十八)】 - 观察者模式

    观察者模式的定义:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新.这种模式有时又称作发布-订阅模式.模型-视图模式,它是对象行为型模式. 观察者 ...

  3. 【每天一个java设计模式(十五)】 - 命令模式

    命令模式是一种数据驱动的设计模式,它属于行为型模式.请求以命令的形式包裹在对象中,并传给调用对象.调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令. 命令模式也就是一个 ...

  4. 【每天一个java设计模式(十四)】 - 解释器模式

    解释器模式提供了评估语言的语法或表达式的方式,它属于行为型模式.这种模式实现了一个表达式接口,该接口解释一个特定的上下文. 解释器模式主要包括以下几个部分: 抽象表达式角色:定义解释器的接口,约定解释 ...

  5. C#设计模式之十六观察者模式(Observer Pattern)【行为型】

    C#设计模式之十六观察者模式(Observer Pattern)[行为型] 原文:C#设计模式之十六观察者模式(Observer Pattern)[行为型] 一.引言 今天是2017年11月份的最后一 ...

  6. 二十三种设计模式(第十二种)-----代理模式(Proxy)

    二十三种设计模式(第十二种)-----代理模式(Proxy) 尚硅谷视频连接https://www.bilibili.com/video/BV1G4411c7N4?from=search&se ...

  7. 疯狂JAVA讲义---第十二章:Swing编程(五)进度条和滑动条

    http://blog.csdn.net/terryzero/article/details/3797782 疯狂JAVA讲义---第十二章:Swing编程(五)进度条和滑动条 标签: swing编程 ...

  8. (原创)无废话C#设计模式之十二:Bridge

    无废话C#设计模式之十二:Bridge 意图 将抽象部分与实现部分分离,使它们都可以独立的变化. 场景 还是说我们要做的网络游戏,多个场景需要扩充的问题我们已经采用了创建型模式来解决.现在的问题就是, ...

  9. 设计模式(十二)—— 享元模式

    设计模式(十二)-- 享元模式 定义 结构 案例实现 优缺点和使用场景 JDK源码解析 定义 运用共享技术来有效地支持大量细粒度对象的复用.它通过共享已经存在的对象来大幅度减少需要创建的对象数量.避免 ...

  10. 云计算设计模式(十二)——索引表模式

    云计算设计模式(十二)--索引表模式 创建索引过的被查询条件经常被引用的数据存储等领域.这种模式可以通过允许应用程序更快速地定位数据来从数据存储中检索提高查询性能. 背景和问题 许多数据存储通过使用主 ...

最新文章

  1. 借力英伟达打造车规L3智能驾驶大脑,揭开德赛西威量产的“秘密”
  2. 【数理知识】方程一阶二阶及常用词语含义
  3. linux 信号量锁 内核,Linux内核中锁机制之信号量、读写信号量
  4. int类型在计算机中的储存(原码、补码、反码)
  5. 动态规划算法-04最长递增子序列问题
  6. 深度剖析开源分布式监控CAT
  7. 携程:2021国庆高星酒店均价945元 环比节前一周提升超80%
  8. 使用ENVI5.3构建时序数据展示
  9. 3ds Max 中的导航控件SteeringWheels入门介绍
  10. 钢铁雄心II(HOI2)作弊码合辑
  11. java判断日期是否为工作日(排除节假日和调整周末上班)
  12. 我九点钟上计算机课用英语怎么说,人教版四年级英语下册 第5次课 Unit 2 What time is it 时间的表达方式...
  13. Struts2 DTD与XML文件编写
  14. 医保业务综合服务终端技术规范_增值税发票综合服务平台出口退税业务操作指引...
  15. 方舟原始恐惧mod生物代码_《暗黑地牢》《方块方舟》周末免费试玩,多款游戏历史新低特惠...
  16. 品牌笔记本预装windows的OEM分区解决方案(联想)
  17. 电脑哪个服务器可以玩无限连击,无尽之剑3手把手教你无限连击攻略
  18. python解析xml读取指定属性_python批量修改xml某些内容和属性
  19. 2021年爱尔兰卫生医疗系统勒索病毒事件
  20. 超级白熊啤酒虎年限定礼盒正式发售;迪桑特限量发售新款针织滑雪服 | 知消...

热门文章

  1. Linux——基于GPU的超低延迟远程桌面Parsec
  2. Cisco(63)——多出口PBR+NAT解决方案
  3. Spring Boot简介,四大核心,特性
  4. Mott-insulator transitions in BEC
  5. 数位DP入门笔记(1)HUD-2089
  6. 程序猿的十年—新猿农计划
  7. Wireshark基础使用,SSL解密及http抓包入门教程
  8. 我的面试题. 业务抽象能力测试.
  9. ajax常见面试问题
  10. 安卓设备逐步升级Android 9,游戏产品该如何做适配?