设计模式之三:观察者模式

简介:
  • 观察者模式
  • 目录介绍
  • 1.观察者模式介绍
  • 2.观察者使用场景
  • 3.观察者UML图解
  • 4.观察者模式简单实现
  • 4.0 举个例子
  • 4.1 观察者代码
  • 4.2 被观察者代码
  • 4.3 测试代码
  • 4.4 思考
  • 5.观察者模式Android源码分析
  • 5.1 先来看看源代码
  • 5.2 观察者从哪里来的,查看setAdapter源代码
  • 5.3 观察者在哪里创建的呢?如何运作
  • 5.4 代码分析
  • 6.观察者模式深入探索
  • 7.EventBus事件总线
  • 7.1 遇到的问题
  • 7.2 目前流行事件总线框架
  • 7.3 源码解读,单独写成博客呢
  • 7.4 使用步骤
  • 8.其他说明
  • 8.1 参考文档:源码设计模式解析

0.本人写的综合案例

1.观察者模式介绍

  • 1.1 最常用的地方是GUI系统、订阅——发布系统等
  • 1.2 重要作用就是解耦,使得它们之间的依赖性更小,甚至做到毫无依赖
  • 1.3 观察者模式又被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
  • 1.4 举个例子:
    就拿csdn这个类似于博客的网站来说吧,如果你订阅了或者说关注了一个领域,就能收到这个领域文章的推送,如果没有关注,则不能。是相当于有一个总控制台(被观察者,持有数据源,这里的数据源是我们每个订阅了的人)通知下面的观察者。

2.观察者使用场景

  • 事件多级触发场景
  • 跨系统的消息交换场景,如消息队列,事件总线的处理机制

3.观察者UML图解

  • 3.1 关于UML类图
    Image.png
  • 3.2 角色介绍

    • 抽象主题(Subject):它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
    • 具体主题(ConcreteSubject):将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。
    • 抽象观察者(Observer):为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
    • 具体观察者(ConcreteObserver):实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调。
  • 3.3 其他说明

    • 1.Subject 和 Observer 是一个一对多的关系,也就是说观察者只要实现 Observer 接口并把自己注册到 Subject 中就能够接收到消息事件;
    • 2.Java API有内置的观察者模式类:java.util.Observable 类和 java.util.Observer 接口,这分别对应着 Subject 和 Observer 的角色;
    • 3.使用 Java API 的观察者模式类,需要注意的是被观察者在调用 notifyObservers() 函数通知观察者之前一定要调用 setChanged() 函数,要不然观察者无法接到通知;
    • 4.使用 Java API 的缺点也很明显,由于 Observable 是一个类,java 只允许单继承的缺点就导致你如果同时想要获取另一个父类的属性时,你只能选择适配器模式或者是内部类的方式,而且由于 setChanged() 函数为 protected 属性,所以你除非继承 Observable 类,否则你根本无法使用该类的属性,这也违背了设计模式的原则:多用组合,少用继承。

4.观察者模式简单实现

  • 4.0 举个例子

    • 就拿csdn这个类似于博客的网站来说吧,如果你订阅了或者说关注了一个领域,就能收到这个领域文章的推送,如果没有关注,则不能。是相当于有一个总控制台(被观察者,持有数据源,这里的数据源是我们每个订阅了的人)通知下面的观察者。
  • 4.1 观察者代码

    • 观察者,也就是你【程序员】,订阅专题的人
public class MeObserver implements Observer {

    private String yourName;
    public MeObserver(String yourName){
        this.yourName=yourName;
    }
    @Override
    public void update(Observable o, Object arg) {
        System.out.println("你订阅的"+arg.toString()+"更新了。");
    }
    @Override
    public String toString() {
        return "your name "+yourName;
    }
}
  • 4.2 被观察者代码

    • 你订阅的简书android领域。被观察者:当他有更新时,所有的观察者都会接收到响应的通知
public class MeUser extends Observable {

    private String name;
    private int age;
    private String sex;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        setChanged();
        notifyObservers();
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
        setChanged();
        notifyObservers();
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
        //setChanged();告知数据改变,通过notifyObservers();发送信号通知观察者。
        setChanged();
        notifyObservers();
    }

    @Override
    public String toString() {
        return "MeUser [name=" + name + ", age=" + age + ", sex=" + sex + "]";
    }
}
  • 4.3 测试代码

    • 相当于服务器更新通知
