概念对象时期的一种一对多依赖关系,当那几个群众号更新时就会通报那么些订阅的微信用户

定义

观看者形式(Observer
Pattern):定义对象时期的一种一对多依赖关系,使得每当一个目标景况爆发变动时,其连带信赖对象皆得到关照并被自动更新

具体世界的情势

例如一些用户订阅了周刊,每一趟周刊公布的时候都会送到用户手上

比如说高考的时候,发送广播布告考试完成,考生都要停笔,教授须求收卷

譬如打仗的时候,指挥官发命令,战场上的新兵跟军命令决定进攻、撤退、驻守

有这几种类型

  • 公布-订阅情势
  • 模型-视图方式
  • 源-监听方式
  • 从属者形式

1.观看者方式形式简介

不难设计

目标

被观察的对象,内置抽象阅览者集合,定义增添、删除、通告抽象寓目者的不二法门。目的类可以是空虚的也得以是现实的。若是有具体的架空业务要促成,还足以分出目的子类

空泛观看者

申明了立异数据的章程,由目的调用

现实观看者

贯彻了抽象观察者的立异数据的格局

即使急需目的的有些情形或者数额状态,可能还保持着对目的的引用

图片 1

观看者格局-类图.png

可以选用在一对多的通讯上。同时,目的和观看者没有太强的正视性和事关,扩展或者减小观望者,不会对目的导致影响

定义

观察者方式(又被喻为公布-订阅(Publish/Subscribe)情势,属于行为型情势的一种,它定义了一种一对多的重视性关系,让多少个观看者对象同时监听某一个大旨对象。那几个宗旨对象在状态变化时,会打招呼所有的观看者对象,使她们力所能及自动更新自己。

应用实例

观看者格局结构图

图片 2

  • Subject:抽象焦点(抽象被观看者),抽象大旨角色把具有观望者对象保存在一个汇合里,每个主旨都得以有擅自数量的观望者,抽象主旨提供一个接口,可以扩大和删除观看者对象。
  • ConcreteSubject:具体宗旨(具体被阅览者),该角色将关于情形存入具体观察者对象,在具体主旨的中间景况暴发变动时,给所有注册过的寓目者发送公告。
  • Observer:抽象观望者,是观望者者的抽象类,它定义了一个翻新接口,使得在获取焦点更改文告时更新自己。
  • ConcrereObserver:具体观看者,是贯彻抽象观看者定义的翻新接口,以便在赢得宗旨更改布告时更新自己的事态。

HTTP DNS 解析性能监控

有那样一个场馆,大家的运用接入了 HTTP DNS,接管了少数接口请求的 DNS
解析。现在某些有总括报告接口,在少数地点,要求 DNS
解析的有些音信,比如解析到的 IP,解析耗时,解析选拔的域名服务器地址

我们可以动用观望者来缓解这些题材

创制一个监控器,内置观望者队列,并提供形式来增加、删除观望者

当 DNS
每一遍发起两遍解析,把多少计算后,交给监控器。然后监控文告所有寓目者拿多少

概念目的类 DnsMonitor

public class DnsMonitor {

    private final List<MonitorWatcher> mWatcherList;

    public DnsMonitor() {
        mWatcherList = new CopyOnWriteArrayList<>();
    }

    @Override
    public void onParseResult(ResolveData data) {
        if (data == null || TextUtils.isEmpty(data.host)) {
            return;
        }

        // 通知观察者们,有解析数据了
        for (MonitorWatcher watcher : mWatcherList) {
            watcher.notifyResolveData(data);
        }
    }

    /**
     * 注册监视者
     */
    public void registerWatcher(MonitorWatcher watcher) {
        mWatcherList.add(watcher);
    }

    /**
     * 注销监视者
     */
    public void unregisterWatcher(MonitorWatcher watcher) {
        mWatcherList.remove(watcher);
    }
}

概念观看者

public interface MonitorWatcher {
    /**
     * 通知获取到的解析数据
     */
    void notifyResolveData(ResolveData data);
}

然后,我们的具备 DNS 解析器,在分析到结果后,调用的 onParseResult
把多少暴发去

可以把 Monitor
做成单例,或者放置单例内,那样就可以全方位经过都得以访问。所有的观察者们,只必要完结Monitor沃·特(W·at)cher,然后等着多少通告过来

实际那几个成效还有众多细节,比如怎么样幸免唤醒观望者不打断,还有哪些让具有解析器使用同一个监视器。因为和这么些形式毫无干系,就不列出来了

2.观望者情势大致完成

观望者模式那种公布-订阅的格局大家可以拿微信公众号来比喻,假如微信用户就是观看者,微信公众号是被寓目者,有多个的微信用户关心了程序猿这几个公众号,当这几个群众号更新时就会通报那几个订阅的微信用户。好了俺们来看看用代码怎么着促成:

ContentObserver

有那般一个须要,大家想要知道数据库某个数据的变更,有一种结果方法,那就是开个工作线程,去轮询访问。那样的做法会导致资源多量消耗

Android 提供的一种格局,称为内容观察者,可以监听数据库变化后会布告出来

譬如大家要监听显示屏亮度的变通,并且做一些事情。显示器亮度变化的多寡在系统数据库里,大家可以通过
ContentObserver 很轻松地取出

屏幕亮度对应的 Uri 可以这么拿到

Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS)

