专栏首页Spring相关一文看懂观察者模式及案例详解

一文看懂观察者模式及案例详解

一、基本介绍

​ 观察者模式是一种对象行为模式。它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。在观察者模式中,主题是通知的发布者,它发出通知时并不需要知道谁是它的观察者,可以有任意数目的观察者订阅并接收通知。观察者模式不仅被广泛应用于软件界面元素之间的交互,在业务对象之间的交互、权限管理等方面也有广泛的应用。 ​ —— 引用自百度百科

二、模式的定义与特点

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

  1. 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。
  2. 目标与观察者之间建立了一套触发机制。

它的主要缺点如下:

  • 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
  • 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。

三、模式的结构与实现

​ 实现观察者模式时要注意具体目标对象和具体观察者对象之间不能直接调用,否则将使两者之间紧密耦合起来,这违反了面向对象的设计原则。 观察者模式的主要角色如下:

KuPg2D.png

四、具体实现步骤

相关场景描述: ​ 某天的下午,班主任通知某班学生和老师将要听一节课,以此来对老师的授课质量进行评分。老师和学生收到后开始安排相关的课程。上课期间老师和班主任通过观察学生的神情来预判课程的讲的好坏,老师观察到学生皱眉头可以适当调节课程气氛,班主任观察到学生课堂氛围好转,给与高分。课程结束后,班主任和老师回到办公室,一起回顾这节开心的课程。

使用自定义的方式实现观察者模式

1.构建一个课程实体类(以下均省略Setter和Getter)

/**
 * 课程类
 * - 上课时间:time
 * - 上课地点:place
 * - 上课内容:content
 */
public class Course {

    private Date time;
    private String place;
    private String content;

    public Course() {
    }
    
    public Course(Date time, String place, String content) {
        this.time = time;
        this.place = place;
        this.content = content;
    }
}

2.构建一个发现者的抽象类以及相关的实现类,老师和班主任都分别继承了该接口并重写相关方法,

public abstract class Observer {
    abstract void update(Object args);
    public Observer(String identity) {
        this.identity = identity;
    }
    private String identity;
}

老师拿着教材开始来上课:

/**
 * 老师类
 * - 观察者之一
 * - 观察学生的上课情况
 */
public class TeacherObserver extends Observer {

    private Course course;
    @Override
    public void update(Object args) {
        DateFormat df = DateFormat.getTimeInstance(DateFormat.LONG, Locale.CHINA);
        System.out.println("我是老师,正在讲课中...");
        course = new Course(new Date(), "A栋教学楼", "Java课程");
        System.out.println("今天上课时间:"+df.format(course.getTime()) + " 地点:" + course.getPlace() + " 上课内容:" + course.getContent());
    }

    public TeacherObserver(String identity) {
        super(identity);
    }
}

班主任来听课:

/**
 * 班主任来听课
 * - 观察者之一
 * - 观察学生的上课情况
 */
public class HeadTeacherObserver extends Observer {
    @Override
    public void update(Object args) {
        System.out.println("我是班主任来听课了,正在检查课程质量...");
        System.out.println("学生反馈课程质量为:" + args);
    }

    public HeadTeacherObserver(String identity) {
        super(identity);
    }
}

这时候轮到我们学生的主体登场了:

/**
 * 主体类
 * - 模拟被观察者主体
 */
public abstract class Subject {
    /**
     * 修改通知
     */
    abstract void doNotify();

    /**
     * 添加被观察者
     */
    abstract void addObservable(Observer o);

    /**
     * 移除被观察者
     */
    abstract void removeObservable(Observer o);
}

学生主体,被观察的对象:

/**
 * 学生主体
 * - 被观察的对象
 */
public class StudentSubject extends Subject {
    /**
     * 上课状态
     */
    private String state;

    private List<Observer> observableList = new ArrayList<>();

    @Override
    public void doNotify() {
        for (Observer observer : observableList) {
            observer.update(state);
        }
    }

