《Android源码设计模式解析与实战》读书笔记(十二)

第十二章、观察者模式

观察者模式是一个使用率非常高的模式,它最常用在GUI系统、订阅–发布系统。因为这个模式的一个重要作用就是解耦,将被观察者和观察者解耦,使得它们之间的依赖性更小,甚至做到毫无依赖。比如安卓的开源项目EventBus、Otto、AndroidEventBus等事件总线类的和RxJava响应式编程其核心都是使用观察者模式。

1.定义

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

2.使用场景

(1)关联行为场景,需要注意的是,关联行为是可拆分的,而不是“组合”关系。

(2)事件多级触发场景。

(3)跨系统的消息交换场景,如消息队列、事件总线的处理机制。

3.简单实现

这里举一个追剧的例子,平常为了不错过最新的电视剧我们会订阅或关注这个电视剧,当电视剧更新后会第一时间推送给我们,下来就简单实现一下。

抽象观察者类

/**
 *  抽象观察者类,为所有具体观察者定义一个接口,在得到通知时更新自己
 */
public interface Observer {
    /**
     *  有更新
     *
     *  @param message 消息
     */
    public void update(String message);

}

抽象被观察者类

/**
 * 抽象被观察者类
 */
public interface Observable {

    /**
     * 推送消息
     *
     * @param message 内容
     */
    void push(String message);

    /**
     * 订阅
     *
     * @param observer 订阅者
     */
    void register(Observer observer);
}

具体的观察者类

/**
 * 具体的观察者类,也就是订阅者
 */
public class User implements Observer {

    @Override
    public void update(String message) {
        System.out.println(name + "," + message + "更新了!");

    }

    // 订阅者的名字
    private String name;

    public User(String name) {
        this.name = name;
    }
}

具体的被观察者类

/**
 *  具体的被观察者类,也就是订阅的节目
 */
public class Teleplay implements Observable{

    private List<Observer> list = new ArrayList<Observer>();//储存订阅者

    @Override
    public void push(String message) {
        for(Observer observer:list){
            observer.update(message);
        }
    }

    @Override
    public void register(Observer observer) {
        list.add(observer);
    }

}

实现

public class Client {
    public static void main(String[] args) {
        //被观察者,这里就是用户订阅的电视剧
        Teleplay teleplay = new Teleplay();
        //观察者,这里就是订阅用户
        User user1 = new User("小明");
        User user2 = new User("小光");
        User user3 = new User("小兰");
        //订阅
        teleplay.register(user1);
        teleplay.register(user2);
        teleplay.register(user3);
        //推送新消息
        teleplay.push("xxx电视剧");
    }
}

结果

小明,xxx电视剧更新了!
小光,xxx电视剧更新了!
小兰,xxx电视剧更新了!

由上面的代码可以看出实现了一对多的消息推送,推送消息都是依赖Observer和Observable这些抽象类,而User和Teleplay完全没有耦合,保证了订阅系统的灵活性和可扩展性。

4.Android源码中的观察者模式

1.BaseAdapter

BaseAdapter我相信大家都不陌生,在ListView的适配器中我们都是继承它。下面来简单分析分析。

BaseAdapter 部分代码:

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {

    //数据集观察者
    private final DataSetObservable mDataSetObservable = new DataSetObservable();

    public boolean hasStableIds() {
        return false;
    }

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

    public void unregisterDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.unregisterObserver(observer);
    }

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

看看mDataSetObservable.notifyChanged()方法:

public class DataSetObservable extends Observable<DataSetObserver> {
    /**
     * Invokes {@link DataSetObserver#onChanged} on each observer.
     * Called when the contents of the data set have changed.  The recipient
     * will obtain the new contents the next time it queries the data set.
     */
    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();
            }
        }
    }

可以看出在mDataSetObservable.notifyChanged()中遍历所有观察者,并调用他们的onChanged(),从而告知观察者发生了什么。

