观察者模式理解:
观察者模式是一种对象行为模式,描述或者说是定义了对象之间一对多的对象关系。如果你了解发布订阅模式,那么理解观察者模式就容易多了。在《Head First设计模式》中观察者模式第一段中讲到,出版者+订阅者=观察者模式。意思就是说,一个出版者对应多个订阅者,出版者将消息告知给订阅者。这样的一套模式称之为观察者模式(如果不清楚发布订阅模式,请看下一段理解)。
我们仔细的讲一讲观察这么模式的定义和方式:
观察者模式描述了当一个对象的状态发生改变时,所有依赖于它的对象(观察者)都得到通知并被自动更新。降低程序之间的耦合度,观察者并不知道到其他观察者的存在。
通俗一点讲:学校和学生,学校每次广播告知学生们下课做课间操,有一
观察者模式中最重要的两个元素:主题和观察者,通过主题通知观察者发生更新动作。
通过下图来理解一下:
源码解读
在明白了基本的观察者模式之后,现在我们来看一下jdk源码中util包下的Observable类,记住它是一个类,而不是一个接口。这样就限制了它的使用,不能像接口一样灵活的实现(java 的单继承)。当前Observable是一个主题类,Observable是被观察的意思。
首先看一下Observable类的基本结构。从上到下,一次为构造、注册、注销、通知、通知、清除、改变changed、清除changed、获取changed、获取观察这里列表长度。私有变量changed默认为false,私有成员观察者列表,为vector类型。
了解完之后,我们在详细的看一下它的源码。
package java.util; //首先是类名,类下定义了两个变量:changed 状态,obs观察者列表(采用Vector集合方式存储) //当changed属性为true时才更行观察者们,该类实现了其set和清除change状态的方法。 public class Observable { private boolean changed = false; private Vector<Observer> obs; public Observable() { obs = new Vector<>(); } //注册,将观察者注册到主题 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) { //临时数组,存储观察者列表 Object[] arrLocal; //判断当前的changed状态,如果当前状态为false,则返回。 synchronized (this) { if (!changed) return; arrLocal = obs.toArray(); clearChanged();//清除 } //遍历当前的arrLocal观察者列表 for (int i = arrLocal.length-1; i>=0; i--) ((Observer)arrLocal[i]).update(this, arg); } //清除观察者 public synchronized void deleteObservers() { obs.removeAllElements(); } //改变changed状态 protected synchronized void setChanged() { changed = true; } //清除changed状态 protected synchronized void clearChanged() { changed = false; }
//获取当前changed public synchronized boolean hasChanged() { return changed; } //获取obs观察者列表大小 public synchronized int countObservers() { return obs.size(); } }
Observable类定义了主题对观察者的添加删除通知操作。当我们在notify方法中调用update中时,我们会看到jdk中的另一个接口:Observer,此接口中的结构比较简单,仅仅只是定义了一个update的方法。
package java.util; public interface Observer { void update(Observable o, Object arg); }
通过对源码的解读来理解观察者模式的思想和使用,在很多时候jdk源码都是我们学习的最好老师,在经过了多位大师不断的更新迭代jdk版本之后,源码的设计已经达到了普通程序员望尘莫及的高度。在读完jdk源码之后,你会发现原来简单的代码和牛逼的设计思想可以实现出如此复杂的功能。源码的解读在于设计思想,在于你怎样使用每一个代码的功能。
设计与实现
那么我们从头来设计并使用一次。
首先理解一下观察者模式能够在哪些场景使用:
1、一个对象的状态改变,其所依赖的对象都需要发生动作的改变。
2、当一个对象发生改变时,其他对象都需要知道该对象的变化。
使用:设计一个假定场景。Head_First设计模式中以气象局来举例。
场景设计:
首先存在气象局和人两个对象,气象局在天气发生变化时通知人们,人们根据当前的天气变化来改变自己的行为,不同的人(管观察者)之间互不干扰,且并不知道还有其他人也关注了气象局的天气报告。
对象设计:
气象管理局-抽象被观察者角色:定义了动态增加、删除以及通知观察者对象的方法。
气象台-具体被观察者角色:实现了气象管理局的所有方法,且本身具有自己的功能和业务。当天气状态发生改变是,发起通知。
人-抽象观察者角色:提供一个观察者接口,创建更新方法,供被观察者发起通知是调用。
学生、工人等-具体观察者角色:实现抽象观察者接口,实现自身的业务逻辑功能。
![](https://img-blog.csdnimg.cn/20190917164107165.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MjQxODU4OQ==,size_16,color_FFFFFF,t_70)
在上面讲到的jdk源码中,提供了一个changed变量,来判断主题是否发生变化,以此来进行发布通知。
在完成了设计之后,下面我们来开始实际的操作,代码:
Obseveable:仿照jdk源码实现方式
package com.cs.ObversblerDemo.Observable; import java.util.ArrayList; import java.util.Observable; import java.util.Observer; import java.util.Vector; /** * @Author chens * @create 2019/9/17 14:48 */ public class ObservableBase { //创建观察者列表 private static ArrayList<Observer> obs=new ArrayList<Observer>(); //提供观察者列表的方法 public synchronized void addObserver(Observers o) { obs.add(o); System.out.println("添加观察者"+o.getName); } public void notifyObservers() { //判断当前obs列表是为空 if (obs!=null||obs.size()==0){ //初始化列表操作 System.out.println("当前观察者列表为空,请添加"); } } //移除全部的观察者列表 public synchronized void deleteObservers() { obs.removeAll(obs); } //移除单个观察者列表 public synchronized void deleteObservers(Observers o) { obs.remove(o); } }
Observatory :气象台,提供具体的通知方法
package com.cs.ObversblerDemo.Observable; import java.util.Observable;
/** * @Author chens * @create 2019/9/17 17:08 */ public class Observatory extends ObservableBase { @Override public void notifyObservers() { System.out.println("执行通知,改变天气"); super.notifyObservers(); } }
Obsever接口:观察者接口
package com.cs.ObversblerDemo.Observers; /** * @Author chens * @create 2019/9/17 17:21 */ public interface Observer { void update(); }
Student:学生类
package com.cs.ObversblerDemo.Observers; /** * @Author chens * @create 2019/9/17 17:23 */ public class Student implements Observer { public void update() { System.out.println("执行Student动作"); } @Override public String toString() { return "Student"; } }
Worker :工人类
package com.cs.ObversblerDemo.Observers; /** * @Author chens * @create 2019/9/17 17:23 */ public class Worker implements Observer { public void update() { System.out.println("执行Worker动作"); } @Override public String toString() { return "Student"; } }
接下来测试我们的工程是否搭建成功:
import com.cs.ObversblerDemo.Observable.Observatory; import com.cs.ObversblerDemo.Observers.Student; import com.cs.ObversblerDemo.Observers.Worker; /** * @Author chens * @create 2019/9/17 17:33 */ public class Test { static Observatory observatory = new Observatory(); public static void main(String[] args) { //依次添加学生和工人 Student student = new Student(); observatory.addObserver(student); Worker worker = new Worker(); observatory.addObserver(worker); //执行通知 observatory.notifyObservers(); } }
执行结果
我们简单的梳理一下本案例的执行流程:
首先添加观察者者列表,在实际上的生产中,我们一般采用枚举的方式,配置当前的观察者的列表,而不是手动的镜像对象的添加。这就需要在被观察者(主题)中执行一步初始化当前观察者的数据。
第二,调用接口update方法,执行所有实现类的update。完成通知。
第三,观察者根据当前通知产生相应的动作。
ps:被观察者又称之为主题。
最后推荐一篇文章:是我见过很通俗易懂的观察者模式的讲解,并且代码和分析非常清晰。https://www.jianshu.com/p/d5a758dd2795