java设计模式之观察者模式

好久没有写博客了,这段时间在项目中接触到了java设计模式中的观察者模式,因为之前从来没有接触过,所以特意花费了一点时间用来学习,在这里也把它记录下来,毕竟好记性不如烂笔头,闲言少叙,话不多说,书归正传。

一、概念(来自百度百科)

观察者模式:(有时又被称为模型(Model)-视图(View)模式、源-收听者(Listener)模式或从属者模式),是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。

百度百科里的概念过于正式,换做自己的话来说,观察者模式就相当于java里面的一对多的关系,多的一方称之为观察者,可以是一,也可以是多,一的一方也被称为被观察者,用现实中的某样事物来比喻的话,个人觉得更像是报纸,被观察者就相当于报纸公司,它在报纸中发布各种信息,然后每一名读者,也就是购买报纸的人,就会收到这条信息。

二、案例

首先需要定义一个被观察者的抽象类

package com.eplugger.test;

import java.util.ArrayList;
import java.util.List;

/**
 * 抽象公司类(作为被观察者,也就是消息发送者)
 */
public abstract class Company {
    // 观察者的集合,也就是订阅该报纸的所有读者
    private List<Person> observers = new ArrayList<Person>();
    
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Company(String name) {
        this.name = name;
    }
    
    // 新增观察者(读者)
    public void addObservers(Person observer){
        observers.add(observer);
    }
    
    // 移除观察者(读者)
    public void removeObservers(Person observer){
        observers.remove(observer);
    }
    
    // 通知观察者(读者)
    public void notifyObservers(){
        for (Person observer : observers) {
            observer.update();
        }
    }

}

接着,定义观察者的抽象类

package com.eplugger.test;

/**
 * 定义抽象观察者
 * 
 * */
public abstract class Person {
    
    private String name;
    private Company company;
    
    public String getName() {
        return name;
    }
    
    public Company getCompany() {
        return company;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public void setCompany(Company company) {
        this.company= company;
    }

    public Person(String name, Company company) {
        this.name = name;
        this.company = company;
    }
    
    public abstract void update();

}

下面是两者的具体实现类

package com.eplugger.test;
/**
 * 具体的消息通知者(报社)
 * 
 * */
public class Newspaper extends Company{

    public Newspaper(String name){
        super(name);
    }
    
    private String title;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }
    
}
package com.eplugger.test;
/**
 * 具体观察者(读者)
 * 
 * */
public class Readers extends Person{

    public Readers(String name, Newspaper newspaper) {
        super(name, newspaper);
    }

    @Override
    public void update(){
        System.out.println(super.getName()+"...."+super.getCompany().getName()+"....通知....");
    }
}

测试代码

package com.eplugger.test;

public class Test1 {

    public static void main(String[] args) {
        Newspaper newspaper = new Newspaper("新华日报");
        Readers readers = new Readers("张三", newspaper);
        
        newspaper.removeObservers(readers);
        newspaper.notifyObservers();
    }
}

上面描述的这是一种最基本,最简单的观察者模式,是建立在所有的观察者都能抽象出一个公共抽象类的基础上,但是在实际的项目运用中,往往没有办法做到所有的观察者都能够抽象出同一个公共抽象类,这就得用到Java自带的Observer接口以提供对观察者模式的支持,同时也会运用到JDK来实现观察者模式。

在Java中,已经有现成的接口和类,对于观察者,也就是消息接收者,Java提供了一个接口,源码如下

package java.util;

/**
 * A class can implement the <code>Observer</code> interface when it
 * wants to be informed of changes in observable objects.
 *
 */
public interface Observer {

    void update(Observable o, Object arg);
}

对于被观察者,也就是消息的发布者,Java提供了一个抽象类,源码如下

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.
 *
 */
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>
     *
     */
    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.
     *
     */
    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.
     *
     */
    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.
     */
    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的源码和上述我自己写的区别,可以看到,源码中使用Vector而不是ArrayList,相比而言,Vector是线程安全的,其次,在添加和删除观察者时对两个方法使用了synchronized关键字,这都是因为多线程的原因,可以看出来,源码考虑的要全面的多。

利用Java提供的接口和抽象类观察者模式和上述我自写的差不多,就放一个小demo,简单的看一下就ok了。

Demo

package com.eplugger.demo;

import java.util.Observable;
import java.util.Observer;

public class Readers implements Observer{
    
    public Readers(Observable o){
        o.addObserver(this);
    }

    @Override
    public void update(Observable o, Object arg) {
        System.out.println("名字是:" + ((Newspaper)o).getName());    
    }
}
package com.eplugger.demo;

import java.util.Observable;

public class Newspaper extends Observable {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        if (!this.name.equals(name)){
            this.name = name;
            setChanged();
        }
        notifyObservers();
    }
}
package com.eplugger.demo;

public class Test {

    public static void main(String[] args) {
        Newspaper newspaper = new Newspaper();
        Readers readers = new Readers(newspaper);
        
        newspaper.setName("新华日报");
    }
}

运用Java自带的接口和抽象类来实现观察者模式,其原理和上一个案例差不多。

三、总结

观察者模式的结构

java设计模式之观察者模式

观察者模式的优点:

1、实现了表示层和数据逻辑层的分离,定义了稳定的消息更新传递机制,抽象了接口,使得可以适应不同的表现层充当观察者角色。

2、在观察目标和观察者之间建立了一个抽象耦合,观察目标只需要维持一个抽象观察者的集合,无需了解具体的观察者。

3、支持广播通信,观察目标会向所有的观察者对象发送通知,简化了一对多系统设计的难度。

4、满足“开闭原则“的要求,增加新的具体观察者无需修改原有系统代码,在具体观察者与观察目标之间不存在关联关系的情况下,增加新的观察目标也很方便。

观察者模式的缺点:

1、如果一个被观察者有着很多观察者,那么会耗费很长的时间。

2、没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

3、 Java中自带的观察者模式,被观察者是一个抽象类而不是接口,限制了复用能力。

应用场景:

一个对象的改变将会导致一个或多个对象的改变的情况,即一对多的程序设计。