别再面向 for 循环编程了,JDK 自带的观察者模式就很香!

简介: 大家好,你还在面向 for 循环编程吗?还有谁不会用观察者模式吗?

大家好,你还在面向 for 循环编程吗?


还有谁不会用观察者模式吗?


本篇栈长带来《观察者模式》理论及实战~


什么是观察者模式?

观察者模式(Observer Pattern)定义了对象间的一种一对多的依赖关系,这样只要一个对象的状态发生改变,其依赖的所有相关对象都会得到通知并自动更新。


在观察者模式中,发生改变的对象叫做观察目标,而被通知更新的对象称为观察者,一个观察目标对应多个观察者,观察者一般是一个列表集合,可以根据需要动态增加和删除,易于扩展。


使用观察者模式的优点在于观察目标和观察者之间是抽象松耦合关系,降低了两者之间的耦合关系。


发布-订阅模式

观察者模式很多地方也叫发布-订阅模式(Publish/Subscribe),其实也可以这么理解,不过两者之间还是略有不同。


观察者模式中的观察者是直接绑定观察目标,观察目标要维护一套观察者列表,两者是有一个基于接口的组合依赖关系的,所以说观察者模式虽然是松耦合的,但并不是完全解耦的。


而发布-订阅模式中的发布者和订阅者两者并没有任何联系,发布者通过中间方发布一个主题(Topic),订阅者通过中间方(调度中心)订阅一个主题(Topic),发布者状态的变更也并不会直接通知订阅者,而要通过中间方进行通知,或者订阅者自行从中间方拉取,所以说发布-订阅模式是完全解耦的。


一图搞懂它们的关系:


image.png


从图片看两者是有差别的,统一都叫观察者模式,也没毛病。


观察者模式轮子

因观察者模式应用比较广泛,所以 JDK 工具包从 1.0 版本里面自带了观察者模式模板套装,我们根据其模板很方便就能实现观察者模式,不需要再重复造轮子了。


观察者目标类:


java.util.Observable


image.png


里面两个最重要的变量:


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 自带了这两个东东,现在就来实现一个简单的观察者模式的应用场景,模拟公众号文章推送,观察目标是栈长我,观察者是你们大家,我在公众号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 方法的源码:

image.png

先获取同步锁,判断状态是否更新,如已更新则清空观察目标状态,然后再使用 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


观察目标和观察者类结构图如下:

image.png

新增测试类:

/**
 * 观察者:读者粉丝
 * 来源微信公众号: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("什么是观察者模式?");
    }
}

观察目标、观察者的创建并没有先后顺序要求,重点是发表文章通知观察者之前,观察目标要添加观察者列表这一步不能少。

输出结果:

image.png

通过这个简单的文章推送实践,大家应该对观察者模式有一个基本的认知了,在实际工作当中也可以有很多场景拿去用,就一对多的依赖关系都可以考虑使用观察者模式。


总结

不容易啊,陆陆续续又肝了大半天,你学会观察者模式了吗?


观察者模式的优点是为了给观察目标和观察者解耦,而缺点也很明显,从上面的例子也可以看出,如果观察者对象太多的话,有可能会造成内存泄露。


另外,从性能上面考虑,所有观察者的更新都是在一个循环中排队进行的,所以观察者的更新操作可以考虑做成线程异步(或者可以使用线程池)的方式,以提升整体效率。


本节教程所有实战源码已上传到这个仓库:


https://github.com/javastacks/javastack


相关文章
|
1月前
|
设计模式 Java 开发者
一目了然!谁能想到Java多线程设计模式竟然被图解,看完不服不行
多线程设计模式在Java编程中起着至关重要的作用,它能够有效提高程序的执行效率,使得程序在处理大量数据和复杂任务时更加高效。然而,对于初学者来说,理解和应用多线程设计模式可能是一项相当具有挑战性的任务。为了让读者更加轻松地掌握这一复杂主题,我们带着一种全新的图解方式,深入剖析Java多线程设计模式的精髓。
|
1月前
|
Java
【JAVA杂货铺】一文带你走进面向对象编程|构造方法调用 | 代码块分类| 期末复习系列 | (中3)
【JAVA杂货铺】一文带你走进面向对象编程|构造方法调用 | 代码块分类| 期末复习系列 | (中3)
19 0
|
1月前
|
设计模式 存储 缓存
二十三种设计模式全面解析-探索解释器模式的高级应用和优化技巧:解锁代码解析的新境界
二十三种设计模式全面解析-探索解释器模式的高级应用和优化技巧:解锁代码解析的新境界
|
8月前
|
设计模式 存储 安全
Java的第十三篇文章——JAVA多线程(后期再学一遍)
Java的第十三篇文章——JAVA多线程(后期再学一遍)
|
设计模式 前端开发
第35/90步《番外篇》第2章 面向对象和设计模式实例讲解 第7课
今天学习《番外篇》第2章 面向对象和设计模式实例讲解 第7课 面向对象的6个基本原则,这节课我们首先看一看设计模式背后蕴藏的面向对象思想,以此作为我们打开设计模式殿堂大门的开端。学习设计模式,重在学习面向对象的基本思想,而非模式本身。
34 0
|
设计模式
第37/90步《番外篇》第2章 面向对象和设计模式实例讲解 第9课
今天学习《番外篇》第2章 面向对象和设计模式实例讲解 第9课 创建型模式二:建造者模式和原型模式,这节课练习建造者模式和原型模式。
35 0
|
设计模式 安全 Java
通俗易懂,值得收藏的 java 设计模式实战,装饰者模式 之 你不用改变,就让你的能力变强了
装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
通俗易懂,值得收藏的 java 设计模式实战,装饰者模式 之 你不用改变,就让你的能力变强了
|
Java 开发者
JDK1.8 新特性之Lambda表达式,你学废了吗?
目录 一、前言 二、Lambda表达式入门 1、Lambada表达式示例 2、Lambda表达式语法说明 3、更多示例 三、Lambda表达式与函数式接口 1、Supplier接口 2、Consumer接口 3、Predicate接口 4、Function接口 四、方法引用与构造器引用 五、方法引用和构造器引用示例 1、引用类方法 2、引用特定对象的实例方法 (1) 示例1:字符串转换 (2) 示例2:反射 3、引用某类对象的实例方法 4、构造器引用
JDK1.8 新特性之Lambda表达式,你学废了吗?
|
C++
爱上c++的第二天:类和对象的三大特性-封装
对只写权限,我们可以检测数据的有效性(不能让用户修改程序,即用户不能访问内部程序,以防用户进行非法操作)
99 0
|
存储 Java 数据库连接
写了这么久代码你了解Java面向对象的设计原则吗?(三)
写了这么久代码你了解Java面向对象的设计原则吗?
94 0
写了这么久代码你了解Java面向对象的设计原则吗?(三)

热门文章

最新文章