关于观察者模式
假设今天您设计一个图形分析算表程序,当中有一个资料物件,您可以用表格图形物件、柱状图形物件、圆饼图形物件等方式来 呈现物件,无论您是用哪种图形物件,重点是若资料物件的内容作了更改,则图形物件的内容也必须跟着修改,或许您的程式中有两个以上的图形物件来呈现资料,您在图形物件上更动资料,则另一个图形物 件也必须作出相对应的变化。
主题 | 资料物件 | ||
观察者 | 柱状图形 | 表格图形 | 圆饼图形 |
又假设您今天设计一个网络游戏,您在服务器上维护一个连线客户端共享的资料物件,当其中一个客户端作了操作,将对此资料物件作修改,则伺服器必须通知其它 客户端作相对应的变化(像是人物位置走动、建了一个城堡等)。
主题 | 资料物件 | ||
观察者 | 客户端一 | 客户端二 | 客户端三 |
在Observer模式中的主角为主题(subject)与观察者(observer),观察者订阅它感兴趣的主题,一个主题可以被多个观察者订阅,当主题的状态发生变化时,它必须通知(notify)所有订阅它的观察者,观察者检视主题的状态变化,并作出对应的动作,所以Observer 模式也称之为Publish-Subscribe模式。
Observer模式的 UML 图如下所示:
Subject类中有一个notify()方法,通常是在Subject的状态发生改变时呼叫它,notify()中会呼叫 Observer的update()方法,通常会先取得Subject的新状态,然后更新Observer的显示或行为,这个过程我们可以透过 Sequence Diagram来表达:
主题物件会是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);
}
}
运行结果: