设计模式系列—观察者模式

开发 前端
本篇和大家一起来学习观察者模式相关内容。

 前言

  • 23种设计模式速记
  • 单例(singleton)模式
  • 工厂方法(factory method)模式
  • 抽象工厂(abstract factory)模式
  • 建造者/构建器(builder)模式
  • 原型(prototype)模式
  • 享元(flyweight)模式
  • 外观(facade)模式
  • 适配器(adapter)模式
  • 装饰(decorator)模式
  • 持续更新中......

23种设计模式快速记忆的请看上面第一篇,本篇和大家一起来学习观察者模式相关内容。

模式定义
定义了对象之间的一对多依赖,让多个观察者对象同时监听某一个主题对象,当主题对象发生变化时,它的所有依赖者都会收到通知并更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。

观察者模式是对象之间一对多的一种模式,被依赖的对象是Subject,依赖的对象是Observer,Subject通知Observer变化,Subject为1,Observer为多。

解决的问题
一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。

模式组成

实例说明
实例概况
某天下午班主任通知某班学生和老师将要听一节课,以此来对老师的授课质量进行评分。

  • 老师和学生收到后开始安排相关的课程;
  • 上课期间老师和班主任通过观察学生的神情来预判课程的讲的好坏
  • 老师观察到学生皱眉头可以适当调节课程气氛
  • 班主任观察到学生课堂氛围好转,给予高分
  • 课程结束后,班主任和老师回到办公室,一起回顾这节开心的课程。

