Android 设计模式之观察者模式

1,660 阅读5分钟

在日常开发过程中时常需要用到设计模式,但是设计模式有23种,如何将这些设计模式了然于胸并且能在实际开发过程中应用得得心应手呢?和我一起跟着《Android源码设计模式解析与实战》一书边学边应用吧!

设计模式系列文章

今天我们要讲的是观察者模式


定义

定义对象间的一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。

使用场景

  • 关联行为场景
  • 事件多级触发场景
  • 跨系统的信息交换场景,如消息队列、事件总线的处理机制

使用例子

  • 常见的发布-订阅模式
  • ListView的Adapter的notifyDataSetChanged更新方法
  • BroadcastReceiver
  • 开源库EventBus
  • RxJava

实现

四大角色

  • 抽象主题,也就是被观察者(Observable),抽象主题把所有的观察者对象的引用保存在一个集合里,每个主题可以有任意数量的观察者,抽象主题提供接口,可以增加和删除观察者对象
  • 具体的主题(具体的被观察者),也就是抽象主题的子类,该角色将有关状态存入具体观察者对象,在具体主题内部状态发生改变时,通知所有注册过的观察者
  • 抽象观察者,观察者的抽象类,定义了一个更新的接口
  • 具体的观察者,实现了抽象观察者的更新接口,在被观察者状态发生变化时更新自身的状态

实现的要点

  • 实现的要点在于把握好以上四个角色
  • 设计模式是一种思想,在应用过程中可以根据具体的需要来实现。RxJava和ListView的Adapter都用到了观察者模式,但实现上肯定会有不一样

实现方式

利用JDK中Observable类和Observer接口实现

  • JDK中有Observable类和Observer接口
  • 观察者实现Observer接口,被观察者继承Observable类
  • 被观察者通过Observable类的addObserver方法添加观察者

观察者

public class MyObserver implements Observer{
    private String mName;

    public MyObserver(String name) {
        mName = name;
    }

    @Override
    public void update(Observable o, Object arg) {
        System.out.println(mName + "-->" + "update: " + arg);
    }
}
  • Observer接口就一个update方法,Observable表示被观察者,Object表示被观察者更新的东西

被观察者

public class MyObservable extends Observable{
    public void sendChangeMeg(String content) {
        //方法继承自Observable,标示状态或是内容发生改变
        setChanged();

        //方法继承自Observable,通知所有观察者,最后会调用每个Observer的update方法
        notifyObservers(content);
    }
}
  • 被观察者通过setChanged()方法标示改变,通过notifyObservers方法通知所有观察者
  • notifyObservers方法会遍历所有的观察者Observer,并调用它们的update方法
  • notifyObservers方法中的参数就是最后传到观察者update方法的参数Object arg

测试

public class ObserverPatternTest {
    @Test
    public void test1() throws Exception {
        MyObservable myObservable = new MyObservable();

        MyObserver myObserver1 = new MyObserver("observer-1");
        MyObserver myObserver2 = new MyObserver("observer-2");
        myObservable.addObserver(myObserver1);
        myObservable.addObserver(myObserver2);

        //发布消息
        myObservable.sendChangeMeg("发布更新啦");
    }

}

测试结果

观察者模式测试结果

  • 不知道大家看到上面的测试结果有没有什么疑问,细心的小伙伴可能会发现我们添加观察者的时候顺序是myObserver1、myObserver2,但是结果输出确是先myObserver2再myObserver1,为啥?

看看源码

Observable的源码
public class Observable {
    private boolean changed = false;
    private final ArrayList<Observer> observers;

    /** Construct an Observable with zero Observers. */

    public Observable() {
        observers = new ArrayList<>();
    }
    
    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!observers.contains(o)) {
            observers.add(o);
        }
    }

    public synchronized void deleteObserver(Observer o) {
        observers.remove(o);
    }

    public void notifyObservers() {
        notifyObservers(null);
    }
  
    public void notifyObservers(Object arg) {
        /*
         * a temporary array buffer, used as a snapshot of the state of
         * current Observers.
         */
        Observer[] arrLocal;

        synchronized (this) {
            if (!hasChanged())
                return;

            arrLocal = observers.toArray(new Observer[observers.size()]);
            clearChanged();
        }

        //注意这里的起始点
        for (int i = arrLocal.length-1; i>=0; i--)
            arrLocal[i].update(this, arg);
    }

    public synchronized void deleteObservers() {
        observers.clear();
    }

    protected synchronized void setChanged() {
        changed = true;
    }

    protected synchronized void clearChanged() {
        changed = false;
    }

    public synchronized boolean hasChanged() {
        return changed;
    }

    public synchronized int countObservers() {
        return observers.size();
    }
}

  • 首先在生成Observable对象时,会初始化一个ArrayList,用于保存所有的观察者Observer
  • 当我们调用notifyObservers时,会循环遍历调用所有添加的观察者Observer,并调用Observer的update方法,而遍历的顺序是从最后添加的一个Observer开始的,所以会有我们上面测试结果图片的情况
  • 解耦的关键就在于,Observer是一个接口,而我们的观察者都实现了这个接口
public interface Observer {
    void update(Observable o, Object arg);
}
  • 想想这样设计遵循了什么设计原则呢?如果忘记了我们前面学得六大原则,可以去复习一下。
其他的实现方式,比如ListView的Adapter以及RxJava里面的观察者模式,大家也可以去尝试看看源码,找找看观察者模式中的四大角色

总结

  • 观察者模式是使用率很高的模式,它的一个重要作用就是解耦,将观察者个被观察者解耦
  • 常用的地方是GUI系统,发布-订阅系统
  • 应用观察者模式需要考虑开发效率和运行效率

欢迎关注我的微信公众号,期待与你一起学习,一起交流,一起成长!

AntDream