美文网首页
观察者模式

观察者模式

作者: 风月寒 | 来源:发表于2021-06-07 19:35 被阅读0次
概念

对象间的一种一个对多的依赖关系,当一个对象的状态发送改变时,所以依赖于它的对象都得到通知并被自动更新。

下面来看UML图,

1622601092(1).png

Subject(抽象主题):又叫抽象被观察者(Observable),把所有观察者对象的引用保存到一个集合里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。

ConcreteSubject(具体主题):又叫具体被观察者,将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。

Observer (抽象观察者):为所有的具体观察者定义一个接口,在得到主题通知时更新自己。

ConcrereObserver(具体观察者):实现抽象观察者定义的更新接口,当得到主题更改通知时更新自身的状态。

具体使用

介绍完其原理,我们来引入一个场景:

在两个类中,如果一个类的变化需要在另一个类中进行一些状态的更新,有哪些方法可以实现?

1、回调

2、让另一个类直接持有该类的对象

3、观察者模式

回调

用高德地图获取位置为例:

首先我们创建一个接口,

public interface OnMapLocationListener {
    void onLocation(AMapLocation amapLocation);
}

当我们获取到定位之后,

@Override
    public void onLocationChanged(AMapLocation aMapLocation) {
        if (aMapLocation != null && listener != null){
            listener.onLocation(aMapLocation);
        }
    }

然后我们可以在activity中直接采用匿名内部类的方式去得到这个回调。

GaoDeMapUtil.getInstance().setListener(new OnMapLocationListener() {
            @Override
            public void onLocation(AMapLocation amapLocation) {
                if (amapLocation != null) {
                    if (amapLocation.getErrorCode() == 0) {
                        //定位成功回调信息,设置相关消息
                        //tvLocation.setText(amapLocation.getCity());
                        set.setLocation(amapLocation.getCity());
                    } else {
                        //显示错误信息ErrCode是错误码,errInfo是错误信息,详见错误码表。
                        Log.e("AmapError", "location Error, ErrCode:"
                                + amapLocation.getErrorCode() + ", errInfo:"
                                + amapLocation.getErrorInfo());
                    }
                }
            }
        });

可以看到,这种方式十分简单,分三步就好,但是有一个缺点,如果回调嵌套过多,则会阅读的时候十分不便,比如Glide中有四层回调嵌套,看源码的时候痛不欲生。

让另一个类直接持有该类的对象

直接在该类中持有另一个类的对象,那样就可以直接得到另一个类的方法进行操作,但是这种方式耦合太高,不符合高内聚低耦合这一性质。

观察者模式

讲解观察者模式我们是结合RecyclerView 的源码来讲解。

在recyclerView中,当我们数据改变的时候我们会调用notifyDataSetChanged()、notifyItemChanged()、notifyItemRangeChanged()等方法去刷新,下面我们进入notifyDataSetChanged()中。

public final void notifyDataSetChanged() {
    mObservable.notifyChanged();
}

我们可以看到调用的是mObservable的notifyChanged(),这里的mObservable就是被观察者。

public void notifyChanged() {
    for (int i = mObservers.size() - 1; i >= 0; i--) {
        mObservers.get(i).onChanged();
    }
}

protected final ArrayList<T> mObservers = new ArrayList<T>();

其中mObservers是 一个ArrayList.T则是我们的观察者observer,notifyChanged()中则是遍历观察者,然后调用observer的onChanged()。这个稍后分析。

既然是将观察者加入到一个list中,那什么时候加入?点击mObservers可以进入public abstract class Observable<T>类中可以看到,在这里面有两个重要的方法。在registerObserver()中将observer添加进去,在unregisterObserver()将observer移除。

Observable<T>是一个抽象类,其具体实现类是:AdapterDataObservable()然后早Adapter中持有AdapterDataObservable对象,

public void registerAdapterDataObserver(@NonNull AdapterDataObserver observer) {
     mObservable.registerObserver(observer);
}

       
public void unregisterAdapterDataObserver(@NonNull AdapterDataObserver observer) {
     mObservable.unregisterObserver(observer);
}

那我们可以假设一下,当我们在设置adapter的时候,会将观察者加入到集合中,下面来验证下。

 public void setAdapter(@Nullable Adapter adapter) {
        // bail out if layout is frozen
        setLayoutFrozen(false);
        setAdapterInternal(adapter, false, true);
        processDataSetCompletelyChanged(false);
        requestLayout();
    }
    
private void setAdapterInternal(@Nullable Adapter adapter, boolean compatibleWithPrevious,
            boolean removeAndRecycleViews) {
        if (mAdapter != null) {
            mAdapter.unregisterAdapterDataObserver(mObserver);
            mAdapter.onDetachedFromRecyclerView(this);
        }
        
        final Adapter oldAdapter = mAdapter;
        mAdapter = adapter;
        if (adapter != null) {
            adapter.registerAdapterDataObserver(mObserver);
            adapter.onAttachedToRecyclerView(this);
        }
    ......
    }

可以看到,setAdapter中,会调用setAdapterInternal(),在setAdapterInternal会先判断mAdapter是否不为null的时候,先移除,然后将我们赋值的adapter重新进行赋值,并进行注册添加到集合中。

private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();

private class RecyclerViewDataObserver extends AdapterDataObserver {
        RecyclerViewDataObserver() {
        }

        @Override
        public void onChanged() {
            assertNotInLayoutOrScroll(null);
            mState.mStructureChanged = true;

            processDataSetCompletelyChanged(true);
            if (!mAdapterHelper.hasPendingUpdates()) {
                requestLayout();
            }
        }
        ......
    }

mObserver的具体实现类是RecyclerViewDataObserver,上面我们分析到notifyChanged()中则是遍历观察者,然后调用observer的onChanged(),其实就是调用RecyclerViewDataObserver的onChanged()。在onChanged中,会调用requestLayout()进行重绘。

所以整个流程就是,在我们setAdapter的时候,会进行注册将观察者加入到集合中,然后我们数据改变的时候,调用notifyDataSetChanged,则会进行对观察者进行遍历,然后调用onChanged方法,里面会调用RequestLayout(),进行重新测量,摆放和重绘。

相关文章

  • 11.9设计模式-观察者模式-详解

    设计模式-观察者模式 观察者模式详解 观察者模式在android中的实际运用 1.观察者模式详解 2.观察者模式在...

  • RxJava基础—观察者模式

    设计模式-观察者模式 观察者模式:观察者模式(有时又被称为发布(publish )-订阅(Subscribe)模式...

  • 前端面试考点之手写系列

    1、观察者模式 观察者模式(基于发布订阅模式) 有观察者,也有被观察者。 观察者需要放到被观察者列表中,被观察者的...

  • RxJava 原理篇

    一、框架思想 观察者模式观察者自下而上注入被观察者被观察者自上而下发射事件观察者模式 装饰器模式自上而下,被观察者...

  • 观察者模式

    观察者模式概念 观察者模式是对象的行为模式,又叫作发布-订阅(publish/subscrible)模式。 观察者...

  • 设计模式-观察者模式

    观察者模式介绍 观察者模式定义 观察者模式(又被称为发布-订阅(Publish/Subscribe)模式,属于行为...

  • 观察者模式

    观察者模式 观察者模式的定义 观察者模式(Observer Pattern)也叫做发布订阅模式(Publish/s...

  • iOS设计模式之观察者模式

    观察者模式 1、什么是观察者模式 观察者模式有时又被称为发布(publish)-订阅(Subscribe)模式、模...

  • 观察者模式和发布订阅模式区别

    观察者模式 所谓观察者模式,其实就是为了实现松耦合(loosely coupled)。 在观察者模式中,观察者需要...

  • RxJava(二)

    一、观察者模式 1.1、传统的观察者模式 1.2、RxJava 的观察者模式 区别传统的观察者模式是一个 Obse...

网友评论

      本文标题:观察者模式

      本文链接:https://www.haomeiwen.com/subject/qyqosltx.html