Java设计模式之观察者模式(Observer Pattern)

      观察者模式是Java23种设计模式中行为模式的一种。观察者模式定义了对象间的一种一对多的关系,使得每当一种对象改变状态,则所有依赖于它的对象都会得到通知并采用一种机制实现自动更新。观察者模式的被观察者对象即目标对象中保留了观察者的集合,当被观察者对象的状态发生变化时,会遍历观察者对象集合,然后通知观察者对象得到更新。下面上图:


 这张图非常简洁明了的说明了观察者模式当中各个角色的分工,下面按照上面的图来进行代码实现。

 目标接口Subject:

public interface Subject {

	/**
	 * 注册观察者 统一管理
	 * 
	 * @param observer
	 *            观察者对象
	 */
	public void registerObserver(Observer observer);

	/**
	 * 移除观察者
	 * 
	 * @param observer
	 *            观察者对象
	 */
	public void removeObserver(Observer observer);

	/**
	 * 当对象的状态改变后 通知更新
	 */
	public void notifyObservers();

}
  观察者接口Observer:
public interface Observer {
	/**
	 *        
	 * @param message  当依赖的对象的状态有所改变会被调用
	 */
     void update(String message);
}
  目标的实现接口ConcreteSubject:
public class ConcreteSubject implements Subject {

	// 观察者的集合
	protected List<Observer> observers = new ArrayList<>();
	// 用字符串模拟状态的改变
	private String message;

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
		// 当状态改变后 马上调用通知观察者的方法
		notifyObservers();
	}

	@Override
	public void registerObserver(Observer observer) {
		observers.add(observer);
	}

	@Override
	public void removeObserver(Observer observer) {
		observers.remove(observer);
	}

	@Override
	public void notifyObservers() {
		for (Observer observer : observers) {
			observer.update(getMessage());
		}
	}

}

  观察者的实现类ConcreteObserver:

public class ConcreteObserver implements Observer {
    /**
     *  为每一个观察者取一个名字 便于分辨
     */
	private String observerName;
	
	public  ConcreteObserver(String mString) {
		this.observerName = mString;
	}
	@Override
	public void update(String message) {
		System.out.println(observerName+"我收到消息啦,消息是:"+message);
	}

}

 我们按照上面的图例已经对上面的四大角色实现了,到这里一个简单的观察者模式框架已经搭好了,下面测试来看看。

 测试类ObserverTest:

public class ObserverTest {

	public static void main(String[] args) {
		// 1 获取具体主题的对象
		ConcreteSubject concreteSubject = new ConcreteSubject();
		// 2 获取观察者的对象
		ConcreteObserver concreteObserver1 = new ConcreteObserver("小明");
		ConcreteObserver concreteObserver2 = new ConcreteObserver("小红");
		// 3 将被观察者和观察者进行绑定
		concreteSubject.registerObserver(concreteObserver1);
		concreteSubject.registerObserver(concreteObserver2);

		// 4 模拟被观察者的状态发生改变
		concreteSubject.setMessage("有点困~~~~");
	}

}
  看一下打印结果:

小明我收到消息啦,消息是:有点困~~~~
小红我收到消息啦,消息是:有点困~~~~
  其实观察者模式再细分的话也分两种模式,一种是推消息模式也叫推模式;还有一种叫拉取消息模式也叫拉模式;

  我们上面的实现方式就叫推模式,为什么呢?大家可以看观察者的接口中的方法void update(String message),它的参数类型是String,这说明什么呢?说明目标对象只有String类型状态的数据改变才能推送过来,才能通知我更新,如果目标对象里面还有int或者double或者其他类型的数据状态改变了呢?这个时候观察者就不知道了,这样就引出了第二种模式---拉模式,拉模式其实很简单 就是把更新的方法中的参数类型改一下,改成什么呢?大家想一想就知道了,改成目标对象,获取了目标对象,那么我们想得到什么数据就能得到什么数据,因为我们可以自己去取。好,下面来实现。

  改造观察者接口Observer:

public interface Observer {
	/**
	 *        
	 * @param message  当依赖的对象的状态有所改变会被调用
	 */
     void update(Subject subject);
}
 四大角色都会稍微的变化:
public class ConcreteObserver implements Observer {
    /**
     *  为每一个观察者取一个名字 便于分辨
     */
	private String observerName;
	
	public  ConcreteObserver(String mString) {
		this.observerName = mString;
	}
	@Override
	public void update(Subject subject) {
		System.out.println(observerName+"我收到消息啦,消息是:"+
	((ConcreteSubject)subject).getMessage()+"---当前的状态是:"+((ConcreteSubject)subject).getStatus());
	}

}
public interface Subject {

    /**
     * 注册观察者 统一管理
     * 
     * @param observer
     *            观察者对象
     */
    public void registerObserver(Observer observer);

    /**
     * 移除观察者
     * 
     * @param observer
     *            观察者对象
     */
    public void removeObserver(Observer observer);

    /**
     * 当对象的状态改变后 通知更新
     */
    public void notifyObservers();

}
public class ConcreteSubject implements Subject {

    // 观察者的集合
    protected List<Observer> observers = new ArrayList<>();
    // 用字符串模拟状态的改变
    private String message;
    // 模拟第二种状态
    private int status;

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;

    }

    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(this);
        }
    }

}
  再看下测试类:
public class ObserverTest {