MeUser user=new MeUser();
MeObserver coder1=new MeObserver("name1");
MeObserver coder2=new MeObserver("name2");
MeObserver coder3=new MeObserver("name3");
user.addObserver(coder1);
user.addObserver(coder2);
user.addObserver(coder3);
user.postNewContentToCoder("contentChanged");
  • 4.4 思考:为什么观察者是实现一个observer接口,而被观察者是继承一个抽象类呢

    • 被观察者写成抽象类的原因是复用,观察者写成接口的原因是降低代码的耦合度,面向接口编程,在原则里就是依赖倒置原则,我们倒着思考,如果这里不是接口,而是一个具体的类,那么,耦合度就相当高了,如果不是观察者注册就无法添加到observable里,就要修改observable的代码

5.观察者模式Android源码分析

  • RecycleView是Android中重要控件,其中adapter刷新数据的adapter.notifyDataSetChanged()就用到了观察者模式
  • 5.1 先来看看源代码
/*
 * Notify any registered observers that the data set has changed.
 * 通知已登记的数据集已更改的任何观察者。
 *
 * <p>This event does not specify what about the data set has changed, forcing
 * any observers to assume that all existing items and structure may no longer be valid.
 * LayoutManagers will be forced to fully rebind and relayout all visible views.</p>
 * 此事件没有指定数据集发生了什么变化,迫使任何观察者假设所有现有的项和结构可能不再有效。
 * LayoutManagers将不得不完全重新绑定和保护所有可见的视图
 */
public final void notifyDataSetChanged() {
    mObservable.notifyChanged();
}
//然后调用此方法
public void notifyChanged() {
    // 遍历所有观察者,并且调用它们的onChanged方法
    for (int i = mObservers.size() - 1; i >= 0; i--) {
         mObservers.get(i).onChanged();
    }
}
  • 5.2 观察者从哪里来的,查看setAdapter源代码
public void setAdapter(Adapter adapter) {
    // bail out if layout is frozen
    setLayoutFrozen(false);
    setAdapterInternal(adapter, false, true);
    requestLayout();
}
private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,boolean removeAndRecycleViews) {
    //如果有adapter,那么先注销对应观察者
    if (mAdapter != null) {
        mAdapter.unregisterAdapterDataObserver(mObserver);
        mAdapter.onDetachedFromRecyclerView(this);
    }

    if (!compatibleWithPrevious || removeAndRecycleViews) {
        removeAndRecycleViews();
    }
    //重置
    mAdapterHelper.reset();
    final Adapter oldAdapter = mAdapter;
    mAdapter = adapter;
    if (adapter != null) {//将观察者注册到adapter中
        adapter.registerAdapterDataObserver(mObserver);
        adapter.onAttachedToRecyclerView(this);
    }
    if (mLayout != null) {
        mLayout.onAdapterChanged(oldAdapter, mAdapter);
    }

    mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
    mState.mStructureChanged = true;
    markKnownViewsInvalid();
}
  • **5.3 观察者在哪里创建的呢?如何运作
//查看源代码,可以知道
private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
//查看onchang方法
private class RecyclerViewDataObserver extends AdapterDataObserver {
    @Override
    public void onChanged() {
        assertNotInLayoutOrScroll(null);
        mState.mStructureChanged = true;
        setDataSetChangedAfterLayout();
        if (!mAdapterHelper.hasPendingUpdates()) {
            requestLayout();
        }
    }
}
void setDataSetChangedAfterLayout() {
    if (mDataSetHasChangedAfterLayout) {
        return;
    }
    mDataSetHasChangedAfterLayout = true;
    //获取adapter中数据的数量
    final int childCount = mChildHelper.getUnfilteredChildCount();
    for (int i = 0; i < childCount; i++) {
        final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
        if (holder != null && !holder.shouldIgnore()) {
  holder.addFlags(ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
        }
    }

    mRecycler.setAdapterPositionsAsUnknown();
    // immediately mark all views as invalid, so prefetched views can be
    // differentiated from views bound to previous data set - both in children, and cache
    markKnownViewsInvalid();
}
  • 5.4 代码分析
  • 在Adapter里面有一个AdapterDataObservable,是被观察者,被观察者必须有三个方法,注册,销毁,通知,这里的注册就是registerAdapterDataObserver,通知就是notify相关的。
  • 在setAdapter的时候,将观察者,也就是RecyclerViewDataObserver注册到AdapterDataObservable里面来维护,观察者里面自然是更新布局。
  • 我们调用notifyDataSetChanged其实就是调用被观察者的notify相关方法

6.观察者模式深入探索

7.EventBus事件总线

  • 7.1 遇到的问题

    • 在Activity与Fragment中通信,需要有对方的引用,会导致耦合性较高。怎么办呢?
    • 想在Activity-B中回调Activity-A的某个函数,但是Activity又不能手动创建对象设置一个listener。该怎么办呢?
    • 如何在service中更新Activity或者fragment的界面呢???
  • 7.2 目前流行事件总线框架

    • EventBus 是异步执行 使用name pattern模式,效率高,但使用不方便
    • Otto 订阅函数不是异步执行 使用注解,使用方便,但效率比不上EventBus
    • AndroidEventBus 是异步执行 订阅函数支持tag,使得事件投递更准确
  • 7.3 源码解读

    • 可以直接看EventBus源码解析文章
  • 7.4 使用步骤

    • 可以直接看EventBus源码解析文章

其他说明

目录
相关文章
|
3月前
|
设计模式 监控 安全
设计模式 | 观察者模式
设计模式 | 观察者模式
18 0
|
3月前
|
设计模式 前端开发 数据中心
设计模式之观察者模式
设计模式之观察者模式
|
4月前
|
设计模式 存储 Java
认真学习设计模式之观察者模式(Observer Pattern)
认真学习设计模式之观察者模式(Observer Pattern)
29 0
|
2月前
|
设计模式 前端开发 JavaScript
观察者模式 vs 发布-订阅模式:两种设计模式的对决!
欢迎来到前端入门之旅!这个专栏是为那些对Web开发感兴趣、刚刚开始学习前端的读者们打造的。无论你是初学者还是有一些基础的开发者,我们都会在这里为你提供一个系统而又亲切的学习平台。我们以问答形式更新,为大家呈现精选的前端知识点和最佳实践。通过深入浅出的解释概念,并提供实际案例和练习,让你逐步建立起一个扎实的基础。无论是HTML、CSS、JavaScript还是最新的前端框架和工具,我们都将为你提供丰富的内容和实用技巧,帮助你更好地理解并运用前端开发中的各种技术。
|
6月前
|
设计模式 供应链
行为型设计模式03-观察者模式
行为型设计模式03-观察者模式
35 0
|
16天前
|
设计模式 监控 Java
设计模式 - 观察者模式(Observer):Java中的战术与策略
【4月更文挑战第7天】观察者模式是构建可维护、可扩展系统的关键,它在Java中通过`Observable`和`Observer`实现对象间一对多的依赖关系,常用于事件处理、数据绑定和同步。该模式支持事件驱动架构、数据同步和实时系统,但需注意避免循环依赖、控制通知粒度,并关注性能和内存泄漏问题。通过明确角色、使用抽象和管理观察者注册,可最大化其效果。
|
1月前
|
设计模式 存储 Java
【设计模式】观察者模式
【设计模式】观察者模式
|
4月前
|
设计模式 存储 Java
Java设计模式【二十】:观察者模式
Java设计模式【二十】:观察者模式
26 0
|
3月前
|
设计模式 Java Spring
设计模式之观察者模式
设计模式之观察者模式
26 0
|
3月前
|
设计模式 算法 自动驾驶
常见的设计模式(模板与方法,观察者模式,策略模式)
随着时间的推移,软件代码越来越庞大,随着而来的就是如何维护日趋庞大的软件系统。在面向对象开发出现之前,使用的是面向过程开发来设计大型的软件程序,面向过程开发将软件分成一个个单独的模块,模块之间使用函数进行组合,最后完成系统的开发,每次需要修改软件,如果不涉及好各个模块的关系,就会导致软件系统难以维护,从而导致软件变得不可使用。面向对象方法用对象模拟问题域中的实体,以对象间的联系刻画实体间联系
63 2