观察者模式——解决、解耦的钥匙

观察者模式是一个使用率非常高的模式,它最常用的地方是GUI系统,订阅—发布系统。另外Android中的listview中的notifyDataSetChange函数,都是使用观察者模式。另外Android中的回调模式也有点观察者模式的影子,很像,但并不是完全的观察者模式,回调模式是一对一,观察者模式是一对多。

观察者模式的一个重要作用是解耦,将观察者与被观察者解耦,使得他们之间依赖性更小,甚至做到毫无依赖。

正如上面所说,观察者模式是一种一对多的依赖关系,使得一个对象改变状态时,则所有依赖它的对象都会得到通知并被自动更新。

观察者模式的框图如下:



Subject:抽象主题角色:也就是被观察者(Observer)的角色。把所有对观察者对象的引用保存在一个集合中,每个抽象主题角色都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者角色。一般用一个抽象类或接口来实现。 

ConcreteSubject:具体主题,也叫具体被观察者,该角色将有关状态存入具体的观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发出通知。

Observer:抽象观察者角色:为所有具体的观察者定义一个接口,在得到主题的通知时更新自己。

ConcreteObserver:具体的观察者,该角色实现抽象观察者角色所定义的更新接口,以便在主题的状态发生变化时更新自身的状态。


观察者模式的简单实现:

这里模拟一个网站发布新消息的情形。

UML类图


首先定义Website.java,作为ConcreteSubject

package com.test.observable;
/**
 * Website网站用来作为被观察者
 * @author QHF
 *
 */
import java.util.Observable;
public class Website extends Observable{
	
	public void postNews(String content) {
		setChanged();
		notifyObservers(content);
	}
}

其中Observable是Subject角色,系统已经帮我们实现,源码如下:已删除注释,并添加自己的注释

public class Observable {
	
	//标志位 在通知所有观察者们的时候需要用到
	private boolean changed = false;
	//存放观察者的集合
	private Vector<Observer> obs;

	public Observable() {
		obs = new Vector<>();
	}
	//向集合添加观察者
	public synchronized void addObserver(Observer o) {
		if (o == null)
			throw new NullPointerException();
		if (!obs.contains(o)) {
			obs.addElement(o);
		}
	}
	//删除观察者
	public synchronized void deleteObserver(Observer o) {
		obs.removeElement(o);
	}
	//通知所有观察者
	public void notifyObservers() {
		notifyObservers(null);
	}
	//通知所有观察者 并且带参数
	public void notifyObservers(Object arg) {

		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 synchronized void deleteObservers() {
		obs.removeAllElements();
	}
	//设置标志位为true
	protected synchronized void setChanged() {
		changed = true;
	}
	//清楚标志位
	protected synchronized void clearChanged() {
		changed = false;
	}
	//返回标志位
	public synchronized boolean hasChanged() {
		return changed;
	}
	//返回所有观察者的数目
	public synchronized int countObservers() {
		return obs.size();
	}
}



然后是Reader.java,表示ConcreteObserver角色

package com.test.observable;
/**
 * 网站阅读者 作为观察者
 */
import java.util.Observable;
import java.util.Observer;

public class Reader implements Observer{
	public String name;
	public Reader(String name) {
		this.name = name;
	}
	@Override
	public void update(Observable o, Object arg) {
		// TODO Auto-generated method stub
		System.out.println("Hi, " + name + ",网站更新啦,内容: " + arg);
	}
}

Observer抽象类表示Observer角色,系统已经实现。源码如下

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

其实就是一个接口,里面只有一个抽象方法update();


然后是客户端Client.java

package com.test.observable;

public class Client {
	public static void main(String[] args) {
		//被观察者
		Website website = new Website();
		//观察者
		Reader zhangsan = new Reader("张三");
		Reader lisi = new Reader("李四");
		Reader wangwu = new Reader("王五");
		
		//将观察者注册到可观察者的观察列表中
		
		website.addObserver(zhangsan);
		website.addObserver(lisi);
		website.addObserver(wangwu);
		
		//发布消息
		website.postNews("新的一期网站周报发布啦!!!!,地址为www.uestc.edu.cn");
		
		
		
	}
}


运行之后出结构为

Hi, 王五,网站更新啦,内容: 新的一期网站周报发布啦!!!!,地址为www.uestc,edu.cn
Hi, 李四,网站更新啦,内容: 新的一期网站周报发布啦!!!!,地址为www.uestc,edu.cn
Hi, 张三,网站更新啦,内容: 新的一期网站周报发布啦!!!!,地址为www.uestc,edu.cn

可以看到所有订阅了网站周报的人都收到了更新消息,这就是一个简单的订阅发布系统。在这个过程中,Reader和Website都是依赖于Observer和Observable这些抽象类,因此对于Reader和Website完全没有耦合,保证了订阅系统的灵活性和可扩展性。









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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值