网站首页 > java教程 正文
大家好,你还在面向 for 循环编程吗?
还有谁不会用观察者模式吗?
本篇栈长带来《观察者模式》理论及实战~
什么是观察者模式?
观察者模式(Observer Pattern)定义了对象间的一种一对多的依赖关系,这样只要一个对象的状态发生改变,其依赖的所有相关对象都会得到通知并自动更新。
在观察者模式中,发生改变的对象叫做观察目标,而被通知更新的对象称为观察者,一个观察目标对应多个观察者,观察者一般是一个列表集合,可以根据需要动态增加和删除,易于扩展。
使用观察者模式的优点在于观察目标和观察者之间是抽象松耦合关系,降低了两者之间的耦合关系。
发布-订阅模式
观察者模式很多地方也叫发布-订阅模式(Publish/Subscribe),其实也可以这么理解,不过两者之间还是略有不同。
观察者模式中的观察者是直接绑定观察目标,观察目标要维护一套观察者列表,两者是有一个基于接口的组合依赖关系的,所以说观察者模式虽然是松耦合的,但并不是完全解耦的。
而发布-订阅模式中的发布者和订阅者两者并没有任何联系,发布者通过中间方发布一个主题(Topic),订阅者通过中间方(调度中心)订阅一个主题(Topic),发布者状态的变更也并不会直接通知订阅者,而要通过中间方进行通知,或者订阅者自行从中间方拉取,所以说发布-订阅模式是完全解耦的。
一图搞懂它们的关系:
从图片看两者是有差别的,统一都叫观察者模式,也没毛病。
观察者模式轮子
因观察者模式应用比较广泛,所以 JDK 工具包从 1.0 版本里面自带了观察者模式模板套装,我们根据其模板很方便就能实现观察者模式,不需要再重复造轮子了。
观察者目标类:
java.util.Observable
里面两个最重要的变量:
- changed:观察目标状态是否变更,默认为:false;
- obs:观察者列表(observers),一个线程安全的列表集合:Vector,默认为空集合;
里面的重要的方法都是和观察目标状态和观察者相关的,一看就清楚,这里就不介绍了。
观察者接口:
java.util.Observable
public interface Observer {
/**
* This method is called whenever the observed object is changed. An
* application calls an <tt>Observable</tt> object's
* <code>notifyObservers</code> method to have all the object's
* observers notified of the change.
*
* @param o the observable object.
* @param arg an argument passed to the <code>notifyObservers</code>
* method.
*/
void update(Observable o, Object arg);
}
观察者接口只有一个 update 方法,用来通知观察者自己更新。
观察者模式实战
OK,知道了 JDK 自带了这两个东东,现在就来实现一个简单的观察者模式的应用场景,模拟公众号文章推送,观察目标是栈长我,观察者是你们大家,我在公众号推一篇文章,你们都能接收到更新通知并能阅读。
新增观察目标类:
import lombok.Getter;
import java.util.Observable;
/**
* 观察目标:栈长
* 来源:Java技术栈
*/
@Getter
public class JavaStackObservable extends Observable {
private String article;
/**
* 发表文章
* @param article
*/
public void publish(String article){
// 发表文章
this.article = article;
// 改变状态
this.setChanged();
// 通知所有观察者
this.notifyObservers();
}
}
观察目标的逻辑是先发表文章,再改变观察目标的状态,再通知所有观察者。
我们来重点看 notifyObservers 方法的源码:
先获取同步锁,判断状态是否更新,如已更新则清空观察目标状态,然后再使用 for 循环遍历所有观察者,一一调用观察者的更新方法通知观察者更新。
新增观察者类:
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import java.util.Observable;
import java.util.Observer;
/**
* 观察者:读者粉丝
* 来源:Java技术栈
*/
@RequiredArgsConstructor
public class ReaderObserver implements Observer {
@NonNull
private String name;
private String article;
@Override
public void update(Observable o, Object arg) {
// 更新文章
updateArticle(o);
}
private void updateArticle(Observable o) {
JavaStackObservable javaStackObservable = (JavaStackObservable) o;
this.article = javaStackObservable.getArticle();
System.out.printf("我是读者:%s,文章已更新为:%s\n", this.name, this.article);
}
}
观察者的逻辑是获取到观察者目标实例对象,然后再用观察目标对象的文章信息更新为自己的文章信息,最后输出某某某的文章已更新。
观察者只要实现 Observer 这个接口的 update 方法即可,用于观察目标进行调用通知。
本节教程所有实战源码已上传到这个仓库:
https://github.com/javastacks/javastack
观察目标和观察者类结构图如下:
新增测试类:
/**
* 观察者:读者粉丝
* 来源:Java技术栈
*/
public class ObserverTest {
public static void main(String[] args) {
// 创建一个观察目标
JavaStackObservable javaStackObservable = new JavaStackObservable();
// 添加观察者
javaStackObservable.addObserver(new ReaderObserver("小明"));
javaStackObservable.addObserver(new ReaderObserver("小张"));
javaStackObservable.addObserver(new ReaderObserver("小爱"));
// 发表文章
javaStackObservable.publish("什么是观察者模式?");
}
}
观察目标、观察者的创建并没有先后顺序要求,重点是发表文章通知观察者之前,观察目标要添加观察者列表这一步不能少。
输出结果:
通过这个简单的文章推送实践,大家应该对观察者模式有一个基本的认知了,在实际工作当中也可以有很多场景拿去用,就一对多的依赖关系都可以考虑使用观察者模式。
总结
不容易啊,陆陆续续又肝了大半天,你学会观察者模式了吗?
观察者模式的优点是为了给观察目标和观察者解耦,而缺点也很明显,从上面的例子也可以看出,如果观察者对象太多的话,有可能会造成内存泄露。
另外,从性能上面考虑,所有观察者的更新都是在一个循环中排队进行的,所以观察者的更新操作可以考虑做成线程异步(或者可以使用线程池)的方式,以提升整体效率。
本节教程所有实战源码已上传到这个仓库:
https://github.com/javastacks/javastack
好了,今天的分享就到这里了,后面栈长我会更新其他设计模式的实战文章,Java技术栈第一时间推送。Java技术栈《设计模式》系列文章陆续更新中,请大家持续关注哦!
最后,觉得我的文章对你用收获的话,动动小手,给个在看、转发,原创不易,栈长需要你的鼓励。
版权申明:本文系 "Java技术栈" 原创,原创实属不易,转载、引用本文内容请注明出处,禁止抄袭、洗稿,请自重,尊重他人劳动成果和知识产权。
猜你喜欢
- 2024-10-10 架构师必须搞懂DNS,一篇文章 就够了
- 2024-10-10 Spring Boot 最核心的 25 个注解,都是干货!
- 2024-10-10 Spring Cloud 如何动态刷新 Git 仓库配置?
- 2024-10-10 公司来了个新同事不会用 Lombok,还说我代码有问题
- 2024-10-10 Spring Boot 怎么打一个可执行 Jar 包?
- 2024-10-10 你必须了解Spring的生态(spring 生态)
- 2024-10-10 Java技术栈ppt分享:大数据+架构篇+算法篇(阿里巴巴内部资料)
- 2024-10-10 腾讯牛逼!终于开源了自家的 JDK——Kona
- 2024-10-10 Spring Boot Redis 实现分布式锁,真香
- 2024-10-10 响应式微服务架构设计,面向未来Java技术栈
你 发表评论:
欢迎- 最近发表
-
- Java常量定义防暴指南:从"杀马特"到"高富帅"的华丽转身
- Java接口设计原则与实践:优雅编程的艺术
- java 包管理、访问修饰符、static/final关键字
- Java工程师的代码规范与最佳实践:优雅代码的艺术
- 编写一个java程序(编写一个Java程序计算并输出1到n的阶乘)
- Mycat的搭建以及配置与启动(mycat部署)
- Weblogic 安装 -“不是有效的 JDK Java 主目录”解决办法
- SpringBoot打包部署解析:jar包的生成和结构
- 《Servlet》第05节:创建第一个Servlet程序(HelloSevlet)
- 你认为最简单的单例模式,东西还挺多
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)