Java行为型设计模式-观察者(一)

来源:互联网 发布:什么软件可以听黄歌 编辑:程序博客网 时间:2024/05/19 18:39

观察者

意图

观察者模式中有两种类型的对象存在:被观察者(发布-Publish),观察者(订阅者-Subscribe)。它们之间存在一(Publish)对一(Subscribe)或者一(Publish)对多(Subscribe)的关系。当Publish的状态发生变化的时候,所有依赖Publish的Subscribe都会接收到通知,并发生自动更新事件(Event)。

别名

依赖(Dependents),发布-订阅(Publish-Subscribe)。

动机

将一个系统分割成一系列相互协作(工作)的类有一个常见的副作用:需要维护相关对象间的一致性。我们不希望为了维护一致性而使各类紧密耦合(增大了类间的耦合性),因为这会导致类的可重用性降低。

适用性

在以下情景可以使用观察者模式

当一个抽象模型有两个方面,其中一个方面依赖另一个方面。将这二者封装在独立的对象中以使它们各自独立地改变和复用。

当一个对象的改变需要同时改变其他对象,而不知道具体有多少对象需要改变的时候。

当一个对象必须通知其他对象,而它又不能假定其他对象是谁。换而言之,你不希望这些对象紧密联系 。

参与者

这里写图片描述

Subject(目标,即被观察者)

  • 目标知道它观察者,可有任意多个观察者观察同一个目标。
  • 提供注册和删除观察者对象的接口

Observer(观察者)

  • 为那些在被观察者发生改变时需获得通知的对象定义一个更新接口。

ConcreteSubject(具体目标)

  • 将有关状态存入ConcreteObserver对象
  • 当它的状态发生改变时,向它的观察者发出通知

ConcreteObserver(具体观察者)

  • 维护一个指向ConcreteSubject对象的引用
  • 存储目标的状态,存储的状态必须与目标的状态具有一致性
  • 实现Observer更新接口以使自身状态与目标状态保持一致

实现

我们可以拿手机关机(publish)-当前程序保存数据(Subcribe)的事件来说明,手机状态是被观察者,当前开启的程序是观察者,当手机发出关机信号时,程序接收到信号广播开始在关机前保存现有数据。

Subject

/** * @author Created by MadJieJie on 2017/4/20-16:00. * @brief * @attention */public interface Subject{    /**     * Add Observer element.     * @param observer register element     */    void attach( Observer observer );    /**     * Remove Observer element.     * @param observer unregister element.     */    void detach( Observer observer );    /**     * Notify Observer's elements that subject's state has changed.     * @param message send message     */    void notify(String message);}

ConcreteSubject

import java.util.ArrayList;import java.util.List;/** * @author Created by MadJieJie on 2017/4/20-16:00. * @brief * @attention */public class ConcreteSubject implements Subject{    private List<Observer> mProgramList = new ArrayList<>();    @Override    public void attach ( Observer observer )    {        mProgramList.add(observer);    }    @Override    public void detach ( Observer observer )    {        mProgramList.remove(observer);    }    @Override    public void notify ( String message )    {        for(Observer observer: mProgramList )        {            observer.saveData(message);        }    }}

Observer

/** * @author Created by MadJieJie on 2017/4/20-16:00. * @brief * @attention */public interface Observer{    /**     * Save data before cellphone close.     */    void saveData(String message);}

ConcreteObserver

import java.util.ArrayList;import java.util.List;/** * @author Created by MadJieJie on 2017/4/20-16:00. * @brief * @attention */public class ConcreteSubject implements Subject{    private List<Observer> mProgramList = new ArrayList<>();    @Override    public void attach ( Observer observer )    {        mProgramList.add(observer);    }    @Override    public void detach ( Observer observer )    {        mProgramList.remove(observer);    }    @Override    public void notify ( String message )    {        for(Observer observer: mProgramList )        {            observer.saveData(message);        }    }}

协作

这里写图片描述

