观察者模式的两种实现和比较

来源:互联网 发布:mac物理地址不匹配 编辑:程序博客网 时间:2024/06/03 19:04

为什么要用观察者模式?

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

完。

0 0
原创粉丝点击