	public static void main(String[] args) {
		// 1 获取具体主题的对象
		ConcreteSubject concreteSubject = new ConcreteSubject();
		// 2 获取观察者的对象
		ConcreteObserver concreteObserver1 = new ConcreteObserver("小明");
		ConcreteObserver concreteObserver2 = new ConcreteObserver("小红");
		// 3 将被观察者和观察者进行绑定
		concreteSubject.registerObserver(concreteObserver1);
		concreteSubject.registerObserver(concreteObserver2);

		// 4 模拟被观察者的状态发生改变
		concreteSubject.setMessage("有点困~~~~");
		concreteSubject.setStatus(1);
		concreteSubject.notifyObservers();
	}

}

  看下打印结果:

小明我收到消息啦,消息是:有点困~~~~---当前的状态是:1
小红我收到消息啦,消息是:有点困~~~~---当前的状态是:1
  这就实现了拉模式,你把你的对象给我,我想要什么数据我来自己取,这里不仅获取到了String类型的数据还获取到了int类型的数据,上面的两种方式大家可以根据实际来进行应用并没有说哪一种方式就一定比另外的一种方式好。

  其实,对观察者模式还不是很熟悉的可以按照上面的图例来敲一遍代码,已经很熟悉的同学可能已经知道,java中已经帮我们实现的目标接口(Observable)和观察者接口(Observer),是不是很震惊,下面我们一起看看。先看下Java中目标接口的源码:

/*
 * Copyright (c) 1994, 2012, 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 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<Observer> 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();
    }
}
  大家看一下Java中的Observable并不是一个接口,而是以基类的形式让我们自己写的类来继承,基类里面已经实现添加观察者和删除观察者以及通知观察者数据变化的方法,子类只需要自己去重写一下就好,那么我们自己来重写一下,其实过程和开篇的过程是一样的。
  看下我们自己的实现过程(两个实现类一个测试类):

public class ConcreteSubject extends Observable {
    
	private String message;

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
		this.setChanged();
		this.notifyObservers(message);
	}
	
}

public class ConcreteObserver implements Observer {
    
    private String observerName;
    public ConcreteObserver(String name){
        this.observerName = name;
    }
    @Override
    public void update(Observable arg0, Object arg1) {
       System.out.println(observerName+"我收到的拉模式消息是:"+((ConcreteSubject)arg0).getMessage());
       System.out.println(observerName+"我收到的推推推模式消息是:"+arg1);
    }

}

public class JavaTest {
    public static void main(String[] args) {
        ConcreteSubject concreteSubject = new ConcreteSubject();
        
        ConcreteObserver concreteObserver1 = new ConcreteObserver("小明");
        ConcreteObserver concreteObserver2 = new ConcreteObserver("小红");
        
        concreteSubject.addObserver(concreteObserver1);
        concreteSubject.addObserver(concreteObserver2);
        
        concreteSubject.setMessage("写博客饿了~~");

    }
}
  打印结果:

小红我收到的拉模式消息是:写博客饿了~~
小红我收到的推推推模式消息是:写博客饿了~~
小明我收到的拉模式消息是:写博客饿了~~
小明我收到的推推推模式消息是:写博客饿了~~
  可以看得出Java中的观察者接口update(Observable arg0, Object arg1)有两个参数,一个是目标的对象,一个是Object,所以这两个你都能拿到,所以算是推拉模式的方式都实现了,看来人家写代码考虑的真的很全面啊。

  但是使用Java自带的观察者模式有需要注意:

  通知更新的调用必须在目标子类的内部调用了,为什么呢?看一下源码就知道了:

    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);
    }
  源码中的更新调用的notifyObservers(Object arg)可以看得到,先是对changed变量进行判断,changed为false直接跳出了就不会进行更新,那么我们看下changed是在哪赋值的呢?
  protected synchronized void setChanged() {
        changed = true;
    }
 在setChanged()进行了赋值,然而这个方法是protected的,所以只能在子类的内部调用,所以在通知更新之前必须调用setChanged方法而且只能在子类的内部调用才能真正的实现观察者的更新。

  好了,观察者模式就写到这了,自己的印象也更加深刻了。其实在安卓中也有很多用观察者模式的例子,如:比如OnClickListener、ContentObserver、android.database.Observable,还有组件通讯库RxJava、RxAndroid、EventBus等等,希望大家都能够做到学以致用,举一反三。

 



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
环境:Windows XP professional, JDK 1.6, Eclipse 3.3 Europa. 我们说学习Java应该从Swing开始,那么学习Swing最重要的思想就是对于观察者模式的理解(Observer Pattern)。因为,该设计模式Java Swing框架中贯穿了始终。对于C#的委托、代理概念所使用的Callback(回调模式--参见本人的“第一个C#程序Hello World ”资源)来看,Java Swing编程是纯粹的OO语言、是一种非常优雅的编程语言(本人认为)--而C#的学习好像非常强调所谓的OO思想,但是不是知道是什么OO思想?(个人观点,请勿激动)。 该示例是翻写一老外Observer Pattern的例子,本人觉得该示例是对该模式的非常精典的说明,于是花点功夫来翻写它,因为他的代码没有注释说明该模式的设计思想怎样体现在代码中去,所以,本人结合代码注释了关键代码的中文意义说明作者怎样表示该设计模式的,以方便Java程序员阅读和理解代码,从而理解该设计模式的思想(本人的注释非常详尽)。 目的,本人认为,作为Java程序员--不是指只会使用JBuilder(或者Websphare等)工具拖拽的java操作员--我们指至少能使用Eclipse工具书写Swing代码的Java编程员,或者使用ant工具编程的Java程序员,应该熟练掌握该设计模式!因为该设计模式贯穿Swing框架的始终。 阅读对象:本锦江中心中从S1阶段就想成为一个Java程序员的学员,并且在S1阶段已经非常认真的学习Java编程的学员。 注:不过该代码应该是本锦江中心Y2阶段Java方向学习的学员的参考代码,因为,该代码是由Y2阶段本教员会讲解。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值