为什么要用观察者模式?
GOF那本书里面是这样描述的:
将一个系统分割成一系列相互协作的类有一个常见的副作用:需要维护相关对象间的一 致性。我们不希望为了维持一致性而使各类紧密耦合,因为这样降低了它们的可重用性。
情景
假设我们有一组数据要用于不同的方案,比如schemeA和schemeB,出于某种原因,A和B之间不能通信,他们不知道彼此的存在。要求当方案改变时可以同时通知到A和B。
BadSolution :
一个比较不好的解决方法是在数据类里面保持SchemeA和SchemeB的对象来改变数据,Data的代码如下所示:
private SchemeA schemeA;
private SchemeB schemeB;
public void setData(int data1,int data2){
schemeA = new SchemeA();
schemeB = new SchemeB();
schemeA.setData(data1,data2);
schemeB.setData(data1,data2);
}
通过调用setData()来改变数据,这样的做法可以实现但确是一个不好的设计,比如当方案B要被方案C代替了,那么Data类的数据要重写。再者,如果有很多方案的话,这个setData()方法需要将这些方案都写进去。简单来说就是高度耦合,重用性低。这种方案的类顺序图如下所示:
观察者模式来实现
jdk里面实现了观察者模式,这里我们先自己实现这个模式,后文再讲jdk里面的实现。
首先我们定义一个主题Subject:
public interface Subject {
void addObserver(Observer o);
void removeObserver(Observer o);
void notifyAllObservers();
}
注意到他有三个方法分别可以注册观察者,移除观察者和通知所有观察者。Data类通过实现这个接口来为SchemeA和SchemeB传送消息。
Data类实现如下:
public class Data implements Subject {
ArrayList observers;
private int data1, data2;
public Data(){
this.observers = new ArrayList();
}
@Override
public void addObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer o) {
int i = observers.indexOf(o);
if(i>0) observers.remove(i);
}
@Override
public void notifyAllObservers() {
for (Object obj:observers){
Observer observer = (Observer)obj;
observer.update(data1,data2);
}
}
public void setData(int data1,int data2){
this.data1 = data1;
this.data2 = data2;
measureChange();
}
public void measureChange(){
notifyAllObservers();
}
}
而观察者类则可以这样实现:
public interface Observer {
void update(int data1,int data2);
}
SchemeA和SchemeB通过实现这个接口的update方法来更新数据。那么Data类怎么知道SchemeA和SchemeB就是观察者呢?一个可行的实现是在构造SchemeA和SchemeB的时候传入Data对象,然后调用Data类的addObserver()方法来为两个方案类注册。
这个系统的类交互图如下所示:
这样设计有什么好处呢?
我们可以看Data类里面的代码,完全没有SchemeA和SchemeB的痕迹,也就是说,无论有多少个方案需要数据,无论方案怎么改变,这个Data类都不需要发生改变,也就可以重用了。而且方案A和方案B之间不知道彼此的存在就能实现数据的同时更新。
用jdk提供的功能来实现
我们的Data类变得简单起来了,因为java.util.Obserable这个类给我们做了很多事情。
public class Data extends Observable {
private int data1,data2;
public Data(){
}
public void setData(int data1,int data2){
this.data1 = data1;
this.data2= data2;
mesureChange();
}
public void mesureChange(){
setChanged();
notifyObservers();
}
public int getData1() {
return data1;
}
public int getData2() {
return data2;
}
}
我们只需要像之前实现Subject类那样来继承Observable这个类(不过使用继承的方式会有很多限制,因为java不允许多重继承但可以实现多个接口)。注意measureChange()这个方法,里面调用Observable的两个方法setChange()和notifyObervers()方法。这两个方法的实现都很简单。
protected synchronized void setChanged() {
changed = true;
}
public void notifyObservers() {
notifyObservers(null);
}
调用有参的notifyObservers()方法。
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);
}
这样我们只要使SchemeA和SchemeB方法都实现java.util.Observer接口就可以了。
public class SchemeA implements Observer {
private int data1,data2;
private Observable o;
public SchemeA(Observable o){
o.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
if(o instanceof Data){
data1 = ((Data) o).getData1();
data2 = ((Data) o).getData2();
}
display();
}
public void display(){
System.out.println("SchemeA display data1 is "+data1+" data2 is "+data2);
}
}
使用jdk内置的观察者模式代码简单多了,但是却有时候会有限制,比如Data类已经继承了Observable类,就不能继承其他类了。注意Observable是一个类而不是一个接口。
献上github代码:
https://github.com/cris1313/mingle/tree/master/src/demo/designpatern/observer
完。