    @Override
    public void addObservable(Observer observable) {
        observableList.add(observable);
    }

    @Override
    public void removeObservable(Observer observable) {
        try {
            if (observable == null) {
                throw new Exception("要移除的被观察者不能为空");
            } else {
                if (observableList.contains(observable) ) {
                    System.out.println("下课了,"+observable.getIdentity()+" 已回到办公室");
                    observableList.remove(observable);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

那么我们焦急万分等待课程终于如期进行了,开始记录报告吧:

/**
 * 课程报告
 * - 一起回顾这节有意思的课
 */
public class CourseReport {
    public static void main(String[] args) {
        // 创建学生主体
        StudentSubject studentSubject = new StudentSubject();
        // 创建观察者老师
        TeacherObserver teacherObversable = new TeacherObserver("老师");
        // 创建观察者班主任
        HeadTeacherObserver headTeacherObserver = new HeadTeacherObserver("班主任");
        // 学生反映上课状态
        studentSubject.setState("(*^▽^*)讲的不错,很好,随手点个关注和在看!");
        studentSubject.addObservable(teacherObversable);
        studentSubject.addObservable(headTeacherObserver);
        // 开始上课
        studentSubject.doNotify();
        // 上课结束
        studentSubject.removeObservable(headTeacherObserver);
        studentSubject.removeObservable(teacherObversable);
    }
}

回到办公室的老师和班主任都眉开眼笑,学生对这节课也是很满意,可以看到如下的报告回顾:

我是老师,正在讲课中...
今天上课时间:下午03时00分00秒 地点:A栋教学楼 上课内容:Java课程
我是班主任来听课了,正在检查课程质量...
学生反馈课程质量为:(*^▽^*)讲的不错,很好,随手点个关注和在看!
下课了,班主任 已回到办公室
下课了,老师 已回到办公室

②使用JDK提供的类实现观察者模式

​ 这里我们可以使用JDK的类来实现相关的观察模式,自带的观察者的类有Observer接口和Observable类,使用这两个类中的方法可以很好的完成观察者模式,而且JDK帮我们做了相关的加锁操作,保证了线程安全,整体来说会对我们上面的类进行改进和简化操作。

​ 我们主要需要改造的是被观察的主体和实现观察者的类,如下所示:

/**
 * 老师类
 * - 观察者之一
 * - 观察学生的上课情况
 */
public class TeacherObserver implements Observer {
    private Course course;
    @Override
    public void update(Observable o, Object arg) {
            DateFormat df = DateFormat.getTimeInstance(DateFormat.LONG, Locale.CHINA);
            System.out.println("我是老师,正在讲课中...");
            course = new Course(new Date(), "A栋教学楼", "Java课程");
            System.out.println("今天上课时间:"+df.format(course.getTime()) + " 地点:" + course.getPlace() + " 上课内容:" + course.getContent());
        }
}

/*************************************/

/**
 * 班主任来听课
 * - 观察者之一
 * - 观察学生的上课情况
 */
public class HeadTeacherObserver implements Observer {
    @Override
    public void update(Observable o, Object arg) {
        System.out.println("我是班主任来听课了,正在检查课程质量...");
        System.out.println("学生反馈课程质量为:" + arg);
    }
}

/*************************************/

/**
 * 学生主体
 * - 被观察的对象
 */
public class StudentObservable extends Observable {
    /**
     * 上课状态
     */
    private String state;

    public void doNotify() {
        // 设置标志
        this.setChanged();
        // 通知观察者做出相应动作
        this.notifyObservers(state);
    }
}

/*************************************/

public class CourseReport {
    public static void main(String[] args) {
        // 创建学生主体
        StudentObservable studentObservable = new StudentObservable();
        // 创建观察者老师
        TeacherObserver teacherObversable = new TeacherObserver();
        // 创建观察者班主任
        HeadTeacherObserver headTeacherObserver = new HeadTeacherObserver();
        // 学生反映上课状态
        studentObservable.setState("(*^▽^*)讲的不错,很好,随手点个关注和在看!");
        studentObservable.addObserver(teacherObversable);
        studentObservable.addObserver(headTeacherObserver);
        // 开始上课
        studentObservable.doNotify();
        // 上课结束
        studentObservable.deleteObserver(headTeacherObserver);
        studentObservable.deleteObserver(teacherObversable);
    }
}

运行效果如下:

我是老师,正在讲课中...
今天上课时间:下午03时00分00秒 地点:A栋教学楼 上课内容:Java课程
我是班主任来听课了,正在检查课程质量...
学生反馈课程质量为:(*^▽^*)讲的不错,很好,随手点个关注和在看!

划重点: 这里Observer内部只定义了一个方法,主要用于在被观察的对象发出通知后做出相应的动作: update(Observable o, Object arg); Observable类中的方法有: addObserver(添加观察者)、deleteObserver(删除观察者)、notifyObservers(唤醒所有的观察者,可带入参数)、deleteObservers(删除所有观察者)、setChanged(改变标志为True)、clearChanged(改变标志为false)、hasChanged(查看标志状态)、countObservers(统计观察者个数) 这些方法中有些加了同步机制保证线程安全,我们可以根据需要使用提供的已有相关方法,增强代码的复用性。有兴趣的可以看下源码的实现,这里就不再累述。

五、总结

观察者模式的让我们知道了在设计开发的时候一定要“多用组合,少用继承”。

我们设计开发是应该是针对接口变成,而不针对实现编程。

这是一种创建松散耦合代码的技术。它定义对象间 一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。由主体和观察者组成,主体负责发布事件,同时观察者通过订阅这些事件来观察该主体。主体并不知道观察者的任何事情,观察者知道主体并能注册事件的回调函数。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 总结关于【代码中的坏味道】

    代码检查工具方法建议是不要超过80行,这只是一个参考标准而已。这样做的目的是:如果超过80行,阅读起来会相对费劲。也见过上千行的方法,这种超级方法不管注释写得再...

    用户4143945
  • IDEA自带Maven Projects窗口打包项目报错

    試毅-思伟
  • Java制作证书的工具keytool用法总结

    keytool 是个密钥和证书管理工具。它使用户能够管理自己的公钥/私钥对及相关证书,用于(通过数字签名)自我认证(用户向别的用户/服务认证自己)或数据完整性以...

    用户4478423
  • 基于nGrinder下的web网站性能测试

    nGrinder 看名字估计很多人就猜到跟Grinder有关系。nGrinder是韩国一家公司居于Grinder二次开发的一个性能平台。nGrinder具有 开...

    用户6367961
  • 手把手教你搭建Jenkins+Jmeter+Ant自动化集成环境

    >双击JDK安装包,选择安装路径(为了节省C盘系统盘空间,不建议选择默认路径。本人安装在 D:\java\JDK目录下,例如Jdk安装在D:\java\JDKj...

    用户6367961
  • SpringBoot深度调优,让你的项目飞起来!

    关于这些设置的JVM参数是什么意思,请参考第二步中的oracle官方给出的调优文档。

    Rookie
  • 初探JVM,只需要这篇文章!

    www.oracle.com -> 右下角Product Documentation -> 往下拉选择Java -> Java SE documentation...

    Java架构
  • ApplicationListener原理分析

    在 Nacos配置服务原理 文中结束时提到过通过发布 ApplicationListener 刷新事件完成 Context 中属性值的更新。那么本章我们一起分析...

    GreizLiao
  • 互联网高级面试题目

    覆盖(Override)是指子类对父类方法的一种重写,只能比父类抛出更少的异常,访问权限不能比父类的小。

    用户4478423
  • JDK 13 的 12 个新特性,真心涨姿势了

    核心库/ java.nio中添加了FileSystems.newFileSystem(Path,Map <String,?>)方法

    Java技术栈

扫码关注云+社区

领取腾讯云代金券