《设计模式》- 观察者模式

375 阅读3分钟

定义

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

使用场景

  • 关联行为场景,需要注意的是,关联行为是可拆分的,而不是“组合”关系。
  • 时间多级触发场景
  • 跨系统的消息交换场景,如消息队列、时间总线的处理机制。

UML 类图

classDiagram
Subject o.. Observer
Subject <|-- ConcreteSubject
Observer <|-- ConcreteObserver
class Subject{
    <<abstract>>
    +register(Observer)
    +unregister(Observer)
    +nofityObservers(Object)
}
class ConcreteSubject{
    +nofityObservers(Object)
}
class Observer{
    <<interface>>
    +update(Object)
}
class ConcreteObserver{
    +update(Object)
}
  • Subject: 抽象主题,也就是被观察者(Observable)角色,抽象主题把所以观察者对象的引用保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以新增和删除观察者对象。
  • ConcerteSubject: 具体主题,该角色将有关状态存入具体观察者对象,在具体主题的内容状态发生改变时,给所有注册过的观察者发出通知,具体主题角色又叫做具体观察者(ConsreteObservable)角色。
  • Observer: 抽象观察者,改角色是观察者的抽象类,它定义了一个更新接口,使得在得到主题的更改通知时更新自己。
  • ConcreteObserver: 具体观察者,该角色实现抽象观察者所定义的更新接口,以便在主题状态发生改变时更新自身的状态。

Android 中的观察者模式

Android源码中有很多使用了观察者模式,比如OnClickListener、ContentObserver、 android.database.Observer 等。还有很多组件库Rxjava、RxAndroid、EventBus;这里我们来看下常用的Adapter的notifyDataSetChanged()。

public abstract class BaseAdapter implements ListAdapter,SpinnerAdapter {
    
    //数据集观察者
    private final DataSetObservable mDataSetObservable = new DataSetObservable();

    public void registerDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.registerObserver(observer);
    }

    public void unregisterDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.unregisterObserver(observer);
    }
    
    /**
     * 当数据变化时,通知所有观察者
     */
    public void notifyDataSetChanged() {
        mDataSetObservable.notifyChanged();
    }
    
}

这里可以看到BaseAdapter 里有一个DataSetObservable 的被观察者,通过register*()、unregister*()方法添加和删除观察者。我们往下看DataSetObservable 这个类

public class DataSetObservable extends Observable<DataSetObserver> {
    /**
     * 当数据变化时调用每个观察者的onChanged() 方法通知它们
     */
    public void notifyChanged() {
        synchronized(mObservers) {
            // since onChanged() is implemented by the app, it could do anything, including
            // removing itself from {@link mObservers} - and that could cause problems if
            // an iterator is used on the ArrayList {@link mObservers}.
            // to avoid such problems, just march thru the list in the reverse order.
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }
    }
}

这里其实很简单,就是遍历所有观察者并调用它们的onChanged()。接下来我们看是谁调用了BaseAdapter 的 registerDataSetObserver(DataSetObserver observer)方法。

public void setAdapter(ListAdapter adapter) {
        if (mAdapter != null && mDataSetObserver != null) {
            mAdapter.unregisterDataSetObserver(mDataSetObserver);
        }
        
        //省略代码...

        // AbsListView#setAdapter will update choice mode states.
        super.setAdapter(adapter);

        if (mAdapter != null) {
            mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
            mOldItemCount = mItemCount;
            mItemCount = mAdapter.getCount();
            checkFocus();
            //创建一个数据集观察者
            mDataSetObserver = new AdapterDataSetObserver();
            //将这个观察者注册到 Adapter 的 DataSetObservable 中
            mAdapter.registerDataSetObserver(mDataSetObserver);
            //省略代码...
        } else {
            //省略代码...
        }

        requestLayout();
    }

以上可以看出ListVeiw 中有一个观察者(AdapterDataSetObserver)在setAdapter() 的时候注册到了Adapter 的DataSetObservable 中,当Adapter 调用 notifyDataSetChanged() 方法时就会通知 ListView 中的观察者(AdapterDataSetObserver)更新状态。

小结

观察者模式主要的作用就是解耦,将观察者与被观察者完全隔离,只依赖于 Observer 和 Observable 抽象,提供代码的灵活性和可扩展性。

优点: 观察者和被观察者直接是抽象耦合,应对业务变化。增强系统灵活性、可扩展性。

缺点: 在应用观察者模式时需要考虑一下开发效率和运行效率问题,因为一个被观察者可能直接或间接有很多观察者,Java 消息中的通知默认顺序执行,通知到所以观察者可能需要花费些时间,如果一个观察者卡顿,会影响整体的执行效率,这里一般考虑采用异步的方式。