现在的位置: 首页 > 综合 > 正文

Observer 模式及JAVA内置的observer示例

2019年03月02日 ⁄ 综合 ⁄ 共 13451字 ⁄ 字号 评论关闭

关于观察者模式

假设今天您设计一个图形分析算表程序,当中有一个资料物件,您可以用表格图形物件、柱状图形物件、圆饼图形物件等方式来 呈现物件,无论您是用哪种图形物件,重点是若资料物件的内容作了更改,则图形物件的内容也必须跟着修改或许您的程式中有两个以上的图形物件来呈现资料,您在图形物件上更动资料,则另一个图形物
件也必须作出相对应的变化。

主题 资料物件
观察者 柱状图形 表格图形 圆饼图形



又假设您今天设计一个网络游戏,您在服务器上维护一个连线客户端共享的资料物件,当其中一个客户端作了操作,将对此资料物件作修改,则伺服器必须通知其它 客户端作相对应的变化(像是人物位置走动、建了一个城堡等)。

主题 资料物件
观察者 客户端一 客户端二 客户端三



在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);
	}
}

运行结果:


抱歉!评论已关闭.