那么观察者怎么来的,那就是setAdapter方法,代码如下:

 @Override
    public void setAdapter(ListAdapter adapter) {
        if (mAdapter != null && mDataSetObserver != null) {
            mAdapter.unregisterDataSetObserver(mDataSetObserver);
        }

        resetList();
        mRecycler.clear();

        if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
            mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
        } else {
            mAdapter = adapter;
        }

        mOldSelectedPosition = INVALID_POSITION;
        mOldSelectedRowId = INVALID_ROW_ID;

        // 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();
            mAdapter.registerDataSetObserver(mDataSetObserver);//注册观察者

            ......省略
        }

AdapterDataSetObserver定义在ListView的父类AbsListView中,是一个数据集观察者,代码:

class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
        @Override
        public void onChanged() {
            super.onChanged();
            if (mFastScroller != null) {
                mFastScroller.onSectionsChanged();
            }
        }

        @Override
        public void onInvalidated() {
            super.onInvalidated();
            if (mFastScroller != null) {
                mFastScroller.onSectionsChanged();
            }
        }
    }

它由继承自AbsListView的父类AdapterView的AdapterDataSetObserver, 代码如下 :

class AdapterDataSetObserver extends DataSetObserver {

        private Parcelable mInstanceState = null;
        // 上文有说道,调用Adapter的notifyDataSetChanged的时候会调用所有观察者的onChanged方法,核心实现就在这里
        @Override
        public void onChanged() {
            mDataChanged = true;
            mOldItemCount = mItemCount;
            // 获取Adapter中数据的数量
            mItemCount = getAdapter().getCount();

            // Detect the case where a cursor that was previously invalidated has
            // been repopulated with new data.
            if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
                    && mOldItemCount == 0 && mItemCount > 0) {
                AdapterView.this.onRestoreInstanceState(mInstanceState);
                mInstanceState = null;
            } else {
                rememberSyncState();
            }
            checkFocus();
            // 重新布局ListView、GridView等AdapterView组件
            requestLayout();
        }

        // 代码省略

        public void clearSavedState() {
            mInstanceState = null;
        }
    }

当ListView的数据发生变化时,调用Adapter的notifyDataSetChanged函数,这个函数又会调用DataSetObservable的notifyChanged函数,这个函数会调用所有观察者 (AdapterDataSetObserver) 的onChanged方法。这就是一个观察者模式!

5.总结

1.优点

(1)观察者和被观察者之间是抽象耦合,应对业务变化。

(2)增强系统的灵活性和可扩展性。

2.缺点

在应用观察者模式时需要考虑一下开发效率和运行效率的问题,程序中包括一个被观察者、多个观察者,开发、调试等内容会比较复杂,而且在Java中消息的通知一般是顺序执行,那么一个观察者卡顿,会影响整体的执行效率,在这种情况下,一般会采用异步实现。

6.参考

链接:Android 设计模式 之 观察者模式

时间: 2024-12-25 06:38:40

《Android源码设计模式解析与实战》读书笔记(十二)的相关文章

《Android源码设计模式解析与实战》读书笔记(十三)

第十三章.备忘录模式 备忘录模式是一种行为模式,该模式用于保存对象当前的状态,并且在之后可以再次恢复到此状态,有点像是我们平常说的"后悔药". 1.定义 在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样,以后就可将该对象恢复到原先保存的状态. 2.使用场景 (1)需要保存一个对象在某一个时刻的状态或部分状态. (2)如果用一个接口来让其他对象得到这些状态,将会暴露对象的实现细节并破坏对象的封装性,一个对象不希望外界直接访问其内部状态,通过中间对象可以间接访

《Android源码设计模式解析与实战》读书笔记(十八)

第十八章.代理模式 代理模式也称委托模式,是结构型设计模式之一.是应用广泛的模式之一. 1.定义 为其他对象提供一种代理以控制对这个对象的访问. 2.使用场景 当无法或不想直接访问某个对象或访问某个对象存在困难时可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,委托对象与代理对象需要实现相同的接口. 3.UML类图 (1)Subject:抽象主题类,声明真实主题与共同接口方法,该类可以是抽象类或接口. (2)RealSubject:真实主题类(被委托类),尤其执行具体的业务逻辑方法.

《Android源码设计模式解析与实战》读书笔记(二十二)

第二十二章.享元模式 享元模式是结构型设计模式之一,是对对象池的一种实现.就像它的名字一样,共享对象,避免重复的创建.我们常用的String 就是使用了共享模式,所以String类型的对象创建后就不可改变,如果当两个String对象所包含的内容相同时,JVM只创建一个String对象对应这两个不同的对象引用. 1.定义 采用一个共享来避免大量拥有相同内容对象的开销.使用享元模式可有效支持大量的细粒度对象. 2.使用场景 (1)系统中存在大量的相似对象. (2)细粒度的对象都具备较接近的外部状态,

《Android源码设计模式解析与实战》读书笔记(十四)

第十四章.迭代器模式 迭代器模式,又叫做游标模式,是行为型设计模式之一.我们知道对容器对象的访问必然会涉及遍历算法,我们可以将遍历的方法封装在容器中,或者不提供遍历方法,让使用容器的人自己去实现去吧.这两种情况好像都能够解决问题. 然而在前一种情况,容器承受了过多的功能,它不仅要负责自己"容器"内的元素维护(添加.删除等等),而且还要提供遍历自身的接口:而且由于遍历状态保存的问题,不能对同一个容器对象同时进行多个遍历.第二种方式倒是省事,却又将容器的内部细节暴露无遗. 正因于此,迭代器

《Android源码设计模式解析与实战》读书笔记(十一)

第十一章.命令模式 命令模式是行为型模式之一.总体来说并不难理解,只是比较繁琐,他会将简单的调用关系解耦成多个部分,增加类的复杂度,但是即便如此,命令模式的结构依然清晰. 1.定义 将一个请求封装成一个对象,从而让用户使用不同的请求把客户端参数化:对请求排队或者记录请求日志,以及支持可撤销的操作. 2.使用场景 (1)需要抽出待执行的动作,然后以参数的形式提供出来. (2)在不同的时刻指定.排列和执行请求.一个命令对象可以有与初始请求无关的生存期. (3)需要支持操作取消. (4)支持修改日志功

《Android源码设计模式解析与实战》读书笔记(二十一)

第二十一章.装饰模式 装饰模式也称为包装模式,是结构型设计模式之一.装饰模式是一种用于替代继承技术的一种方案. 1.定义 动态的给一个对象添加一些额外的职责.就增加功能来说,装饰模式相比生成子类更为灵活. 2.使用场景 (1)需要透明且动态地扩展类的功能时.且在不影响其他对象的情况下. (2)当不能采用继承对系统进行扩展时可以使用装饰模式.比如final类. 3.UML类图 (1)Component:抽象组件.可以是一个接口或抽象类,其充当的就是被装饰的原始对象. (2)ConcreteComp

《Android源码设计模式解析与实战》读书笔记(二十四)

第二十四章.桥接模式 桥接模式也称为桥梁模式,是结构型设计模式之一.桥接模式中体现了"单一职责原则"."开闭原则"."里氏替换原则"."依赖倒置原则"等.同时它也是很实用的一种模式. 1.定义 将抽象部分与现实部分分离,使它们都可以独立地进行变化. 2.使用场景 (1)如果一个系统需要在构建的抽象化角色和具体角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系. (2)对于那些不希望使用继承或因为多层次继承导致系统类

《Android源码设计模式解析与实战》读书笔记(十)

第十章.解释器模式 解释器模式是一种用的比较少的行为型模式,其提供了一种解释语言的语法或表达式的方式.但是它的使用场景确实很广泛,只是因为我们自己很少回去构造一个语言的文法,所以使用较少. 1.定义 给定一个语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子.(其中语言就是我们需要解释的对象,文法就是这个语言的规律,解释器就是翻译机,通过文法来翻译语言.) 2.使用场景 1.如果某个简单的语言需要解释执行而且可以将该语言中的语句表示为一个抽象的语法树时可以考虑使

《Android源码设计模式解析与实战》读书笔记(八)

第八章.状态模式 1.定义 状态模式中的行为是由状态来决定,不同的状态下有不同的行为.当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类. 2.使用场景 1.一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为. 2.代码中包含大量与对象状态有关的条件语句,例如,一个操作中含有大量的多分支语句,且这些分支依赖于该对象的状态. 3.简单实现 实现效果:首先将电视的状态分为开机与关机状态,开机时可以通过遥控器实现频道切换和调节音量,但是关机时,这些操作都会失效.