
前言
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 dont 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 doesnt 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