1.得到Subject的对象,然后Subject对象调用attach方法分别赋入Observer对象qqObserver、weiXinObserver使它们订阅到被观察者。

2.被观察者Subject对象发出Notify(“close”)事件,分别发送给它们的订阅者们,订阅者都接受到事件,进行更新方法-saveData().

Android中的Observer

Android中大量使用了观察者模式,像许多点击事件OnClickListener、OnItemClickListener、android.database.Observable等;还有第三方开源库EventBus、RxJava、RxAndroid都是基于观察者模式设计的。下面我将拿BaseAdapter的中的notifyDataSetChanged()方法来做例子

BaseAdapter明显是被观察者(Observable),实现了被观察者的方法-注册、注销和通知。

BaseAdapter

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {    private final DataSetObservable mDataSetObservable = new DataSetObservable();    //register        public void registerDataSetObserver(DataSetObserver observer) {        mDataSetObservable.registerObserver(observer);    }    //unregister    public void unregisterDataSetObserver(DataSetObserver observer) {        mDataSetObservable.unregisterObserver(observer);    }    /**     * Notifies the attached observers that the underlying data has been changed     * and any View reflecting the data set should refresh itself.     */    public void notifyDataSetChanged() {        mDataSetObservable.notifyChanged();    }    ......  }

进入查看mDataSetObservable.notifyChanged()的实现,它是使用同步锁锁住一个ArrayList对象,并对它的元素遍历实现onChanged()方法。

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();            }        }    }    }

再进入它的该类的父类看到了具体被观察者(ConcreteObservable)

Observable

public abstract class Observable<T> {    /**     * The list of observers.  An observer can be in the list at most     * once and will never be null.     */    protected final ArrayList<T> mObservers = new ArrayList<T>();    /**     * Adds an observer to the list. The observer cannot be null and it must not already     * be registered.     * @param observer the observer to register     * @throws IllegalArgumentException the observer is null     * @throws IllegalStateException the observer is already registered     */    public void registerObserver(T observer) {        if (observer == null) {            throw new IllegalArgumentException("The observer is null.");        }        synchronized(mObservers) {            if (mObservers.contains(observer)) {                throw new IllegalStateException("Observer " + observer + " is already registered.");            }            mObservers.add(observer);        }    }    /**     * Removes a previously registered observer. The observer must not be null and it     * must already have been registered.     * @param observer the observer to unregister     * @throws IllegalArgumentException the observer is null     * @throws IllegalStateException the observer is not yet registered     */    public void unregisterObserver(T observer) {        if (observer == null) {            throw new IllegalArgumentException("The observer is null.");        }        synchronized(mObservers) {            int index = mObservers.indexOf(observer);            if (index == -1) {                throw new IllegalStateException("Observer " + observer + " was not registered.");            }            mObservers.remove(index);        }    }    /**     * Remove all registered observers.     */    public void unregisterAll() {        synchronized(mObservers) {            mObservers.clear();        }    }}

ListView中setAdapter方法中诠释了注册的流程-》

1.先判断mAdapter&mDataSetObserver 不为空后,注销之前的观察者mAdapter.unregisterDataSetObserver(mDataSetObserver);

2.然后判断mAdapter不为NULL后创建新的观察者mDataSetObserver = new AdapterDataSetObserver();

3.注册新的观察者mAdapter.registerDataSetObserver(mDataSetObserver)。