使用步骤
步骤1:构建一个课程实体类

  1. class Course { 
  2.  
  3.     // 上课时间:time 
  4.     private Date time
  5.     // 上课地点:place 
  6.     private String place; 
  7.     // 上课内容:content 
  8.     private String content; 
  9.  
  10.     // 省略get/set... 
  11.  
  12.     public Course() { 
  13.     } 
  14.  
  15.     public Course(Date time, String place, String content) { 
  16.         this.time = time
  17.         this.place = place; 
  18.         this.content = content; 
  19.     } 

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

  1. abstract class Observer { 
  2.     abstract void update(Object args); 
  3.  
  4.     public Observer(String identity) { 
  5.         this.identity = identity; 
  6.     } 
  7.  
  8.     private String identity; 
  9.  
  10.     public String getIdentity() { 
  11.         return identity; 
  12.     } 
  13.  
  14.     public void setIdentity(String identity) { 
  15.         this.identity = identity; 
  16.     } 

步骤3:创建一个具体的观察者角色,老师拿着教材开始来上课

  1. /** 
  2.  * 老师类 
  3.  * - 观察者之一 
  4.  * - 观察学生的上课情况 
  5.  */ 
  6. class TeacherObserver extends Observer { 
  7.  
  8.     private Course course; 
  9.  
  10.     @Override 
  11.     public void update(Object args) { 
  12.         DateFormat df = DateFormat.getTimeInstance(DateFormat.LONG, Locale.CHINA); 
  13.         System.out.println("我是王老师,正在讲课中..."); 
  14.          
  15.         course = new Course(new Date(), "A栋教学楼""高等数学"); 
  16.         System.out.println("今天上课时间:" + df.format(course.getTime()) + " 地点:" + course.getPlace() + " 上课内容:" + course.getContent()); 
  17.     } 
  18.  
  19.     public TeacherObserver(String identity) { 
  20.         super(identity); 
  21.     } 

步骤4:创建一个具体的观察者角色,班主任来听课

  1. /** 
  2.  * 班主任来听课 
  3.  * - 观察者之一 
  4.  * - 观察学生的上课情况 
  5.  */ 
  6. class HeadTeacherObserver extends Observer { 
  7.     @Override 
  8.     public void update(Object args) { 
  9.         System.out.println("我是班主任来听课了,正在检查课程质量..."); 
  10.         System.out.println("学生反馈课程质量为:" + args); 
  11.     } 
  12.  
  13.     public HeadTeacherObserver(String identity) { 
  14.         super(identity); 
  15.     } 

步骤5:创建被观察者抽象主题角色

  1. /** 
  2.  * 主体类 
  3.  * - 模拟被观察者主体 
  4.  */ 
  5. abstract class Subject { 
  6.     /** 
  7.      * 修改通知 
  8.      */ 
  9.     abstract void doNotify(); 
  10.  
  11.     /** 
  12.      * 添加被观察者 
  13.      */ 
  14.     abstract void addObservable(Observer o); 
  15.  
  16.     /** 
  17.      * 移除被观察者 
  18.      */ 
  19.     abstract void removeObservable(Observer o); 

步骤6:创建具体的被观察者主体角色,学生主体为被观察对象

  1. /** 
  2.  * 学生主体 
  3.  * - 被观察的对象 
  4.  */ 
  5. class StudentSubject extends Subject { 
  6.     /** 
  7.      * 上课状态 
  8.      */ 
  9.     private String state; 
  10.   
  11.     public String getState() { 
  12.         return state; 
  13.     } 
  14.  
  15.     public void setState(String state) { 
  16.         this.state = state; 
  17.     } 
  18.     private List<Observer> observableList = new ArrayList<>(); 
  19.  
  20.     @Override 
  21.     public void doNotify() { 
  22.         for (Observer observer : observableList) { 
  23.             observer.update(state); 
  24.         } 
  25.     } 
  26.  
  27.     @Override 
  28.     public void addObservable(Observer observable) { 
  29.         observableList.add(observable); 
  30.     } 
  31.  
  32.     @Override 
  33.     public void removeObservable(Observer observable) { 
  34.         try { 
  35.             if (observable == null) { 
  36.                 throw new Exception("要移除的被观察者不能为空"); 
  37.             } else { 
  38.                 if (observableList.contains(observable) ) { 
  39.                     System.out.println("下课了,"+observable.getIdentity()+" 已回到办公室"); 
  40.                     observableList.remove(observable); 
  41.                 } 
  42.             } 
  43.         } catch (Exception e) { 
  44.             e.printStackTrace(); 
  45.         } 
  46.     } 

步骤7:开始上课,开始记录报告

  1. /** 
  2.  * 观察者模式 
  3.  */ 
  4. public class ObserverPattern { 
  5.  
  6.     public static void main(String[] args) { 
  7.         // 创建学生主体 
  8.         StudentSubject studentSubject = new StudentSubject(); 
  9.  
  10.         // 创建观察者老师 
  11.         TeacherObserver teacherObversable = new TeacherObserver("王老师"); 
  12.  
  13.         // 创建观察者班主任 
  14.         HeadTeacherObserver headTeacherObserver = new HeadTeacherObserver("班主任"); 
  15.  
  16.         // 学生反映上课状态 
  17.         studentSubject.setState("讲的不错,很好!"); 
  18.         studentSubject.addObservable(teacherObversable); 
  19.         studentSubject.addObservable(headTeacherObserver); 
  20.  
  21.         // 开始上课 
  22.         studentSubject.doNotify(); 
  23.  
  24.         // 上课结束 
  25.         studentSubject.removeObservable(headTeacherObserver); 
  26.         studentSubject.removeObservable(teacherObversable); 
  27.     } 

输出结果

  • 我是王老师,正在讲课中...
  • 今天上课时间:下午11时57分01秒 地点:A栋教学楼 上课内容:高等数学
  • 我是班主任来听课了,正在检查课程质量...
  • 学生反馈课程质量为:讲的不错,很好!
  • 下课了,班主任 已回到办公室
  • 下课了,王老师 已回到办公室

优点

  • 符合开闭原则
  • 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系;
  • 目标与观察者之间建立了一套触发机制。

缺点

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

应用场景
当更改一个对象的状态可能需要更改其他对象,并且实际的对象集事先未知或动态更改时,请使用观察者模式。

注意事项: 1、JAVA 中已经有了对观察者模式的支持类。 2、避免循环引用。 3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。

源码中的应用

  1. #JDK: 
  2. java.util.Observable 
  3.  
  4. #Spring: 
  5. org.springframework.context.ApplicationListener 

Observable源码分析
Observable接口

  1. public interface Observer { 
  2.  void update(Observable o, Object arg); 

Observable类

  1. public class Observable { 
  2.  private Vector<Observer> obs; 
  3.  
  4.  //添加观察者 
  5.  public synchronized void addObserver(Observer o) { 
  6.         if (o == null
  7.             throw new NullPointerException(); 
  8.         if (!obs.contains(o)) { 
  9.             obs.addElement(o); 
  10.         } 
  11.     } 
  12.  
  13.  //删除观察者 
  14.  public synchronized void deleteObserver(Observer o) { 
  15.         obs.removeElement(o); 
  16.     } 
  17.   
  18.  //通知所有观察者 
  19.  public void notifyObservers() { 
  20.         notifyObservers(null); 
  21.     } 
  22.  
  23.  public void notifyObservers(Object arg) { 
  24.         /* 
  25.          * a temporary array buffer, used as a snapshot of the state of 
  26.          * current Observers. 
  27.          */ 
  28.         Object[] arrLocal; 
  29.  
  30.         synchronized (this) { 
  31.             /* We don't want the Observer doing callbacks into 
  32.              * arbitrary code while holding its own Monitor. 
  33.              * The code where we extract each Observable from 
  34.              * the Vector and store the state of the Observer 
  35.              * needs synchronization, but notifying observers 
  36.              * does not (should not).  The worst result of any 
  37.              * potential race-condition here is that: 
  38.              * 1) a newly-added Observer will miss a 
  39.              *   notification in progress 
  40.              * 2) a recently unregistered Observer will be 
  41.              *   wrongly notified when it doesn't care 
  42.              */ 
  43.             if (!changed) 
  44.                 return
  45.             arrLocal = obs.toArray(); 
  46.             clearChanged(); 
  47.         } 
  48.  
  49.         for (int i = arrLocal.length-1; i>=0; i--) 
  50.             ((Observer)arrLocal[i]).update(this, arg); 
  51.     } 

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

  1. package com.niuh.designpattern.observer.v2; 
  2.  
  3. import java.text.DateFormat; 
  4. import java.util.Date
  5. import java.util.Locale; 
  6. import java.util.Observable; 
  7. import java.util.Observer; 
  8.  
  9. /** 
  10.  * 观察者模式 
  11.  */ 
  12. public class ObserverPattern { 
  13.  
  14.     // 步骤6:开始上课,开始记录报告 
  15.     public static void main(String[] args) { 
  16.         // 创建学生主体 
  17.         StudentSubject studentSubject = new StudentSubject(); 
  18.  
  19.         // 创建观察者老师 
  20.         TeacherObserver teacherObversable = new TeacherObserver(); 
  21.  
  22.         // 创建观察者班主任 
  23.         HeadTeacherObserver headTeacherObserver = new HeadTeacherObserver(); 
  24.  
  25.         // 学生反映上课状态 
  26.         studentSubject.setState("讲的不错,很好!"); 
  27.         studentSubject.addObserver(teacherObversable); 
  28.         studentSubject.addObserver(headTeacherObserver); 
  29.  
  30.         // 开始上课 
  31.         studentSubject.doNotify(); 
  32.  
  33.         // 上课结束 
  34.         studentSubject.deleteObserver(headTeacherObserver); 
  35.         studentSubject.deleteObserver(teacherObversable); 
  36.     } 
  37.  
  38.  
  39. /** 
  40.  * 课程类 
  41.  */ 
  42. class Course { 
  43.  
  44.     // 上课时间:time 
  45.     private Date time
  46.     // 上课地点:place 
  47.     private String place; 
  48.     // 上课内容:content 
  49.     private String content; 
  50.  
  51.     public Date getTime() { 
  52.         return time
  53.     } 
  54.  
  55.     public void setTime(Date time) { 
  56.         this.time = time
  57.     } 
  58.  
  59.     public String getPlace() { 
  60.         return place; 
  61.     } 
  62.  
  63.     public void setPlace(String place) { 
  64.         this.place = place; 
  65.     } 
  66.  
  67.     public String getContent() { 
  68.         return content; 
  69.     } 
  70.  
  71.     public void setContent(String content) { 
  72.         this.content = content; 
  73.     } 
  74.  
  75.     public Course() { 
  76.     } 
  77.  
  78.     public Course(Date time, String place, String content) { 
  79.         this.time = time
  80.         this.place = place; 
  81.         this.content = content; 
  82.     } 
  83.  
  84.  
  85.  
  86.  
  87.  
  88. /** 
  89.  * 老师类 
  90.  * - 观察者之一 
  91.  * - 观察学生的上课情况 
  92.  */ 
  93. class TeacherObserver implements Observer { 
  94.  
  95.     private Course course; 
  96.  
  97.     @Override 
  98.     public void update(Observable o, Object arg)  { 
  99.         DateFormat df = DateFormat.getTimeInstance(DateFormat.LONG, Locale.CHINA); 
  100.         System.out.println("我是王老师,正在讲课中..."); 
  101.  
  102.         course = new Course(new Date(), "A栋教学楼""高等数学"); 
  103.         System.out.println("今天上课时间:" + df.format(course.getTime()) + " 地点:" + course.getPlace() + " 上课内容:" + course.getContent()); 
  104.     } 
  105.  
  106. /** 
  107.  * 班主任来听课 
  108.  * - 观察者之一 
  109.  * - 观察学生的上课情况 
  110.  */ 
  111. class HeadTeacherObserver implements Observer { 
  112.     @Override 
  113.     public void update(Observable o, Object arg) { 
  114.         System.out.println("我是班主任来听课了,正在检查课程质量..."); 
  115.         System.out.println("学生反馈课程质量为:" + arg); 
  116.     } 
  117.  
  118.  
  119. /** 
  120.  * 学生主体 
  121.  * - 被观察的对象 
  122.  */ 
  123. class StudentSubject extends Observable { 
  124.     /** 
  125.      * 上课状态 
  126.      */ 
  127.     private String state; 
  128.  
  129.     public String getState() { 
  130.         return state; 
  131.     } 
  132.  
  133.     public void setState(String state) { 
  134.         this.state = state; 
  135.     } 
  136.  
  137.     public void doNotify() { 
  138.         // 设置标志 
  139.         this.setChanged(); 
  140.         // 通知观察者做出相应动作 
  141.         this.notifyObservers(state); 
  142.     } 

输出结果:

  • 我是班主任来听课了,正在检查课程质量...
  • 学生反馈课程质量为:讲的不错,很好!
  • 我是王老师,正在讲课中...
  • 今天上课时间:上午12时04分27秒 地点:A栋教学楼 上课内容:高等数学

PS:以上代码提交在 Github :

https://github.com/Niuh-Study/niuh-designpatterns.git

 

 

 

责任编辑:姜华 来源: 今日头条
相关推荐

2013-11-26 17:09:57

Android设计模式

2021-07-08 11:28:43

观察者模式设计

2022-01-29 22:12:35

前端模式观察者

2015-11-25 11:10:45

Javascript设计观察

2024-02-18 12:36:09

2021-09-06 10:04:47

观察者模式应用

2009-03-30 09:39:04

观察者思想换位设计模式

2011-04-29 09:22:22

2021-03-29 07:14:28

Spring观察者模式

2012-08-27 10:52:20

.NET架构观察者模式

2021-01-25 05:38:04

设计原理VueSubject

2022-05-09 10:50:13

观察者模式设计模式

2020-10-20 13:33:00

建造者模式

2021-10-26 00:21:19

设计模式建造者

2020-11-05 09:38:07

中介者模式

2021-01-21 05:34:14

设计模式建造者

2021-09-29 19:45:24

观察者模式Observable

2021-06-03 12:26:28

观察者模式面试阿里P6

2022-07-13 08:36:57

MQ架构设计模式

2021-06-07 20:03:04

监听器模式观察者
点赞
收藏

51CTO技术栈公众号