java设计模式解析(1) Observer观察者模式

时间:2019-08-22
本文章向大家介绍java设计模式解析(1) Observer观察者模式,主要包括java设计模式解析(1) Observer观察者模式使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

更多文章参考--java设计模式解析

主要内容

1、简述

2、实现代码(Talk is cheap,Show me the code)

3、注意点

1、简述

 观察者设计模式在日常软件开发非常常见。比如MQ消息监听、ZooKeeper监听节点事件、Spring发布事件以完成容器初始化(后续会有分析)等。

 度娘上对观察者模式有很多版本定义,个人觉得还是《设计模式之禅》定义比较全面,不愧设计模式宝典啊。先贴出来具体定义:

针对上面给出的几个关键对角色,补充一下自己的理解:

1)、Subject:被观察者,即事件的起源。一般在软件实现阶段是 接口 或者 抽象类 

2)、Observer:观察者,即观察到事件发生则同步更新自身的状态。一般在软件实现阶段是 接口 或者 抽象类 

2、实现代码(Talk is cheap,Show me the code)

《设计模式之禅》已经给出具体的代码实现部分,但是个人觉得过于标准化,现实软件落地肯定会对其进行改造,例如Spring发布事件机制(后续会有分析)。这里个人实现场景,欢迎拍砖:

定义:大学每个学期都会提前订阅该学期的课程,一个课程会被N多个学生订阅。假设某个数学课程任课老师变更之后,学生需要同步更新课程信息,实现自级联变更。

分析:此时可以看出来【课程】就是被观察者的角色,【学生】就是观察者的角色。当【课程】有变动即任课老师变更,需要及时通知【学生】。

Subject课程

/**
 * 课程 被观察的对象
 * @author  zhou.guangfeng on 2019/8/21 下午5:29
 */
public interface Subject {
    void teacherChange(String teacherName) ;
}

 Student学生

/**
 * 学生 观察者
 * @author  zhou.guangfeng on 2019/8/21 下午5:29
 */
public interface Student {
    void changeSubjectTeacherName(String teacherName) ;
}

 Observer监听 串联观察者 和 被观察者,同时实现被观察者接口

/**
 * 监听 串联观察者 和 被观察者。同时实现被观察者接口
 * @author  zhou.guangfeng on 2019/8/21 下午5:29
 */
public class Observer implements Subject {

    private Subject subject ;

    private List<Student> studentList = new ArrayList<>() ;

    public Observer(Subject subject) {
        this.subject = subject;
    }

    @Override
    public void teacherChange(String teacherName) {
        subject.teacherChange(teacherName);
        studentList.forEach(student -> student.changeSubjectTeacherName(teacherName));
    }

    /**
     * 动态注册观察者
     *
     * @param
     * @return
     */
    public void registerStudent(Student student){
        System.out.println("动态注册观察者 --> " + student);
        studentList.add(student) ;
    }

    /**
     * 动态删除观察者
     *
     * @param
     * @return
     */
    public void removeStudent(Student student){
        System.out.println("动态删除观察者 --> " + student);
        studentList.remove(student) ;
    }

}

 ObserverMain执行代码

public class ObserverMain {

    public static void main(String[] args) {
        // 被观察者 数学课程
        Subject shuxue = new Subject() {
            @Override
            public void teacherChange(String teacherName) {
                System.out.println("数学课程 修改老师为 " + teacherName);
            }
        };

        // 监听者 代理了被观察者 即 数学课程
        Observer watcher = new Observer(shuxue) ;
        Student student = new Student() {
            @Override
            public void changeSubjectTeacherName(String teacherName) {
                System.out.println("学生A 修改老师为 " + teacherName);
            }
        } ;

        // 注册 观察者
        watcher.registerStudent(student);
        watcher.registerStudent(new Student() {
            @Override
            public void changeSubjectTeacherName(String teacherName) {
                System.out.println("学生B 修改老师为 " + teacherName);
            }
        });

        // 改变课程
        watcher.teacherChange("华罗庚");

        // 删除监听学生
        watcher.removeStudent(student);

        // 改变课程
        watcher.teacherChange("爱因斯坦");
    }
}

 执行结果: 

动态注册观察者 --> com.nancy.observer.ObserverMain$2@6f94fa3e
动态注册观察者 --> com.nancy.observer.ObserverMain$3@5e481248
数学课程 修改老师为 华罗庚
学生A 修改老师为 华罗庚
学生B 修改老师为 华罗庚
动态删除观察者 --> com.nancy.observer.ObserverMain$2@6f94fa3e
数学课程 修改老师为 爱因斯坦
学生B 修改老师为 爱因斯坦

3、注意点

(1)、由于触发观察者是顺序调用,如果观察者很多(例子中student群体特别多,触发时间很长)势必会有效率瓶颈,此时可以考虑使用线程池等异步进行。

(2)、关系广播链接不能太复杂,否则将难于维护。

原文地址:https://www.cnblogs.com/xiaoxing/p/11392725.html