setAdapter

    /**     * Sets the data behind this ListView.     *     * The adapter passed to this method may be wrapped by a {@link WrapperListAdapter},     * depending on the ListView features currently in use. For instance, adding     * headers and/or footers will cause the adapter to be wrapped.     *     * @param adapter The ListAdapter which is responsible for maintaining the     *        data backing this list and for producing a view to represent an     *        item in that data set.     *     * @see #getAdapter()      */    @Override    public void setAdapter(ListAdapter adapter) {        if (mAdapter != null && mDataSetObserver != null) {            mAdapter.unregisterDataSetObserver(mDataSetObserver);//Unregister Observer        }        .....        // 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();//Create Observer object.             mAdapter.registerDataSetObserver(mDataSetObserver);//Register Observer.           .....           }        requestLayout(); //Request layout again.    }

然后我们进入观察者(Observer)类AdapterDataSetObserver中看具体实现的更新方法onChanged();暂时看不到什么,但它调用了父类的super.onChanged(),进父类看

onChanged()

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

进入父类我们看到了具体的观察者(ConcreteObserver),也看到了更新方法更为具体的实现,当数据源发生变化的时候,我们调用Adapter的notifyDataSetChanged()会导致数据重新填充入View&调用requestLayout()方法布局重构

super.onChanged()

class AdapterDataSetObserver extends DataSetObserver {        private Parcelable mInstanceState = null;        @Override        public void onChanged() {            mDataChanged = true;            mOldItemCount = mItemCount;            mItemCount = getAdapter().getCount();            if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null                    && mOldItemCount == 0 && mItemCount > 0) {                AdapterView.this.onRestoreInstanceState(mInstanceState);                mInstanceState = null;            } else {                rememberSyncState();            }            checkFocus();            requestLayout();//Request layout again.        }        ...        public void clearSavedState() {            mInstanceState = null;        }    }

效果

1.被观察者和观察者之间的抽象耦合

一个被观察者拥有一系列的观察者,每个观察者都符合Observer类的简单接口,被观察者自身高内聚,与其他观察者无过多的耦合,这样它们在系统中可以属于不同的层次,一个较低层次的被观察者可以与处于较高层次的观察者通信并通知它,这样保持了系统层次的完整。

2.支持广播通信

被观察者对象不需要关心有多少观察者,它只需要向登记的观察者发送通知

3.过度耦合

由于被观察者和观察者的高度耦合,一个观察者并不知道其他观察者存在,若一个观察者对被观察者进行改变,则会影响所有依赖它的观察者,并且这种错误难以追踪,故观察者最好不要改变被观察者。由于程序我们习以为常顺序执行,多个观察者会导致我们开发和调试难度增大,而一个被观察者的卡顿会影响整体的效率,我们一般采用异步解决

源码下载

1 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 云课堂密码忘记手机号更改了怎么办 孩子做事才要奖励怎么办的教案 员工做事效率低家庭情况不好怎么办 安全云课堂人脸识别失败怎么办 酷我k歌里音乐太少怎么办 酷狗里面的歌没有了怎么办 第一试用网登录密码忘记了怎么办 第一试用网登录密码忘了怎么办 微博登录直播间帐号被锁定怎么办 硫酸不小心弄到皮肤上怎么办 小米平板进水了开不了机怎么办 U盘被弹出后再插没有显示怎么办 视频从U盘传下来不能播放怎么办 微博账号忘了登录名怎么办 迅雷应版权方要求无法下载怎么办? 微信账号忘记密码上面有钱怎么办 蘑菇街账号忘记了里面有钱怎么办 手机注册的百度云帐号换号怎么办 网易云音乐sd卡写入失败怎么办? 玩客云涌u盘满了怎么办 手机安装杜比音效失败死机该怎么办 网易云音乐下载额度用完了怎么办 苹果手机付费app连续扣费怎么办 oppoO粉卡的流量用没了怎么办 qq邮箱里的文件过期了怎么办 163邮箱里的文件过期了怎么办 做人事的给员工漏交社保了怎么办 公司漏交了员工一个月的社保怎么办 小米4c能充电不能开机怎么办 小米手机关机开不开机该怎么办 小米5s关机开不开机怎么办 老板跑路了员工社保还挂着怎么办 红米5连wifi很慢怎么办 电脑百度网盘下载速度慢怎么办 新手机与旧手机同步了怎么办 百度网盘2t满了怎么办 魅族手机相册的密码忘了怎么办 手机百度网盘本地空间不足怎么办 百度网盘保存视频内存不够怎么办 别人的百度网盘链接打不开怎么办 百度网盘视频插件升级失败怎么办