创立观望者

    private static class ScreenBrightnessObserver extends ContentObserver {

        ScreenBrightnessObserver(@NonNull Handler handler) {
            super(handler);
        }

        @Override
        public void onChange(boolean selfChange) {
            super.onChange(selfChange);

            // 亮度发生变化了,可以查询最新的亮度,然后做相应的业务

        }
    }

接下来在页面启动的施用登记

mScreenBrightnessObserver = new ScreenBrightnessObserver(new Handler());
getContentResolver().registerContentObserver(Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS), true, mScreenBrightnessObserver);

在页面销毁的时候注销

getContentResolver().unregisterContentObserver(mScreenBrightnessObserver);

空洞寓目者(Observer)

个中定义了一个革新的办法:

public interface Observer {
    public void update(String message);
}

EventBus

伊芙(Eve)ntBus 也是观望者形式的一种落成

比方大家有七个目的,多个观望者,假若每个目的都去管理观察者列表的话,维护起来很可怕

故此那边又抽象出一个层次,可以精通为音信主题,或者音信总线,内部维护着观看者的列表,目的发出多少变动交给这些音讯宗旨开展音信的分发和调度

图片 3

观看者格局-伊夫ntBus.png

EventBus
提供了一个这个有力新闻总线,目的暴发变化的时候,把要通报的音讯封装成一个个音讯。把音信文告给订阅该音信的观望者们

可以用 伊芙(Eve)ntBus
在做模块间的通讯。把要通报的数据变动封装成事件抛出去。通讯的模块没有耦合,发送者不要求知道有啥接收者。伊芙(Eve)ntBus
会通知到位

实际观望者(ConcrereObserver)

微信用户是寓目者,里面已毕了革新的法子:

public class WeixinUser implements Observer {
    // 微信用户名
    private String name;
    public WeixinUser(String name) {
        this.name = name;
    }
    @Override
    public void update(String message) {
        System.out.println(name + "-" + message);
    }


}

抽象被观望者(Subject)

架空主旨,提供了attach、detach、notify三个措施:

public interface Subject {
    /**
     * 增加订阅者
     * @param observer
     */
    public void attach(Observer observer);
    /**
     * 删除订阅者
     * @param observer
     */
    public void detach(Observer observer);
    /**
     * 通知订阅者更新消息
     */
    public void notify(String message);
}

切切实实被寓目者(ConcreteSubject)

