写在模式学习之前
什么是设计模式:在我们进行程序设计时,逐渐形成了一些典型问题和问题的解决方案,这就是软件模式;每一个模式描述了一个在我们程序设计中经常发生的问题,以及该问题的解决方案;当我们碰到模式所描述的问题,就可以直接用相应的解决方法去解决这个问题,这就是设计模式。
设计模式就是抽象出来的东西,它不是学出来的,是用出来的;或许你根本不知道任何模式,不考虑任何模式,却写着最优秀的代码,即使以“模式专家”的角度来看,都是最佳的设计,不得不说是“最佳的模式实践”,这是因为你积累了很多的实践经验,知道“在什么场合代码应该怎么写”,这本身就是设计模式。
有人说:“水平没到,学也白学,水平到了,无师自通”。诚然,模式背熟,依然可能写不出好代码,更别说设计出好框架;OOP理解及实践经验到达一定水平,同时也意味着总结了很多好的设计经验,但"无师自通",却也未必尽然,或者可以说,恰恰是在水平和经验的基础上,到了该系统的学习一下“模式”的时候了,学习一下专家总结的结果,印证一下自己的不足,对于提高水平还是很有帮助的。
本系列的设计模式学习笔记,实际是对于《Java与模式》这本书的学习记录。
观察者模式的定义
观察者模式,又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式、从属者(Dependerts)模式。
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同事监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新。
观察者模式的 结构
结构图
所涉及的角色
(1)抽象主题(Subject)角色:主题角色吧所有对观察者对象的引用保存在一个聚集(比如Vector对象)里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,主题角色又叫做抽象被观察者(Observable)角色,一般用一个抽象类或者一个接口实现。
(2)抽象观察者(Observer)角色:为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。这个接口叫做更新接口。抽象观察者角色一般用一个抽象类或者一个接口实现。
(3)具体主题(ConcreteSubject)角色:将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做具体被观察者角色(Concrete Observable)。具体主题角色通常用一个具体子类实现,负责实现如何 引用观察者的聚集的管理办法。
(4)具体观察者(ConcreteObserver)角色:存储于主题的状态自洽的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。如果需要,具体观察者角色可以保存一个指向具体主题对象的引用。具体观察者角色通常用一个具体子类实现。
代码实现
import java.util.*;
interface Subject
{
//调用这个方法登记一个新的观察者对象
void attach(Observer observer);
//调用这个方法删除一个登记过的观察者对象
void detach(Observer observer);
//调用这个方法通知所有登记过的观察者对象
void notifyObservers();
}
class ConcreteSubject implements Subject
{
private Vector observersVector = new Vector();
public void attach(Observer observer)
{
observersVector.addElement(observer);
}
public void detach(Observer observer)
{
observersVector.removeElement(observer);
}
public void notifyObservers()
{
Enumeration e = observers();
while(e.hasMoreElements())
{
((Observer)e.nextElement()).update();
}
}
public Enumeration observers()
{
return ((Vector)observersVector.clone()).elements();
}
}
interface Observer
{
void update();
}
class ConcreteObserver implements Observer
{
public void update()
{
System.out.println("I am notified");
}
}
JDK对观察者模式的支持
JDK提供了java.util.Observable类和Observer接口,构成Java语言对观察者模式的支持。
Java对观察者模式的实现,是把对观察者(Observer)的维护和引用,放在了抽象类(Observable)中,从而大大减小具体被观察者对象需要书写的代码。
下面演示一个使用这两个接口/对象的例子。
结构图
代码实现
import java.util.Observer;
import java.util.Observable;
class Tester
{
private static Watched watched;
private static Observer watcher;
public static void main(String[] args)
{
//创建被观察者对象
watched = new Watched();
//创建观察者对象,并将被观察者对象登记
watcher = new Watcher(watched);
//给给观察者对象的状态赋值2次
watched.changeData("I am Tom");
watched.changeData("I am Jack");
}
}
class Watched extends Observable
{
private String data = "";
public String retrieveData()
{
return data;
}
public void changeData(String data)
{
if(!this.data.equals(data))
{
this.data = data;
setChanged();
}
notifyObservers();
}
}
class Watcher implements Observer
{
public Watcher(Watched w)
{
w.addObserver(this);
}
public void update(Observable ob,Object arg)
{
System.out.println("Data has been changed to: '" + ((Watched)ob).retrieveData() + "'");
}
}
Observable源码
我们也来看看JDK源码:java.util.Observable:
package java.util;
class Observable
{
private boolean changed = false;
private Vector obs;
public Observable()
{
obs = new Vector();
}
public synchronized void addObserver(Observer o)
{
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();
}
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();
}
}
观察者模式的效果
优点:
(1)观察者模式在被观察者和观察者时间建立一个抽象的耦合。被观察者角色所知道的的只是一个具体观察者聚集,每一个具体观察者都符合抽象观察者接口。被观察者并不认识任何一个具体观察者,它只知道它们都有一个共同的接口。由于被观察者和观察者没有紧急地耦合在一起,因此它们可以属于不同的抽象化层次。如果被观察者和观察者都被扔到一起,那么这个对象必然跨越抽象化和具体化层次。
(2)观察者模式支持广播通信。被观察者会向所有的登记过的观察者发出通知。
缺点:
(1)如果一个被观察者对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
(2)如果在被观察者直接有循环依赖的话,被观察者会触发它们直接进行循环调用,导致系统崩溃。在使用观察者模式时要特别注意这一点。
(3)如果对观察者的通知时通过另外的线程进行异步投递的话,系统必须保证投递是以自洽的方式进行的。
(4)虽然观察者模式可以随时是观察者知道所观察的对象发生了编号,但是观察者模式模式没有相应的机制使观察者知道所观察的对象是怎么发生编号的。
更多了解
在分布式的编程框架中,在面向服务的编程模型中,即使我们根本不知道观察者模式,但是我们可能一直在使用着其思想。