观察者模式(Observer Pattern)
属于对象行为型模式
的一种,定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆获得通知并被自动更新。html
<!-- more -->java
观察者模式
是一种使用率极高的模式,用于创建一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其余对象,其余对象将相应做出反应。在观察者模式中,发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标能够对应多个观察者,并且这些观察者之间能够没有任何相互联系,能够根据须要增长和删除观察者,使得系统更易于扩展。git
观察者模式的别名包括发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。设计模式
前言:观察者模式有两种方模型,分别是推模型
和拉模型
数组
UML结构图安全
1.定义目标对象,它知道观察它的观察者,并提供注册和删除观察者的接口微信
class Subject { /** * 用来保存注册的观察者对象 */ private List<Observer> observers = new ArrayList<>(); /** * 注册观察者对象 * * @param observer 观察者对象 */ void attach(Observer observer) { observers.add(observer); } /** * 通知全部注册的观察者对象 */ void notifyObservers(String newState) { for (Observer observer : observers) { observer.update(newState); } } }
2.具体的目标对象,负责把有关状态存入到相应的观察者对象,并在本身状态发生改变时,通知各个观察者多线程
class ConcreteSubject extends Subject { private String subjectState; public String getSubjectState() { return subjectState; } public void change(String subjectState) { this.subjectState = subjectState; //状态发生改变,通知各个观察者 this.notifyObservers(subjectState); } }
3.建立观察者接口,定义一个更新的接口给那些在目标发生改变的时候被通知的对象架构
interface Observer { /** * 更新的接口 * * @param subject 传入目标对象,好获取相应的目标对象的状态 */ void update(String subject); }
4.具体观察者对象,实现更新的方法,使自身的状态和目标的状态保持一致ide
class ConcreteObserver implements Observer { @Override public void update(String newState) { //具体的更新实现 //这里可能须要更新观察者的状态,使其与目标的状态保持一致 System.out.println("接收到:" + newState); } }
5.建立推模型客户端,用于测试
public class PushClient { public static void main(String[] args) { //建立主题对象 ConcreteSubject subject = new ConcreteSubject(); //建立观察者对象 Observer observer = new ConcreteObserver(); //将观察者对象登记到主题对象上 subject.attach(observer); //改变主题对象的状态 subject.change("push state"); } }
6.运行结果
接收到:push state
1.定义目标对象,它知道观察它的观察者,并提供注册和删除观察者的接口
class Subject { /** * 用来保存注册的观察者对象 */ private List<Observer> observers = new ArrayList<>(); /** * 注册观察者对象 * * @param observer 观察者对象 */ public void attach(Observer observer) { observers.add(observer); } /** * 通知全部注册的观察者对象 */ public void notifyObservers() { for (Observer observer : observers) { // 注意这句代码' observer.update(this); } } }
2.具体的目标对象,负责把有关状态存入到相应的观察者对象,并在本身状态发生改变时,通知各个观察者
class ConcreteSubject extends Subject { /** * 示意,目标对象的状态 */ private String subjectState; public String getSubjectState() { return subjectState; } public void change(String subjectState) { this.subjectState = subjectState; //状态发生改变,通知各个观察者 this.notifyObservers(); } }
3.建立观察者接口,定义一个更新的接口给那些在目标发生改变的时候被通知的对象
interface Observer { /** * 更新的接口 * * @param subject 传入目标对象,好获取相应的目标对象的状态 */ void update(Subject subject); }
4.具体观察者对象,实现更新的方法,使自身的状态和目标的状态保持一致
class ConcreteObserver implements Observer { /** * 示意,观者者的状态 */ private String observerState; @Override public void update(Subject subject) { //具体的更新实现 //这里可能须要更新观察者的状态,使其与目标的状态保持一致 observerState = ((ConcreteSubject) subject).getSubjectState(); System.out.println("接收到:" + observerState); } }
5.建立拉模型客户端,用于测试
public class PullClient { public static void main(String[] args) { //建立主题对象 ConcreteSubject subject = new ConcreteSubject(); //建立观察者对象 Observer observer = new ConcreteObserver(); //将观察者对象登记到主题对象上 subject.attach(observer); //改变主题对象的状态 subject.change("pull state"); } }
6.运行结果
接收到:pull state
上文说过推模型
是假定主题对象知道观察者须要的数据,这种模型下若是数据发生变动会形成极大的影响;而拉模型是主题对象不知道观察者具体须要什么数据,没有办法的状况下,干脆把自身传递给观察者,让观察者本身去按须要取值。因而可知:拉模式的适用范围更广;
对于观察者模式,其实Java已经为咱们提供了已有的接口和类。对于订阅者(Subscribe,观察者)Java为咱们提供了一个接口。
UML图
在JAVA语言的 java.util 库里面,提供了一个Observable
类以及一个Observer
接口,构成JAVA语言对观察者模式的支持。
Observer: 只定义了一个 update()
方法,当被观察者对象的状态发生变化时,被观察者对象的 notifyObservers()
方法就会调用这一方法。
public interface Observer { void update(Observable o, Object arg); }
Observable: 充当观察目标类,在Observable中定义了一个向量Vector来存储观察者对象。一个观察目标类能够有多个观察者对象,每一个观察者对象都是实现Observer接口
的对象。在被观察者发生变化时,会调用Observable
的notifyObservers()
方法,此方法调用全部的具体观察者的update()方法, 从而使全部的观察者都被通知更新本身。
setChanged()
设置一个内部标记变量,表明被观察者对象的状态发生了变化。notifyObservers()
调用全部登记过的观察者对象的update()方法,使这些观察者对象能够更新本身。public class Observable { private boolean changed = false; //是否改变状态,每次都须要设置,表示内容发生变化 private Vector<Observer> obs; //Vector利用同步方法来线程安全,线程安全在多线程状况下不会形成数据混乱 /** Construct an Observable with zero Observers. */ 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); } //通知方法,用于在方法内部循环调用向量中每个观察者的update()方法。 public void notifyObservers() { notifyObservers(null); } public void notifyObservers(Object arg) { Object[] arrLocal; synchronized (this) { if (!changed) //状态值未改变时返回,不通知 return; arrLocal = obs.toArray(); //将Vector转换成数组 clearChanged(); //重置状态 } for (int i = arrLocal.length-1; i>=0; i--) ((Observer)arrLocal[i]).update(this, arg); } public synchronized void deleteObservers() { obs.removeAllElements(); } protected synchronized void setChanged() { changed = true; } protected synchronized void clearChanged() { changed = false; } public synchronized boolean hasChanged() { return changed; } public synchronized int countObservers() { return obs.size(); } }
1.定义两个实现了实现java.util.Observer
接口的观察者
class SubscribeReader implements Observer { @Override public void update(Observable o, Object arg) { System.out.println("开始读取:" + ((Publish) o).getMessage()); } } class SubscribeWrite implements Observer { @Override public void update(Observable o, Object arg) { System.out.println("开始写入:" + ((Publish) o).getMessage()); } }
2.建立继承java.util.Observable
的通知者
class Publish extends Observable { private String message; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; //改变通知者的状态 super.setChanged(); //调用父类Observable方法,通知全部观察者 super.notifyObservers(); } }
3.建立测试客户端
public class Client { public static void main(String[] args) { Publish publish = new Publish(); // 遵循FIFO 模型 先进后出 SubscribeWrite write = new SubscribeWrite(); SubscribeReader reader = new SubscribeReader(); publish.addObserver(reader); publish.addObserver(write); publish.setMessage("Hello Battcn"); publish.setMessage("QQ:1837307557"); publish.setMessage("Email:1837307557@qq.com"); } }
4.运行结果
开始写入:Hello Battcn 开始读取:Hello Battcn 开始写入:QQ:1837307557 开始读取:QQ:1837307557 开始写入:Email:1837307557@qq.com 开始读取:Email:1837307557@qq.com
在当前流行的MVC(Model-View-Controller)架构中也应用了观察者模式,MVC是一种架构模式,它包含三个角色:模型(Model),视图(View)和控制器(Controller)。其中模型可对应于观察者模式中的观察目标,而视图对应于观察者,控制器可充当二者之间的中介者。当模型层的数据发生改变时,视图层将自动改变其显示内容。
实现的关键是要创建观察者和被观察者之间的联系、好比在被观察者类中有个集合是用于存放观察者的、当被检测的东西发生改变的时候就要通知全部观察者。在被观察者中要提供一些对全部观察者管理的一些方法.目的是添加或者删除一些观察者.这样才能让被观察者及时的通知观察者关系的状态已经改变、而且调用观察者通用的方法将变化传递过去。
在实现观察者模式
,若是JDK的Observable类和一个Observer接口能知足需求,直接复用便可,无需本身编写抽象观察者、抽象主题类;
可是,java.util.Observable是一个类而不是接口,你必须设计一个类继承它。若是某个类想同时具备Observable类和另外一个超类的行为,因为java不支持多重继承。因此这个时候就须要本身实现一整套观察者模式。
优势
缺点
被观察者对象
有不少直接和间接的观察者,那么将全部的观察者都通知到会花费不少时间。观察者模式
是一种使用频率很是高的设计模式,不管是移动应用、Web应用或者桌面应用,观察者模式几乎无处不在,它为实现对象之间的联动提供了一套完整的解决方案,凡是涉及到一对一或者一对多的对象交互场景均可以使用观察者模式。
参考文献:http://www.cnblogs.com/JsonShare/p/7270546.html
全文代码:https://gitee.com/battcn/design-pattern/tree/master/Chapter17/battcn-observer
微信公众号:battcn
(欢迎调戏)