微信公众号是有血有肉宗旨(具体被观望者),里面储存了订阅该群众号的微信用户,并落到实处了抽象宗旨中的方法:

public class SubscriptionSubject implements Subject {
    //储存订阅公众号的微信用户
    private List<Observer> weixinUserlist = new ArrayList<Observer>();

    @Override
    public void attach(Observer observer) {
        weixinUserlist.add(observer);
    }

    @Override
    public void detach(Observer observer) {
        weixinUserlist.remove(observer);
    }

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

客户端调用

public class Client {
    public static void main(String[] args) {
        SubscriptionSubject mSubscriptionSubject=new SubscriptionSubject();
        //创建微信用户
        WeixinUser user1=new WeixinUser("杨影枫");
        WeixinUser user2=new WeixinUser("月眉儿");
        WeixinUser user3=new WeixinUser("紫轩");
        //订阅公众号
        mSubscriptionSubject.attach(user1);
        mSubscriptionSubject.attach(user2);
        mSubscriptionSubject.attach(user3);
        //公众号更新发出消息给订阅的微信用户
        mSubscriptionSubject.notify("刘望舒的专栏更新了");
    }
}

结果

杨影枫-刘望舒的专栏更新了
月眉儿-刘望舒的专栏更新了
紫轩-刘望舒的专栏更新了

3.用到观察者格局的场景和优缺点

使用处境

  • 关系行为现象,要求专注的是,关联行为是可拆分的,而不是“组合”关系。
  • 事件多级触发场景。
  • 跨系统的新闻交流场景,如新闻队列、事件总线的处理机制。

优点

消除耦合,让耦合的两岸都信赖于肤浅,从而使得个其余更换都不会影响另一头的变换。

缺点

在动用阅览者情势时必要考虑一下开发成效和周转作用的题目,程序中包蕴一个被观望者、三个观看者,开发、调试等内容会比较复杂,而且在Java中音讯的通报一般是种种执行,那么一个寓目者卡顿,会影响全体的实践作用,在这种场所下,一般会利用异步已毕。

4.Android中的阅览者格局

android源码中也有无数应用了观察者情势,比如OnClickListener、ContentObserver、android.database.Observable等;还有组件通信库RxJava、RxAndroid、伊夫(Eve)ntBus;在那里将拿大家最常用的Adapter的notifyDataSetChanged()方法来比喻:
当大家用ListView的时候,数据暴发变化的时候大家都会调用Adapter的notifyDataSetChanged()方法,那几个情势定义在Base艾达(Ada)per中,我们来探望Base艾达(Ada)per的部分源码:

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

很醒目BaseAdapter用的是观看者方式,BaseAdapter是切实被观望者,接下去看看mDataSetObservable.notifyChanged():

public class DataSetObservable extends Observable<DataSetObserver> {
    public void notifyChanged() {
        synchronized(mObservers) {
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }
    }

大家来看了mObservers,那就是观看者的集合,那一个观察者是在ListView通过set艾达per()设置Adaper时暴发的:

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

        if (mAdapter != null) {
            mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
            mOldItemCount = mItemCount;
            mItemCount = mAdapter.getCount();
            checkFocus();
            //创建数据观察者
            mDataSetObserver = new AdapterDataSetObserver();
            //注册观察者
            mAdapter.registerDataSetObserver(mDataSetObserver);

            ...
        }

接下去看看观望者艾达(Ada)pterDataSetObserver中拍卖了如何:

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

从地点的代码看不出什么,再看看艾达pterDataSetObserver的父类AdapterView的AdapterDataSetObserver:

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

        ...

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

我们看看在onChanged()方法中调用了requestLayout()方法来重新开展布局。好了,看到那里大家都领悟了,当ListView的多寡暴发变化时,我们调用Adapter的notifyDataSetChanged()方法,那一个方法又会调用观察者们(艾达pterDataSetObserver)的onChanged()方法,onChanged()方法又会调用requestLayout()方法来再一次开展布局。

相关文章