关于观察者模式
假设今天您设计一个图形分析算表程序,当中有一个资料物件,您可以用表格图形物件、柱状图形物件、圆饼图形物件等方式来 呈现物件,无论您是用哪种图形物件,重点是若资料物件的内容作了更改,则图形物件的内容也必须跟着修改,或许您的程式中有两个以上的图形物件来呈现资料,您在图形物件上更动资料,则另一个图形物
件也必须作出相对应的变化。
主题 | 资料物件 | ||
观察者 | 柱状图形 | 表格图形 | 圆饼图形 |
又假设您今天设计一个网络游戏,您在服务器上维护一个连线客户端共享的资料物件,当其中一个客户端作了操作,将对此资料物件作修改,则伺服器必须通知其它 客户端作相对应的变化(像是人物位置走动、建了一个城堡等)。
主题 | 资料物件 | ||
观察者 | 客户端一 | 客户端二 | 客户端三 |
在Observer模式中的主角为主题(subject)与观察者(observer),观察者订阅它感兴趣的主题,一个主题可以被多个观察者订阅,当主题的状态发生变化时,它必须通知(notify)所有订阅它的观察者,观察者检视主题的状态变化,并作出对应的动作,所以Observer 模式也称之为Publish-Subscribe模式。
Observer模式的 UML 图如下所示:
Subject类中有一个notify()方法,通常是在Subject的状态发生改变时呼叫它,notify()中会呼叫 Observer的update()方法,通常会先取得Subject的新状态,然后更新Observer的显示或行为,这个过程我们可以透过 Sequence Diagram来表达:
在Java中支持观察者模式,要成为观察者的类必须实作Observer介面,这个介面中定义了一个update()方法,这个方法会被主题物件在通知状态变化时呼叫,您必须在这个方法中实作您所想要的对应行为。
主题物件会是Observable的子类,在这边注意两个重要的方法:setChanged()与notifyObserver()。 setChanged()是用来设定主题物件的状态已经被改变,而notifyObserver()方法会通知所要订阅主题物件的观察者,调用其 update()方法。
关于Java的相关源码分析
其中Java中的内置的模式的源码如下:
Observer.java接口类
仅有一个Observer的接口类,含有一个update抽象方法.
/* * Copyright (c) 1994, 1998, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * */ package java.util; /** * A class can implement the <code>Observer</code> interface when it * wants to be informed of changes in observable objects. * * @author Chris Warth * @see java.util.Observable * @since JDK1.0 */ public interface Observer { /** * This method is called whenever the observed object is changed. An * application calls an <tt>Observable</tt> object's * <code>notifyObservers</code> method to have all the object's * observers notified of the change. * * @param o the observable object. * @param arg an argument passed to the <code>notifyObservers</code> * method. */ void update(Observable o, Object arg); }
observable.java类
具体的类内容,请看注释文档,这已经非常详细了.
/* * Copyright (c) 1994, 2004, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * */ package java.util; /** * This class represents an observable object, or "data" * in the model-view paradigm. It can be subclassed to represent an * object that the application wants to have observed. * <p> * An observable object can have one or more observers. An observer * may be any object that implements interface <tt>Observer</tt>. After an * observable instance changes, an application calling the * <code>Observable</code>'s <code>notifyObservers</code> method * causes all of its observers to be notified of the change by a call * to their <code>update</code> method. * <p> * The order in which notifications will be delivered is unspecified. * The default implementation provided in the Observable class will * notify Observers in the order in which they registered interest, but * subclasses may change this order, use no guaranteed order, deliver * notifications on separate threads, or may guarantee that their * subclass follows this order, as they choose. * <p> * Note that this notification mechanism is has nothing to do with threads * and is completely separate from the <tt>wait</tt> and <tt>notify</tt> * mechanism of class <tt>Object</tt>. * <p> * When an observable object is newly created, its set of observers is * empty. Two observers are considered the same if and only if the * <tt>equals</tt> method returns true for them. * * @author Chris Warth * @see java.util.Observable#notifyObservers() * @see java.util.Observable#notifyObservers(java.lang.Object) * @see java.util.Observer * @see java.util.Observer#update(java.util.Observable, java.lang.Object) * @since JDK1.0 */ public class Observable { private boolean changed = false; private Vector obs; /** Construct an Observable with zero Observers. */ public Observable() { obs = new Vector(); } /** * Adds an observer to the set of observers for this object, provided * that it is not the same as some observer already in the set. * The order in which notifications will be delivered to multiple * observers is not specified. See the class comment. * * @param o an observer to be added. * @throws NullPointerException if the parameter o is null. */ public synchronized void addObserver(Observer o) { if (o == null) throw new NullPointerException(); if (!obs.contains(o)) { obs.addElement(o); } } /** * Deletes an observer from the set of observers of this object. * Passing <CODE>null</CODE> to this method will have no effect. * @param o the observer to be deleted. */ public synchronized void deleteObserver(Observer o) { obs.removeElement(o); } /** * 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 <code>null</code>. In other * words, this method is equivalent to: * <blockquote><tt> * notifyObservers(null)</tt></blockquote> * * @see java.util.Observable#clearChanged() * @see java.util.Observable#hasChanged() * @see java.util.Observer#update(java.util.Observable, java.lang.Object) */ public void notifyObservers() { notifyObservers(null); } /** * 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); } /** * Clears the observer list so that this object no longer has any observers. */ public synchronized void deleteObservers() { obs.removeAllElements(); } /** * 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; } /** * Tests if this object has changed. * * @return <code>true</code> if and only if the <code>setChanged</code> * method has been called more recently than the * <code>clearChanged</code> method on this object; * <code>false</code> otherwise. * @see java.util.Observable#clearChanged() * @see java.util.Observable#setChanged() */ public synchronized boolean hasChanged() { return changed; } /** * Returns the number of observers of this <tt>Observable</tt> object. * * @return the number of observers of this object. */ public synchronized int countObservers() { return obs.size(); } }<span style="font-size:18px;"> </span>
实现观察者模式
实现观察者模式非常简单,
[1]创建被观察者类,它继承自java.util.Observable类;
[2]创建观察者类,它实现java.util.Observer接口;
[3]对于被观察者类,
添加它的观察者:
/** * Adds an observer to the set of observers for this object, provided * that it is not the same as some observer already in the set. * The order in which notifications will be delivered to multiple * observers is not specified. See the class comment. * * @param o an observer to be added. * @throws NullPointerException if the parameter o is null. */ public synchronized void addObserver(Observer o) { if (o == null) throw new NullPointerException(); if (!obs.contains(o)) { obs.addElement(o); } }
addObserver()方法把观察者对象添加到观察者对象列表中。
当被观察事件发生时,执行:
/** * 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 <code>null</code>. In other * words, this method is equivalent to: * <blockquote><tt> * notifyObservers(null)</tt></blockquote> * * @see java.util.Observable#clearChanged() * @see java.util.Observable#hasChanged() * @see java.util.Observer#update(java.util.Observable, java.lang.Object) */ public void notifyObservers() { notifyObservers(null); } /** * 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; }
只有在setChange()被调用后,notifyObservers()才会去调用update()。setChange()方法用来设置一个内部标志位注明数据发生了变化;notifyObservers()方法会去调用观察者对象列表中所有的Observer的update()方法,通知它们数据发生了变化。
[4]对于观察者类,实现Observer接口的唯一方法update
void update(Observable o, Object arg);
形参Object arg,对应一个由notifyObservers(Object arg);传递来的参数,当执行的是notifyObservers();时,arg为null。
示例
1.NumObserable是一个被观察者,当它的成员变量data的数值发生变化时,会通知所有的观察者。
被观察者--主题类
/** * 被观察者--即是主题 * @author SuooL * @version 1.0.0 * <p> <a herf="suool.net" > SuooL's Blog </a> */ package observer; import java.util.Observable; /** * 被观察者--主题类 * */ public class numObservable extends Observable { private int data = 0; /** * 获取数字值 * * @return 取得的data */ public int getData() { return data; } /** * 设置数字值 * * @param i 要设置的数字值 */ public void setData(int i) { data = i; setChanged(); notifyObservers(); } }
观察者--订阅者
/** * 观察者--即是订阅者 * @author SuooL * @version 1.0.0 * <p> <a herf="suool.net" > SuooL's Blog </a> */ package observer; import java.util.Observable; import java.util.Observer; /** * 继承自<code>Observer</code>接口类的观察者类 * @see java.util.Observer */ public class numObserver implements Observer{ /** * Observe接口类的的唯一抽象方<code>update</code>的实现 * <p>只有在setChange()被调用后,notifyObservers()才会去调用update()。 *@param o 主题类型 *@param arg Object类型参数, * 对应一个由<code>notifyObservers(Object arg);</code>传递来的参数, * 当执行的是<code>notifyObservers();</code>时,arg为<code>null</code>。 * */ public void update(Observable o, Object arg) { numObservable myObserable=(numObservable) o; System.out.println("Data has changed to " +myObserable.getData()); } }
test.
/** * 测试 * @author SuooL * @version 1.0.0 * <p> <a herf="suool.net" > SuooL's Blog </a> */ package observer; /** * 测试 */ public class observerTest { public static void main(String[] args) { // 创建一个主题对象 numObservable number = new numObservable(); // 为主题对象增加订阅者 number.addObserver(new numObserver()); // 修改主题对象 number.setData(1); number.setData(2); number.setData(3); } }
结果如下:
2.这个实例中,还是对data进行观察,拥有两个观察者,分别观察奇数和偶数的变化,通过notifyObservers(arg)中的参数arg来识别通知信息.
NumsObservable.java
/** * Description: * 被观察者--主题类 * <p>两个私有静态变量,分别变化 * <p>被两个订阅者订阅,分别关注不同的兴趣点--奇数变化 OR 偶数变化 * @author SuooL * @version 1.0.0 * <p> <a herf = "http://suool.net"> SuooL's Blog </a> */ package observers; import java.util.Observable; /** * 主题类 */ public class NumsObservable extends Observable { public final static Integer ODD = 1; public final static Integer EVEN = 2; private int data = 0; /** * 获取对象的数据 * @return data 返回取得的数据 */ public int getData() { return data; } /** * 设置数据变化 * 根据数据的变化设置相应的标志变量,通知给订阅者 * @param i 要设置的数据 */ public void setData(int i) { data = i; Integer flag = EVEN; if ((data & 0x0001) == 1) flag = ODD; setChanged(); // 将变化的变化的标识变量通知给订阅者 notifyObservers(flag); } }
/** * Description: * 偶数观察者类 * <p>订阅主题的内容的偶数变化 * @author SuooL * @version 1.0.0 * <p> <a herf = "http://suool.net"> SuooL's Blog </a> */ package observers; import java.util.Observable; import java.util.Observer; /** * 偶数内容订阅类 * @author SuooL * */ public class evenObserver implements Observer { /** * 继承自Observer接口类,update的方法的实现 * @param o 主题对象 * @param arg notifyObservers(flag);传来的参数,即是标识变量 */ public void update(Observable o, Object arg) { if (arg == NumsObservable.EVEN) { NumsObservable myObserable=(NumsObservable) o; System.out.println("Data has changed to EVEN number " +myObserable.getData()); } } }
/** * Description: * 奇数观察者类 * <p>订阅主题的内容的奇数变化 * @author SuooL * @version 1.0.0 * <p> <a herf = "http://suool.net"> SuooL's Blog </a> */ package observers; import java.util.Observable; import java.util.Observer; /** * 奇数内容订阅类 * @author SuooL * */ public class oddObserver implements Observer{ /** * 继承自Observer接口类,update的方法的实现 * @param o 主题对象 * @param arg notifyObservers(flag);传来的参数,即是标识变量 */ public void update(Observable o, Object arg) { if (arg == NumsObservable.ODD) { NumsObservable myObserable=(NumsObservable) o; System.out.println("Data has changed to ODD number " +myObserable.getData()); } } }
测试类:
/** * Description: * 被观察者--主题类 * <p>两个私有静态变量,分别变化 * <p>被两个订阅者订阅,分别关注不同的兴趣点--奇数变化 OR 偶数变化 * @author SuooL * @version 1.0.0 * <p> <a herf = "http://suool.net"> SuooL's Blog </a> */ package observers; public class NumsTest { public static void main(String[] args) { // TODO Auto-generated method stub // 创建主题对象和订阅者对象 NumsObservable observerNum = new NumsObservable(); oddObserver oddObserver = new oddObserver(); evenObserver evenObserver = new evenObserver(); // 为主题增加订阅者 observerNum.addObserver(oddObserver); observerNum.addObserver(evenObserver); // 修改主题对象内容 observerNum.setData(12); observerNum.setData(11); observerNum.setData(10); } }
运行结果: