设计模式之观察者模式

一、什么叫观察者?

先理解一个简单的例子:很多购房者都在关注房子的价格变化,每当房子的价格变化时,所有购房者都可以观察得到,实际上购房者都属于观察者。

  • 观察者模式的定义:定义对象间一种一对多的依赖关系,使得每当一个对象发生改变,则所有依赖她的对象都会等到通知并被自动更新。
  • 使用场景:1、 关联行为场景;2、事件多级触发场景;3、跨系统的消息交互场景,如消息队列、事件总线的处理机制

二、观察者模式实现

在Java.util包中提供了Observable和Observer接口,使用它们即可完成观察者模式。
Observable:需要被观察的类必须继承Observable类。Observable类的常用方法有:

  • 1、public void addObserver(Observer o) : 添加一个观察者;
  • 2、public void deleteObserver(Observer o):删除一个观察者;
  • 3、public void setChanged() ; 被观察者状态发生改变;
  • 4、public void notifyObservers(Object args) ; 通知所有观察者状态改变;

Observer:每个观察者都需要实现Observer接口,Observer接口定义如下:

public interface Observer {
     /**
     *当被观察者发生变化,并调用了notifyObserver方法,就调用该方法
     * @param o   被观察者
     * @param args  修改的内容
     */
    void update(Observable o, Object arg);
}

例:

package sl.com.designmodedemo.observer;
import java.util.Observable;
import java.util.Observer;
/**
 * 定义房子为被观察者
 */
class House extends Observable{
    private float price ;   //定义房子价格
    public House(float price) {
        this.price = price;
    }
    public float getPrice() {
        return price;
    }
    public void setPrice(float price) {
        this.price = price;
        setChanged();           //通知变化点
        notifyObservers(price);      //通知所有观察者价格改变
    }
    @Override
    public String toString() {
        return "房子的价格是:" + this.price;
    }
}
/**
 * 房子价格变化观察者
 */
class HousePriceObserver implements Observer{
    private String name ;       //观察房子价格变化的人名
    public HousePriceObserver(String name) {
        this.name = name;
    }
    @Override
    public void update(Observable o, Object arg) {
            if (arg instanceof Float){              //判断参数类型
                System.out.println(this.name + " 观察到的价格更改为:" + ((float)arg));
            }
    }
}

/**
 * 测试
 */
public class Test {
    public static void main(String args[]){
        House house = new House(1000000.0F);
        HousePriceObserver observer1 = new HousePriceObserver("观察者A");
        HousePriceObserver observer2 = new HousePriceObserver("观察者B");
        HousePriceObserver observer3 = new HousePriceObserver("观察者C");
        //加入观察者
        house.addObserver(observer1);
        house.addObserver(observer2);
        house.addObserver(observer3);
        //输出房子价格
        System.out.println(house);
        //修改房子价格
        house.setPrice(2000000.0F);
        System.out.println(house);
    }
}

程序运行结果为:

房子的价格是:1000000.0
观察者C 观察到的价格更改为:2000000.0
观察者B 观察到的价格更改为:2000000.0
观察者A 观察到的价格更改为:2000000.0
房子的价格是:2000000.0

从程序运行结果可以发现,多个观察者都在观察着价格的变化,当被观察者房子价格一变化,则所有观察者都会知道;、

三、Android 源码分析

ListView是Android中重要控件之一,而ListView有一个Adapter,通常我们在给ListView的数据进行操作变化后,都会调用Adapter的notifyDataSetChanged()方法,这是为什么呢?下面我们从源码解析:
第一步,跟进notifyDataSetChanged()方法,这个方法定义在android.widget的BaseAdapter中,具体代码如下:

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就是一个观察者模式,那么BaseAdapter是如何运作的?这些观察者又是什么?下面接着一步一步的分析。
现在先到mDataSetObservalbe.notifyChanged()的源码看看:

public class DataSetObservable extends Observable<DataSetObserver> {
    /**
    * 调用每个观察者的onChanged函数来通知他们被观察者发生了变化
    */
   public void notifyChanged() {
        synchronized(mObservers) {
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }
    }
    //代码省略
}

这个代码很简单,就是在mDataSetObservable.notifyChanged()中遍历所有的观察者,并且调用他的onChanged()方法,从而告知观察者发生了变化。
那么 这些观察者从哪里来的呢?其实这些观察者是就是ListView在setAdapter方法设置Adapter产生的,我们看相关代码:

    @Override
    public void setAdapter(ListAdapter adapter) {
        //如果已经有了一个Adapter,并且mDataSetObserver不为空
        if (mAdapter != null && mDataSetObserver != null) {
            //先注销该Adapter的观察者
            mAdapter.unregisterDataSetObserver(mDataSetObserver);
        }

        //代码省略
       
        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();
    }

从程序可以看到,在设置Adapter的时会构建一个AdapterDataSetObserver,这就是上面说的观察者,最后将这个观察者注册到了Adapter,这样我们的被观察者、观察者就都有了;
那么AdapterDataSetObserver是什么?它如何运作?那么现在来看看,AdapterDataObserver定义在ListView的父类AbsListView中,具体代码如下:

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

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

class AdapterDataSetObserver extends DataSetObserver {
        private Parcelable mInstanceState = null;

    //调用Adapter的notifyDataSetChanged的时会调用所有观察者的onChanged()方法,核心就是这里
      @Override
        public void onChanged() {
            mDataChanged = true;
            mOldItemCount = mItemCount;
            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();
            requestLayout();
        }

      //代码省略

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

到这里就知道了,当ListView的数据发生变化时,调用Adapter的notifyDataSetChanged(),这个方法又调用了DataSetObservable的notifyChanged()方法,这个方法会调用所有观察者AdapterDataSetObserver的onChanged(),在onChanged函数中又会调用ListView重新布局的函数和使得ListView刷新界面,这就是一个观察者模式;
现在再整理一下这个过程,AdapterView中又一个内部类AdapterDataSetObserver,在ListView设置Adapter时会构建一个AdapterDataSetObserver,并且注册到Adapter中,这就是一个观察者。而Adapter中有一个数据集被观察者DataSetObservable,在数据数量发生变更时,开发者手动调用Adapter.notifyDataSetChanged(),而notifyDataSetChanged()实际上是调用DataObservable的notifyChanged()方法,该方法会遍历所有的观察者的onChanged()。在AdapterDataSetObserver的onChanged()会获取Adpaer中数据集的新数量,然后调用ListView的requestLayout()方法重新进行布局,更新用户界面。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容

  • Android 架构师之路 目录 1、观察者模式概念 1.1 介绍 当对象间存在一对多关系时,则使用观察者模式(O...
    香沙小熊阅读 1,246评论 1 2
  • 一、观察者模式的定义 定义对象间一对多的依赖关系,使得当前对象改变了状态,则所有依赖于它的对象都会得到通知并自动更...
    sssssss_阅读 566评论 0 0
  • 参考 《设计模式:可复用面向对象软件的基础 》5.7 Observer 观察者 对象行为型模式 《设计模式解析》 ...
    WangGavin阅读 512评论 0 2
  • 1 定义 定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。...
    菜小轩526阅读 537评论 3 3
  • 有时候,我还是会想起若曦。她现在会是什么样子?一定还是那个样子。她就像是那种不会被岁月变老的女孩。 “我是妖精变的...
    又见光明阅读 648评论 1 2