目录
观察者模式【Observer Pattern】,什么是观察者模式?作用?优缺点?观察者模式实现?
什么是观察者模式?
观察者模式 (Observer Pattern) 又叫发布-订阅模式 (Publish/Subscribe),是一种行为型设计模式,它定义了一种一对多的依赖关系,当一个对象的状态发生改变时,其所有依赖者都会收到通知并自动更新。
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。
观察者模式的核心是将观察者与主题对象解耦,以类似于消息、广播发送的机制联动两者,使被观察者的变动能通知到感兴趣的观察者们,从而做出相应的响应。
观察者模式作用?
创建了对象间的一种一对多的依赖关系,当一个对象状态改变时,所有依赖于它的对象都会得到通知并自动更新。
观察者模式优缺点
优点
抽象耦合:观察者和主题之间是抽象耦合的。
触发机制:建立了一套状态改变时的触发和通知机制。
缺点
性能问题:如果观察者众多,通知过程可能耗时。
循环依赖:可能导致循环调用和系统崩溃。
缺乏变化详情:观察者不知道主题如何变化,只知道变化发生。
观察者模式包含角色
(1)抽象主题 (Subject)
指被观察的对象 (Observable)。该角色是一个抽象类或接口,定义了增加、删除、通知观察者对象的方法
(2)具体主题 (Concrete Subject)
具体被观察者,当其内部状态变化时,会通知已注册的观察者
(3)抽象观察者 (Observer)
定义了响应通知的更新方法
(4)具体观察者 (Concrete Observer)
在得到状态更新时,会自动做出响应
主题(Subject):也称为被观察者或可观察者,它是具有状态的对象,并维护着一个观察者列表。主题提供了添加、删除和通知观察者的方法。
观察者(Observer):观察者是接收主题通知的对象。观察者需要实现一个更新方法,当收到主题的通知时,调用该方法进行更新操作。
具体主题(Concrete Subject):具体主题是主题的具体实现类。它维护着观察者列表,并在状态发生改变时通知观察者。
具体观察者(Concrete Observer):具体观察者是观察者的具体实现类。它实现了更新方法,定义了在收到主题通知时需要执行的具体操作。
观察者模式实现方式
(1)定义观察者接口:包含一个更新方法。
(2)创建具体观察者:实现观察者接口,定义接收到通知时的行为。
(3)定义主题接口:包含添加、删除和通知观察者的方法。
(4)创建具体主题:实现主题接口,管理观察者列表,并在状态改变时通知它们。
观察者模式实现
《孙子兵法》有云:“知彼知己,百战不殆;不知彼而知己,一胜一负;不 知彼,不知己,每战必殆”,
那怎么才能知己知彼呢?
知己是很容易的,自己的军队嘛,很容易知道。
那怎么知彼呢?
安插间谍是很好的一个办法。 韩非子大家都应该记得吧,法家的代表人物,主张建立法制社会,实施重罚制度,真是非常有远见呀,看看现在社会在呼吁什么,建立法制化的社会,在 2000 多年前就已经提出了。大家可能还不知道,法家还有一个非常重要的代表人物,李斯,对,就是李斯,秦国的丞相,最终被残忍的车裂的那位,李斯和韩非子都是荀子的学生,李斯是师兄,韩非子是师弟,若干年后,李斯成为最强诸侯秦国的上尉,致力于统一全国,于是安插了间谍到各个国家的重要人物的身边,以获取必要的信息,韩非子作为韩国的重量级人物,身边自然没少间谍了,韩非子早饭吃的什么,中午放了几个 P,晚上在做什么娱乐,李斯都了如指掌,那可是相隔千里!怎么做到的呢?间谍呀!
1、实现Observable(被观察者)
JDK 中提供了 :java.util.Observable 实现类和 java.util.Observer 接口。
package com.uhhe.common.design.observer;
import java.util.Observable;
/**
* 韩非子,李斯的师弟,韩国的重要人物
*
* @author nizhihao
* @version 1.0.0
* @date 2023/3/1 17:06
*/
public class HanFeiZi extends Observable {
/**
* 韩非子要吃饭了
*/
public void haveBreakfast() {
System.out.println("韩非子:开始吃饭了...");
// 通知所有的观察者
super.setChanged();
this.notifyObservers("韩非子在吃饭");
}
/**
* 韩非子开始娱乐了,古代人没啥娱乐,你能想到的就那么多
*/
public void haveFun() {
System.out.println("韩非子:开始娱乐了...");
super.setChanged();
this.notifyObservers("韩非子在娱乐");
}
}
2、实现Observer(李斯、王斯、刘斯)
李斯
package com.uhhe.common.design.observer;
import java.util.Observable;
import java.util.Observer;
/**
* 李斯这个人,是个观察者,只要韩非子一有动静,这边就知道
*
* @author nizhihao
* @version 1.0.0
* @date 2023/3/1 17:09
*/
public class LiSi implements Observer {
/**
* 首先李斯是个观察者,一旦韩非子有活动,他就知道,他就要向老板汇报
*
* @param observable 观察者
* @param obj 报告内容
*/
@Override
public void update(Observable observable, Object obj) {
System.out.println("李斯:观察到李斯活动,开始向老板汇报了...");
this.reportToQiShiHuang(obj.toString());
System.out.println("李斯:汇报完毕,秦老板赏给他两个萝卜吃吃...\n");
}
/**
* 汇报给秦始皇
*
* @param reportContext 报告内容
*/
private void reportToQiShiHuang(String reportContext) {
System.out.println("李斯:报告,秦老板!韩非子有活动了--->" + reportContext);
}
}
刘斯
package com.uhhe.common.design.observer;
import java.util.Observable;
import java.util.Observer;
/**
* 刘斯这个人,是个观察者,只要韩非子一有动静,这边就知道
*
* @author nizhihao
* @version 1.0.0
* @date 2023/3/1 17:11
*/
public class LiuSi implements Observer {
@Override
public void update(Observable observable, Object obj) {
//刘斯,观察到韩非子活动后,自己也做一定得事情
System.out.println("刘斯:观察到韩非子活动,开始动作了...");
this.happy(obj.toString());
System.out.println("刘斯:真被乐死了\n");
}
private void happy(String context) {
//一看韩非子有变化,他就快乐
System.out.println("刘斯:因为" + context + ",——所以我快乐呀!");
}
}
王斯
package com.uhhe.common.design.observer;
import java.util.Observable;
import java.util.Observer;
/**
* 王斯,也是观察者,杜撰的人名
*
* @author nizhihao
* @version 1.0.0
* @date 2023/3/1 17:11
*/
public class WangSi implements Observer {
@Override
public void update(Observable observable, Object obj) {
// 王斯,看到韩非子有活动,自己就受不了
System.out.println("王斯:观察到韩非子活动,自己也开始活动了...");
this.cry(obj.toString());
System.out.println("王斯:真真的哭死了...\n");
}
private void cry(String context) {
// 一看李斯有活动,就哭,痛哭
System.out.println("王斯:因为" + context + ",——所以我悲伤呀!");
}
}
3、Client(使用观察者)
package com.uhhe.common.design.observer;
import java.util.Observer;
/**
* 使用观察者
*
* @author nizhihao
* @version 1.0.0
* @date 2023/3/1 17:04
*/
public class Client {
/**
* 观察者模式【Observer Pattern】
* 观察者模式有一个变种叫做发布/订阅模型(Publish/Subscribe)
* <p>
* 观察者模式在实际项目的应用中非常常见
* 比如: 到 ATM 机器上取钱,多次输错密码,卡就会被 ATM 吞掉,吞卡动作发生的时候,会触发哪些事件呢?
* 第一,摄像头连续快拍
* 第二,通知监控系统,吞卡发生;
* 第三,初始化 ATM 机屏幕,返回最初状态
* 不能因为就吞了一张卡,整个 ATM 都不能用了吧,一般前两个动作都是通过观察者模式来完成的
* <p>
* 广播链的问题?
* 如果你做过数据库的触发器,你就应该知道有一个触发器链的问题,比如表 A 上写了一个触发器,
* 内容是一个字段更新后更新表 B 的一条数据,而表 B 上也有个触发器,要更新表 C,表 C 也有
* 触发器…,完蛋了,这个数据库基本上就毁掉了!我们的观察者模式也是一样的问题,一个观察者可以有双
* 重身份,即使观察者,也是被观察者,这没什么问题呀,但是链一旦建立,这个逻辑就比较复杂,可维护
* 性非常差,根据经验建议,在一个观察者模式中最多出现一个对象既是观察者也是被观察者,也就是说消
* 息最多转发一次(传递两次),这还是比较好控制的
* <p>
* 异步处理问题?
* 这个 EJB 是一个非常好的例子,被观察者发生动作了,观察者要做出回应,如果观察者比较多,
* 而且处理时间比较长怎么办?那就用异步呗,异步处理就要考虑线程安全和队列的问题,这个
* 大家有时间看看 Message Queue,就会有更深的了解。
* <p>
* 工厂方法模式的时候用到了 ClassUtils 这个类,其中有一个方法就是根据接口查找到所有的实现类,问题解决了吧!
* 可以查找到所有的观察者,然后全部加进来,以后要是新增加观察者也没有问题呀,程序那真是一点都不用改了
*/
public static void main(String[] args) {
// 三个观察者产生出来
Observer liSi = new LiSi();
Observer wangSi = new WangSi();
Observer liuSi = new LiuSi();
// 定义出韩非子
HanFeiZi hanFeiZi = new HanFeiZi();
// 后人根据历史,描述这个场景,有三个人在观察韩非子
hanFeiZi.addObserver(liSi);
hanFeiZi.addObserver(wangSi);
hanFeiZi.addObserver(liuSi);
// 然后这里我们看看韩非子在干什么
hanFeiZi.haveBreakfast();
}
}