前言
- 23种设计模式速记
- 单例(singleton)模式
- 工厂方法(factory method)模式
- 抽象工厂(abstract factory)模式
- 建造者/构建器(builder)模式
- 原型(prototype)模式
- 享元(flyweight)模式
- 外观(facade)模式
- 适配器(adapter)模式
- 装饰(decorator)模式
- 持续更新中......
23种设计模式快速记忆的请看上面第一篇,本篇和大家一起来学习观察者模式相关内容。
模式定义
定义了对象之间的一对多依赖,让多个观察者对象同时监听某一个主题对象,当主题对象发生变化时,它的所有依赖者都会收到通知并更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。
观察者模式是对象之间一对多的一种模式,被依赖的对象是Subject,依赖的对象是Observer,Subject通知Observer变化,Subject为1,Observer为多。
解决的问题
一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
模式组成
实例说明
实例概况
某天下午班主任通知某班学生和老师将要听一节课,以此来对老师的授课质量进行评分。
- 老师和学生收到后开始安排相关的课程;
- 上课期间老师和班主任通过观察学生的神情来预判课程的讲的好坏
- 老师观察到学生皱眉头可以适当调节课程气氛
- 班主任观察到学生课堂氛围好转,给予高分
- 课程结束后,班主任和老师回到办公室,一起回顾这节开心的课程。
使用步骤
步骤1:构建一个课程实体类
- class Course {
- // 上课时间:time
- private Date time;
- // 上课地点:place
- private String place;
- // 上课内容:content
- private String content;
- // 省略get/set...
- public Course() {
- }
- public Course(Date time, String place, String content) {
- this.time = time;
- this.place = place;
- this.content = content;
- }
- }
步骤2:构建一个发现者的抽象类以及相关的实现类,老师和班主任都分别继承了该接口并重写相关方法
- abstract class Observer {
- abstract void update(Object args);
- public Observer(String identity) {
- this.identity = identity;
- }
- private String identity;
- public String getIdentity() {
- return identity;
- }
- public void setIdentity(String identity) {
- this.identity = identity;
- }
- }
步骤3:创建一个具体的观察者角色,老师拿着教材开始来上课
- /**
- * 老师类
- * - 观察者之一
- * - 观察学生的上课情况
- */
- 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栋教学楼", "高等数学");
- System.out.println("今天上课时间:" + df.format(course.getTime()) + " 地点:" + course.getPlace() + " 上课内容:" + course.getContent());
- }
- public TeacherObserver(String identity) {
- super(identity);
- }
- }
步骤4:创建一个具体的观察者角色,班主任来听课
- /**
- * 班主任来听课
- * - 观察者之一
- * - 观察学生的上课情况
- */
- class HeadTeacherObserver extends Observer {
- @Override
- public void update(Object args) {
- System.out.println("我是班主任来听课了,正在检查课程质量...");
- System.out.println("学生反馈课程质量为:" + args);
- }
- public HeadTeacherObserver(String identity) {
- super(identity);
- }
- }
步骤5:创建被观察者抽象主题角色
- /**
- * 主体类
- * - 模拟被观察者主体
- */
- abstract class Subject {
- /**
- * 修改通知
- */
- abstract void doNotify();
- /**
- * 添加被观察者
- */
- abstract void addObservable(Observer o);
- /**
- * 移除被观察者
- */
- abstract void removeObservable(Observer o);
- }
步骤6:创建具体的被观察者主体角色,学生主体为被观察对象
- /**
- * 学生主体
- * - 被观察的对象
- */
- class StudentSubject extends Subject {
- /**
- * 上课状态
- */
- private String state;
- public String getState() {
- return state;
- }
- public void setState(String state) {
- this.state = 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();
- }
- }
- }
步骤7:开始上课,开始记录报告
- /**
- * 观察者模式
- */
- public class ObserverPattern {
- 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);
- }
- }
输出结果
- 我是王老师,正在讲课中...
- 今天上课时间:下午11时57分01秒 地点:A栋教学楼 上课内容:高等数学
- 我是班主任来听课了,正在检查课程质量...
- 学生反馈课程质量为:讲的不错,很好!
- 下课了,班主任 已回到办公室
- 下课了,王老师 已回到办公室
优点
- 符合开闭原则
- 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系;
- 目标与观察者之间建立了一套触发机制。
缺点
- 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用;
- 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。
应用场景
当更改一个对象的状态可能需要更改其他对象,并且实际的对象集事先未知或动态更改时,请使用观察者模式。
注意事项: 1、JAVA 中已经有了对观察者模式的支持类。 2、避免循环引用。 3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
源码中的应用
- #JDK:
- java.util.Observable
- #Spring:
- org.springframework.context.ApplicationListener
Observable源码分析
Observable接口
- public interface Observer {
- void update(Observable o, Object arg);
- }
Observable类
- public class Observable {
- private Vector<Observer> obs;
- //添加观察者
- public synchronized void addObserver(Observer o) {
- if (o == null)
- throw new NullPointerException();
- if (!obs.contains(o)) {
- obs.addElement(o);
- }
- }
- //删除观察者
- public synchronized void deleteObserver(Observer o) {
- obs.removeElement(o);
- }
- //通知所有观察者
- public void notifyObservers() {
- notifyObservers(null);
- }
- public void notifyObservers(Object arg) {
- /*
- * a temporary array buffer, used as a snapshot of the state of
- * current Observers.
- */
- Object[] arrLocal;
- synchronized (this) {
- /* We don't want the Observer doing callbacks into
- * arbitrary code while holding its own Monitor.
- * The code where we extract each Observable from
- * the Vector and store the state of the Observer
- * needs synchronization, but notifying observers
- * does not (should not). The worst result of any
- * potential race-condition here is that:
- * 1) a newly-added Observer will miss a
- * notification in progress
- * 2) a recently unregistered Observer will be
- * wrongly notified when it doesn't care
- */
- if (!changed)
- return;
- arrLocal = obs.toArray();
- clearChanged();
- }
- for (int i = arrLocal.length-1; i>=0; i--)
- ((Observer)arrLocal[i]).update(this, arg);
- }
- }
使用JDK提供的类实现观察者模式
通过使用JDK的类来实现上面实例相关的观察模式,自带的观察者的类有Observer接口和Observable类,使用这两个类中的方法可以很好的完成观察者模式,而且JDK帮我们做了相关的加锁操作,保证了线程安全,整体来说会对我们上面的例子进行改进和简化操作,代码如下:
- package com.niuh.designpattern.observer.v2;
- import java.text.DateFormat;
- import java.util.Date;
- import java.util.Locale;
- import java.util.Observable;
- import java.util.Observer;
- /**
- * 观察者模式
- */
- public class ObserverPattern {
- // 步骤6:开始上课,开始记录报告
- public static void main(String[] args) {
- // 创建学生主体
- StudentSubject studentSubject = new StudentSubject();
- // 创建观察者老师
- TeacherObserver teacherObversable = new TeacherObserver();
- // 创建观察者班主任
- HeadTeacherObserver headTeacherObserver = new HeadTeacherObserver();
- // 学生反映上课状态
- studentSubject.setState("讲的不错,很好!");
- studentSubject.addObserver(teacherObversable);
- studentSubject.addObserver(headTeacherObserver);
- // 开始上课
- studentSubject.doNotify();
- // 上课结束
- studentSubject.deleteObserver(headTeacherObserver);
- studentSubject.deleteObserver(teacherObversable);
- }
- }
- /**
- * 课程类
- */
- class Course {
- // 上课时间:time
- private Date time;
- // 上课地点:place
- private String place;
- // 上课内容:content
- private String content;
- public Date getTime() {
- return time;
- }
- public void setTime(Date time) {
- this.time = time;
- }
- public String getPlace() {
- return place;
- }
- public void setPlace(String place) {
- this.place = place;
- }
- public String getContent() {
- return content;
- }
- public void setContent(String content) {
- this.content = content;
- }
- public Course() {
- }
- public Course(Date time, String place, String content) {
- this.time = time;
- this.place = place;
- this.content = content;
- }
- }
- /**
- * 老师类
- * - 观察者之一
- * - 观察学生的上课情况
- */
- 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栋教学楼", "高等数学");
- System.out.println("今天上课时间:" + df.format(course.getTime()) + " 地点:" + course.getPlace() + " 上课内容:" + course.getContent());
- }
- }
- /**
- * 班主任来听课
- * - 观察者之一
- * - 观察学生的上课情况
- */
- class HeadTeacherObserver implements Observer {
- @Override
- public void update(Observable o, Object arg) {
- System.out.println("我是班主任来听课了,正在检查课程质量...");
- System.out.println("学生反馈课程质量为:" + arg);
- }
- }
- /**
- * 学生主体
- * - 被观察的对象
- */
- class StudentSubject extends Observable {
- /**
- * 上课状态
- */
- private String state;
- public String getState() {
- return state;
- }
- public void setState(String state) {
- this.state = state;
- }
- public void doNotify() {
- // 设置标志
- this.setChanged();
- // 通知观察者做出相应动作
- this.notifyObservers(state);
- }
- }
输出结果:
- 我是班主任来听课了,正在检查课程质量...
- 学生反馈课程质量为:讲的不错,很好!
- 我是王老师,正在讲课中...
- 今天上课时间:上午12时04分27秒 地点:A栋教学楼 上课内容:高等数学
PS:以上代码提交在 Github :
https://github.com/Niuh-Study/niuh-designpatterns.git