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

为什么要用观察者模式?

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值