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

概述

观察者模式(Observer Pattern)是一个非常有用的模式,在Java语言当中,有许多地方用到了这一模式,比如Swing当中的事件模型。这一模式其实是建立在观察者模式基础之上的。这篇文章就来看看这个模式的一些基本的东西。
定义:
这个模式主要用来定义对象之间的一种一对多的依赖关系。当一个对象的状态发生改变的时候,所以依赖于它的对象得到通知并且自动更新。也称为发布-订阅模式(Publish-Subscribe)。这种模式使得对象之间的关系非常松,也就是松耦合。如下图:

结构:
该模式中的四个参与者分别为:目标(Subject)、具体目标(Concrete Subject)、观察者(Observer)、具体观察者(Concrete Observer)。

分别介绍如下:
  • Subject:Subject知道它的观察者,一个目标可以有多个观察者。(其实,一个观察者也可以对应多个目标,因此定义中一对多的说法并不是很准确。Subject还提供注册和删除观察者对象的接口。
  • Concrete Subject: 将观察者感兴趣的状态存入观察者对象,当状态改变的时候,通知各个观察者。
  • Observer: 定义一个更新接口
  • Concrete Observer: 维护一个指向ConcreteSubject对象的引用,存储有关的状态,实现Observer接口中的更新接口。

Java类库中的实现:

Java类库中对观察者模式提供的支持并没有完全按照上面的描述。
Observer接口:
对于观察者,它提供了一个Observer接口,位于java.util包中,之定义了一个update方法。如下:
void update(Observable o, Object arg);

有2个参数,其中Observable是目标的引用,可以通过这一引用获取目标的状态或者进行其他操作。Object是一个与具体实现逻辑相关的对象引用,也用于传递操作相关的信息。
Observable类:
Java中没有提供目标的接口,而是直接提供一个类。这样做有好有坏,我们就可以省去许多代码,同时也失去了一定的灵活性。来看一下这个类提供的各个方法;
注册、删除观察者:
以下三个方法用于注册、删除观察者,返回观察者数量等。
    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }

这个类采用Vector类型(私有与obs)来维护所有观察者的引用。很明白,增加一个观察者。
    public synchronized void deleteObservers() {
        obs.removeAllElements();
    }
    public synchronized void deleteObservers() {
        obs.removeAllElements();
    }

死一个方法删除一个观察者,后一个方法删除全部观察者。一样简单明了。
    public synchronized int countObservers() {
        return obs.size();
    }

返回观察者数量,没什么可说的。

有关目标状态的几个方法:
通过私有的布尔类型变量changed来判断、操作。
    public synchronized boolean hasChanged() {
        return changed;
    }

查看当前目标状态是否改变。
    protected synchronized void setChanged() {
        changed = true;
    }

设置状态改变标志位true。
    protected synchronized void clearChanged() {
        changed = false;
    }

清空状态改变标志位。
注意一下各个方法的访问控制符,具体实现的时候小心使用。

通知观察者:
这些方法通知观察者。其实就是调用观察者的update方法,并将改变标志位清空。通知所有观察者,可以传递一个对象用于交互。如下:
    public void notifyObservers(Object arg) {
        /*
         * a temporary array buffer, used as a snapshot of the state of
         * current Observers.
         */
        Object[] arrLocal;

        synchronized (this) {
            if (!changed)
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }

        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }

    public void notifyObservers() {
        notifyObservers(null);
    }

一样很明了,比较麻烦的就是多线程的问题了,不多说。

一个简单例子:

下面是一个最简单的例子。DataStore作为目标,Screen作为观察者,同时有一个特殊的观察者SpecialScreen,这个观察着在第一次得到通知之后就把自己从观察者中删除。
DataStore:
import java.util.Observable;

/**
 * @author Brandon B. Lin
 *
 */
public class DataStore extends Observable {
	private String data;

	public String getData() {
		return data;
	}

	public void setData(String data) {
		this.data = data;
		/*mark the abservable as changed*/
		setChanged();
	}

}

Screen:
import java.util.Observable;
import java.util.Observer;

/**
 * @author Brandon B. Lin
 *
 */
public class Screen implements Observer {

	protected int observerId;

	public Screen(int observerId) {
		this.observerId = observerId;
	}

	@Override
	public void update(Observable o, Object arg) {
		// act on the update
		System.out.println(observerId + ": The subject has changed!");

	}

}

SpecialScreen:
import java.util.Observable;

public class SpecialObserver extends Screen {

	public SpecialObserver(int observerId) {
		super(observerId);
	}

	@Override
	public void update(Observable o, Object arg) {
		// act on the update
		System.out.println(observerId
				+ ": The subject has changed! and I will leave!");
		o.deleteObserver(this);

	}

}

Test:
/**
 * @author Brandon B. Lin
 * 
 */
public class Test {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		DataStore dataStore = new DataStore();
		Screen[] screens = new Screen[10];
		for (int i = 0; i < 10; i++) {
			if (i == 5) {
				screens[i] = new SpecialObserver(i);
			} else {
				screens[i] = new Screen(i);
			}

			dataStore.addObserver(screens[i]);
		}

		dataStore.setData("new data");
		dataStore.notifyObservers();

		dataStore.setData("Latest data");
		dataStore.notifyObservers();

	}

}

参考资料:

《设计模式:可复用面向对象软件的基础》
JDK源码

声明:版权所有,转载务必注明出处,本作者保留一切法律权利。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值