定义对象间的一种一对多的依赖关系。当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
为了当对象的状态发生改变时,所有依赖它的对象得到通知并进行相应处理。
被观察的对象称之为Subject,观察者成为Observer
(1)图形化编程
(2)分布式事件处理
推模型:目标对象主动向观察者推动目标的详细信息,不管观察者是否需要,推动的信息通常是目标对象的全部或部分数据,相当于是在广播通信。
拉模型:目标对象在通知观察者的时候,只传递少量信息,如果观察者需要更具体的信息,由观察者主动到目标对象中获取,相当于是观察者从目标对象中拉数据。一般这种模型的实现中,会把目标对象自身通过update方法传递给观察者,这样在观察者需要获取数据的时候,就可以通过这个引用来获取了。
推模型假定目标对象知道观察者需要的数据,
而拉模型是目标对象不知道观察者具体需要什么数据,没有办法的情况下,干脆把自己传递给观察者,让观察者自己去按需取值。
推模型可能会使得观察者对象难以复用,因为观察者定义的update方法是按需而定义的,可能无法兼顾没有考虑到的使用情况,当新情况出现的时候,就可能需要提供新的update方法,或者干脆重新实现观察者。
拉模型update参数是目标对象本身,是能够传递的最大数据集合了,基本上可以适应各种情况的需要。
/** * 目标对象,作为被观察者 */ public class Subject { /** * 用来保存注册的观察者对象,也就是报纸的订阅者 */ private List<Observer> readers = new ArrayList<Observer>(); /** * 报纸的读者需要先向报社订阅,先要注册 * @param reader 报纸的读者 * @return 是否注册成功 */ public void attach(Observer reader) { readers.add(reader); } /** * 报纸的读者可以取消订阅 * @param reader 报纸的读者 * @return 是否取消成功 */ public void detach(Observer reader) { readers.remove(reader); } /** * 当每期报纸印刷出来后,就要迅速的主动的被送到读者的手中, * 相当于通知读者,让他们知道 */ protected void notifyObservers() { for(Observer reader : readers){ reader.update(this); } } }
具体subject
/** * 报纸对象,具体的目标实现 */ public class NewsPaper extends Subject{ /** * 报纸的具体内容 */ private String content; /** * 获取报纸的具体内容 * @return 报纸的具体内容 */ public String getContent() { return content; } /** * 示意,设置报纸的具体内容,相当于要出版报纸了 * @param content 报纸的具体内容 */ public void setContent(String content) { this.content = content; //内容有了,说明又出报纸了,那就通知所有的读者 notifyObservers(); } }
观察者
/** * 观察者,比如报纸的读者 */ public interface Observer { /** * 被通知的方法 * @param subject 具体的目标对象,可以获取报纸的内容 */ public void update(Subject subject); }
观察者实现
/** * 真正的读者,为了简单就描述一下姓名 */ public class Reader implements Observer{ /** * 读者的姓名 */ private String name; public void update(Subject subject) { //这是采用拉的方式 System.out.println(name+"收到报纸了,阅读先。内容是==="+((NewsPaper)subject).getContent()); } public String getName() { return name; } public void setName(String name) { this.name = name; } }
客户端
public class Client { public static void main(String[] args) { //创建一个报纸,作为被观察者 NewsPaper subject = new NewsPaper(); //创建阅读者,也就是观察者 Reader reader1 = new Reader(); reader1.setName("张三"); Reader reader2 = new Reader(); reader2.setName("李四"); Reader reader3 = new Reader(); reader3.setName("王五"); //注册阅读者 subject.attach(reader1); subject.attach(reader2); subject.attach(reader3); //要出报纸啦 subject.setContent("本期内容是观察者模式"); } }
观察者
/** * 观察者,比如报纸的读者 */ public interface Observer { /** * 被通知的方法,直接把报纸的内容推送过来 * @param content 报纸的内容 */ public void update(String content); }
观察者实现
/** * 真正的读者,为了简单就描述一下姓名 */ public class Reader implements Observer{ /** * 读者的姓名 */ private String name; public void update(String content) { //这是采用推的方式 System.out.println(name+"收到报纸了,阅读先。内容是==="+content); } public String getName() { return name; } public void setName(String name) { this.name = name; } }
Subject类
/** * 目标对象,作为被观察者,使用推模型 */ public class Subject { /** * 用来保存注册的观察者对象,也就是报纸的订阅者 */ private List<Observer> readers = new ArrayList<Observer>(); /** * 报纸的读者需要先向报社订阅,先要注册 * @param reader 报纸的读者 * @return 是否注册成功 */ public void attach(Observer reader) { readers.add(reader); } /** * 报纸的读者可以取消订阅 * @param reader 报纸的读者 * @return 是否取消成功 */ public void detach(Observer reader) { readers.remove(reader); } /** * 当每期报纸印刷出来后,就要迅速的主动的被送到读者的手中, * 相当于通知读者,让他们知道 * @param content 要主动推送的内容 */ protected void notifyObservers(String content) { for(Observer reader : readers){ reader.update(content); } } }
subject类实现
/** * 报纸对象,具体的目标实现 */ public class NewsPaper extends Subject{ /** * 报纸的具体内容 */ private String content; /** * 获取报纸的具体内容 * @return 报纸的具体内容 */ public String getContent() { return content; } /** * 示意,设置报纸的具体内容,相当于要出版报纸了 * @param content 报纸的具体内容 */ public void setContent(String content) { this.content = content; //内容有了,说明又出报纸了,那就通知所有的读者 notifyObservers(content); } }
Java的util包里面有一个类Observable,它实现了大部分我们需要的目标的功能;
还有一个Observer,定义了update方法,就是观察者的接口。
1、不需要再定义Subject和Observer类
2、具体的Subject实现类里面不再需要维护Observer的注册信息
3、触发通知方式有点变化,需要先调用setChanged方法
4、具体的Observer实现类里面,update方法能同时指出推模型和拉模型
Subject实现类
/** * 报纸对象,具体的目标实现 */ public class NewsPaper extends java.util.Observable{ /** * 报纸的具体内容 */ private String content; /** * 获取报纸的具体内容 * @return 报纸的具体内容 */ public String getContent() { return content; } /** * 示意,设置报纸的具体内容,相当于要出版报纸了 * @param content 报纸的具体内容 */ public void setContent(String content) { this.content = content; //内容有了,说明又出报纸了,那就通知所有的读者 //注意在用Java中的Observer模式的时候,这句话不可少 this.setChanged(); //然后主动通知,这里用的是推的方式 this.notifyObservers(this.content); //如果用拉的方式,这么调用 //this.notifyObservers(); } }
Obsever实现类
/** * 真正的读者,为了简单就描述一下姓名 */ public class Reader implements java.util.Observer{ /** * 读者的姓名 */ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public void update(Observable o, Object obj) { //这是采用推的方式 System.out.println(name+"收到报纸了,阅读先。目标推过来的内容是==="+obj); //这是获取拉的数据 System.out.println(name+"收到报纸了,阅读先。主动到目标对象去拉的内容是===" +((NewsPaper)o).getContent()); } }
客户端
public class Client { public static void main(String[] args) { //创建一个报纸,作为被观察者 NewsPaper subject = new NewsPaper(); //创建阅读者,也就是观察者 Reader reader1 = new Reader(); reader1.setName("张三"); Reader reader2 = new Reader(); reader2.setName("李四"); Reader reader3 = new Reader(); reader3.setName("王五"); //注册阅读者 subject.addObserver(reader1); subject.addObserver(reader2); subject.addObserver(reader3); //要出报纸啦 subject.setContent("本期内容是观察者模式"); } }
/** * If this object has changed, as indicated by the * <code>hasChanged</code> method, then notify all of its observers * and then call the <code>clearChanged</code> method to indicate * that this object has no longer changed. * <p> * Each observer has its <code>update</code> method called with two * arguments: this observable object and the <code>arg</code> argument. * * @param arg any object. * @see java.util.Observable#clearChanged() * @see java.util.Observable#hasChanged() * @see java.util.Observer#update(java.util.Observable, java.lang.Object) */ 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); } /** * Marks this <tt>Observable</tt> object as having been changed; the * <tt>hasChanged</tt> method will now return <tt>true</tt>. */ protected synchronized void setChanged() { changed = true; } /** * Indicates that this object has no longer changed, or that it has * already notified all of its observers of its most recent change, * so that the <tt>hasChanged</tt> method will now return <tt>false</tt>. * This method is called automatically by the * <code>notifyObservers</code> methods. * * @see java.util.Observable#notifyObservers() * @see java.util.Observable#notifyObservers(java.lang.Object) */ protected synchronized void clearChanged() { changed = false; }
采用倒序的方式通知。
采用JDK的观察者模式的限制:
A、继承Observable类,如果需要多继承的话,只能采用新类继承它,然后组合新累,调用setChanged方法,因为该方法时protected的
B、如果需要更改通知顺序的话,需要自己专门来处理