你们好,你还在面向 for 循环编程吗?java
还有谁不会用观察者模式吗?git
本篇栈长带来《观察者模式》理论及实战~github
观察者模式(Observer Pattern)定义了对象间的一种一对多的依赖关系,这样只要一个对象的状态发生改变,其依赖的全部相关对象都会获得通知并自动更新。编程
在观察者模式中,发生改变的对象叫作观察目标,而被通知更新的对象称为观察者,一个观察目标对应多个观察者,观察者通常是一个列表集合,能够根据须要动态增长和删除,易于扩展。设计模式
使用观察者模式的优势在于观察目标和观察者之间是抽象松耦合关系,下降了二者之间的耦合关系。安全
观察者模式不少地方也叫发布-订阅模式(Publish/Subscribe),其实也能够这么理解,不过二者之间仍是略有不一样。微信
观察者模式中的观察者是直接绑定观察目标,观察目标要维护一套观察者列表,二者是有一个基于接口的组合依赖关系的,因此说观察者模式虽然是松耦合的,但并非彻底解耦的。app
而发布-订阅模式中的发布者和订阅者二者并无任何联系,发布者经过中间方发布一个主题(Topic),订阅者经过中间方(调度中心)订阅一个主题(Topic),发布者状态的变动也并不会直接通知订阅者,而要经过中间方进行通知,或者订阅者自行从中间方拉取,因此说发布-订阅模式是彻底解耦的。异步
一图搞懂它们的关系:ide
从图片看二者是有差异的,统一都叫观察者模式,也没毛病。
因观察者模式应用比较普遍,因此 JDK 工具包从 1.0 版本里面自带了观察者模式模板套装,咱们根据其模板很方便就能实现观察者模式,不须要再重复造轮子了。
观察者目标类:
java.util.Observable
里面两个最重要的变量:
里面的重要的方法都是和观察目标状态和观察者相关的,一看就清楚,这里就不介绍了。
观察者接口:
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 自带了这两个东东,如今就来实现一个简单的观察者模式的应用场景,模拟公众号文章推送,观察目标是栈长我,观察者是大家你们,我在公众号Java技术栈推一篇文章,大家都能接收到更新通知并能阅读。
新增观察目标类:
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("什么是观察者模式?"); } }
观察目标、观察者的建立并无前后顺序要求,重点是发表文章通知观察者以前,观察目标要添加观察者列表这一步不能少。
输出结果:
经过这个简单的文章推送实践,你们应该对观察者模式有一个基本的认知了,在实际工做当中也能够有不少场景拿去用,就一对多的依赖关系均可以考虑使用观察者模式。
不容易啊,陆陆续续又肝了大半天,你学会观察者模式了吗?
观察者模式的优势是为了给观察目标和观察者解耦,而缺点也很明显,从上面的例子也能够看出,若是观察者对象太多的话,有可能会形成内存泄露。
另外,从性能上面考虑,全部观察者的更新都是在一个循环中排队进行的,因此观察者的更新操做能够考虑作成线程异步(或者可使用线程池)的方式,以提高总体效率。
本节教程全部实战源码已上传到这个仓库:
好了,今天的分享就到这里了,后面栈长我会更新其余设计模式的实战文章,公众号Java技术栈第一时间推送。Java技术栈《设计模式》系列文章陆续更新中,请你们持续关注哦!
最后,以为个人文章对你用收获的话,动动小手,给个在看、转发,原创不易,栈长须要你的鼓励。
版权申明:本文系公众号 "Java技术栈" 原创,原创实属不易,转载、引用本文内容请注明出处,禁止抄袭、洗稿,请自重,尊重他人劳动成果和知识产权。