Android设计模式详解之观察者模式

news2024/10/6 14:25:34

前言

观察者常用于订阅-发布系统,能够将观察者和被观察者进行解耦,降低两者之间的依赖;

定义:定义对象间一种一对多的依赖关系,使得每当一个对象改变状态时,则所有依赖于它的对象都会得到通知并被自动更新。

使用场景:

  • 关联行为场景,需要注意的是,关联行为是可拆分的,而不是组合关系;
  • 事件多级触发场景;
  • 跨系统的消息交换场景,如消息队列、事件总线的处理机制。

UML类图:
观察者模式UML

  • Subject:抽象主题,被观察者的角色,抽象主题角色把所有观察者对象的引用保存到一个集合中,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察对象;
  • ConcreteSubject:具体主题,具体被观察者,该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发出通知;
  • Observer:抽象观察者,该角色是观察者的抽象类,它定义了一个更新接口,使得在得到主题的更改通知时更新自己;
  • ConcreteObserver:具体的观察者,该角色实现抽象观察者角色所定义的更新接口,以便在主题的状态发生变化时更新自己的状态;

实现示例

这里以关注微信公众号进行举例,对于公众号是被观察者,对于微信用户则是观察者,微信用户订阅微信公众号后,公众号发布文章通知到微信用户,典型的发布-订阅模式;下面我们就用观察者模式来实现:

  • 定义抽象观察者,Observer
interface Observer<T> {
    //定义文章更新方法
    fun update(t: T)
}
  • 定义抽象被观察者,持有所有的观察者,Observable
open class Observable<T> {
    private val observers = arrayListOf<Observer<T>>()

    /**
     * 订阅被观察者
     */
    fun addObserver(observer: Observer<T>) {
        observers.add(observer)
    }

    /**
     * 解除订阅
     */
    fun removeObserver(observer: Observer<T>) {
        observers.remove(observer)
    }

    /**
     * 通知所有的观察者
     */
    fun notifyObservers(t: T) {
        for (observer in observers) {
            observer.update(t)
        }
    }
}
  • 具体观察者,微信用户,WXUser
class WXUser(private val name: String) : Observer<String> {

    override fun update(article: String) {
        println("$name receive article:$article")
    }

}
  • 微信公众号,具体的被观察者,WXPublicObservable
class WXPublicObservable : Observable<String>() {
    /**
     * 更新公众号文章
     */
    fun updateArticle(article: String) {
        notifyObservers(article)
    }

}
  • 编写测试类以及测试结果
object Test {

    @JvmStatic
    fun main(args: Array<String>) {
        //定义微信用户
        val user1 = WXUser("user1")
        val user2 = WXUser("user2")
        val wxPublicObservable = WXPublicObservable()
        //订阅公账号
        wxPublicObservable.addObserver(user1)
        wxPublicObservable.addObserver(user2)
        //发布一篇文章
        wxPublicObservable.updateArticle("互联网行业,迎来寒冬...")
        println("---------------------------")
        println("---------------------------")
        //user1取消订阅
        wxPublicObservable.removeObserver(user1)
        wxPublicObservable.updateArticle("疫情何时才能结束...")
    }
}
user1 receive article:互联网行业,迎来寒冬...
user2 receive article:互联网行业,迎来寒冬...
---------------------------
---------------------------
user2 receive article:疫情何时才能结束...

Android源码中的观察者模式

  • ListView中的观察者模式;
    我们首先看下ListView.setAdapter方法
    public void setAdapter(ListAdapter adapter) {
    	//判断是否之前已经注册过观察者,如果注册过先注销,避免多次注册;
        if (mAdapter != null && mDataSetObserver != null) {
            mAdapter.unregisterDataSetObserver(mDataSetObserver);
        }

     ...
        super.setAdapter(adapter);
        if (mAdapter != null) {
        	//创建一个数据集观察者进行注册
            mDataSetObserver = new AdapterDataSetObserver();
            mAdapter.registerDataSetObserver(mDataSetObserver);
         ...

        requestLayout();
    }

我们紧着看下AdapterDataSetObserver

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

AdapterDataSetObserver会继承DataSetObserver,我们看下DataSetObserver,典型的一个抽象观察者角色

public abstract class DataSetObserver {
    public void onChanged() {
      
    }
    public void onInvalidated() {
      
    }
}

通过上面的ListView.setAdapter方法,就完成了观察者注册工作,我们在看下数据更新时,我们一般会调用notifyDataSetChanged方法

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

mDataSetObservable代码如下,显然,调用notifyDataSetChanged方法实际上就是让被观察者去通知观察者数据发生了变化;

public class DataSetObservable extends Observable<DataSetObserver> {

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

    public void notifyInvalidated() {
        synchronized (mObservers) {
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onInvalidated();
            }
        }
    }
}

我们在回头看下AdapterDataSetObserver的onChanged()方法都做了什么

 class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
        @Override
        public void onChanged() {
            super.onChanged(); //重点看下 super.onChanged()方法
            if (mFastScroll != null) {
                mFastScroll.onSectionsChanged();
            }
        }

        @Override
        public void onInvalidated() {
            super.onInvalidated();
            if (mFastScroll != null) {
                mFastScroll.onSectionsChanged();
            }
        }
    }

super.onChanged()对应AdapterDataSetObserver.onChanged()方法

    class AdapterDataSetObserver extends DataSetObserver {

     

        @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()完成listview所有数据更新
            requestLayout();
        }

小结:Listview数据更新是典型的观察者设计模式,在setAdapter方法里完成了观察者的注册工作,当调用notifyDataSetChanged方法的时候最终会通过requestLayout()完成所有数据更新!

总结

优点:

  • 观察者和被观察者之间是抽象耦合,应对业务变化;
  • 增强系统灵活性、可扩展性;

缺点:
程序中包括一个被观察者,多个观察者对象,消息通知是遍历执行的,一个观察者卡顿,会影响整体的执行效率,这时候可以考虑采用异步的方式。

结语

如果以上文章对您有一点点帮助,希望您不要吝啬的点个赞加个关注,您每一次小小的举动都是我坚持写作的不懈动力!ღ( ´・ᴗ・` )

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/115561.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

湖南人与江西人,关系有多密切?回顾四省填湘的历史

现在的湖南人&#xff0c;可能90%都是四省填湘的移民后代&#xff0c;这种说法可信吗&#xff1f;而湖南人又与江西人的关系有多密切呢&#xff1f;我们来回顾一下四省填湘的历史吧。 四省填湘的背景 在历史上&#xff0c;湖南属于广义上的荆楚之地&#xff0c;是春秋战国时…

图解深度学习-神经网络

深度学习 深度学习是一种统计学习方法&#xff0c;可以在大量数据中自动提取关键特征信息。 深度学习的分类 深度学习的起源有感知器和基于图模型的玻尔兹曼机。在这两个的基础上引入多层结构形成了现在的深度学习。 神经网络的历史 M-P模型和感知器模型 M-P模型是首个通过…

中断和中断系统

目录 中断的引入 中断的概念 中断源&#xff08;2018年&#xff09;背会 中断系统的功能 1&#xff1a;实现中断及返回 2&#xff1a;实现优先权排队&#xff08;中断判优&#xff09; 3&#xff1a;高级中断源能中断低级的中断处理 CPU对外部可屏蔽中断的响应以及中断过…

AtCoder Grand Contest 060 A - No Majority

比赛名称&#xff1a;AtCoder Grand Contest 060 比赛链接&#xff1a;AtCoder Grand Contest 060 A - No Majority 题意&#xff1a; 一个由小写英文字母组成的字符串x被认为是好的&#xff0c;当且仅当以下条件得到满足。 x的每一个长度为2或更大的&#xff08;连续的&am…

springmvc源码之DispatcherServlet前端控制器

系列文章目录 springmvc源码之Web上下文初始化 springmvc源码之DispatcherServlet前端控制器 文章目录系列文章目录DispatcherServlet前端控制器配置serlvet3.0扩展静态资源请求问题源码分析初始化配置刷新WebApplicationContextonRefresh介绍各个组件处理请求getHandlerha.ha…

与时代共命运:数智时代的到来意味着什么?

在数字经济时代背景下&#xff0c;各行各业都在全面推动新技术与商业的加速融合&#xff0c;全力驱动商业模式创新。 当下科技迅速发展&#xff0c;而数智化的发展体现的是社会与经济向新范式的根本转变。而这种转变为企业带来的是产业组织模式、现代基础设施体系、科技人才培…

Iceberg-Trino 如何解决链上数据面临的挑战

1.链上数据处理面临的挑战 区块链数据公司&#xff0c;在索引以及处理链上数据时&#xff0c;可能会面临一些挑战&#xff0c;包括&#xff1a; 海量数据。随着区块链上数据量的增加&#xff0c;数据索引将需要扩大规模以处理增加的负载并提供对数据的有效访问。因此&#xff…

应用层概述(计算机网络-应用层)

目录 网络应用 网络应用程序体系结构 客户/服务器体系结构 P2P对等体系结构 网络应用程序体系结构 应用层协议 网络应用 网络应用是计算机网络体系结构的最上层&#xff0c;是设计和建立计算机网络的最终目的&#xff0c;也是计算机网络中发展最快的部分 我们以一些经典的…

如何选择一个合适的Web存储方案

Web客户端存储是一个现代Web应用必不可少的功能&#xff0c;常见的有Cookie、WebStorage和IndexedDB等&#xff0c;如何选择一个合适的Web存储方案呢&#xff1f; 一. Cookie 1. 为什么要有Cookie? HTTP协议是无状态的&#xff0c;即一次请求和响应就是一次完整地HTTP通信&…

Oracle监听报错相关问题汇总

监听服务报错问题汇总&#xff1a; 1.Windows 2008系统下oracle 11g监听异常死机&#xff0c;无法连接 **C:\Users\administrator>lsnrctl status LSNRCTL for 64-bit Windows: Version 11.2.0.1.0 - Production on 16-9月 -2020 10:09:26 Copyright (c) 1991, 2010, Orac…

(十六).net学习之SOA-WebService

SOA-WebService一、SOA的思想&#xff0c;分布式服务1.SOA2.优势3.数据总线二、建立webservice1.关于webservice2.具体实现三、WCF多宿主协议1、自托管宿主2、windows service宿主3、IIS宿主4、WAS宿主一、SOA的思想&#xff0c;分布式服务 1.SOA SOA:面向服务架构&#xff0…

一个umi4的项目适配到FireFox60.7.1esr版本上的从头到尾

项目场景&#xff1a; 一个使用umi4创建的大屏项目&#xff0c;用户的浏览器使用的是火狐60.7.1的稳定版。然后就报错了&#xff01;&#xff01;&#xff01; 为什么不让用户换谷歌嘞&#xff0c;咱也不知道。那咱就搞兼容吧~~ 贴个浏览器的版本图片&#xff1a; 问题历程 …

【springboot】从解决@valid失效问题 到根据判断放行的更灵活替代方案 再到优雅的打日志

文章目录前言valid失效问题替代方案前言 valid 可以帮助我们节省很多代码 比较方便 但操作失误时 可能会失效 达不到我们预期效果&#xff1b; valid会有个问题 因为注解过于方便 反而会导致拦截后 错误日志的收集会比较麻烦 &#xff0c;以及在面对有时需要拦截 有时不需要拦截…

机器学习中的模型选择和评估

机器学习中的模型选择和评估1. 介绍2. 模型拟合效果2.1欠拟合与过拟合表现方式2.2 避免欠拟合与过拟合的方法3.实例分析3.1鸢尾花数据集3.2 对鸢尾花数据进行聚类1. 介绍 在机器学习系统中&#xff0c;如何训练出更好的模型、如何判断模型的效果&#xff0c;以及模型是否过拟合…

【内存对齐】一篇文章带你看懂内存对齐(万字详细介绍+代码样例)

目录 为什么需要内存对齐 性能 范围 原子性 结论 数据模型 C 的内存对齐 具名要求 平凡类 标准布局类 平凡类与标准布局类总结 标准布局类的内存对齐 普通的标准布局类 带有位域的标准布局类 手动指定对齐大小的标准布局类 非标准布局类的内存对齐 GLSLang 的…

分布式事务(3):AT模式实战-Seata

1 介绍 Seata&#xff08;Simple Extensible Autonomous Transaction Architecture&#xff0c;简单可扩展自治事务框架&#xff09;是 2019 年 1 月份蚂蚁金服和阿里巴巴共同开源的分布式事务解决方案。Seata 开源半年左右&#xff0c;目前已经有接近一万 star&#xff0c;社…

信息系统项目管理师考试总结

1、结果展示 从结果上看&#xff0c;论文刚刚过&#xff0c;案例分析差两份&#xff0c;选择题差五分。 问题一&#xff1a;案例分析中&#xff0c;计算题可以拿到分&#xff0c;提高案例分析的通过率。计算题比较客观。可以在下一次考试中多准备准备。 问题二&#xff1a;客观…

QUIC的诞生

背景 自1999年HTTP/1.1被提出以来&#xff0c;它已经稳定地被使用超过了20个年头。不过经典并不意味着完美&#xff0c;HTTP/1.1中一个连接同一时刻只能处理一个HTTP请求&#xff0c;如果当前的请求没有结束之前&#xff0c;其他的请求只能处于阻塞状 态。这一“对头阻塞”问题…

权限管理的配置思路

1.在main.js中引入permission.js 2.permission.js中通过vuex中getters.access_token进行路由的重定向 直接import ‘.permission’

MSF基本使用和控制台命令

前言 ● Msfconsole使用接口 ○ 最流行的用户接口 ○ 几乎可以使用全部MSF功能 ○ 控制台命令支持TAB自动补全 ○ 支持外部命令的执行&#xff08;系统命令等&#xff09; ○ 点击鼠标启动 / msfconsole -h -q -r -v / exit ○ help / &#xff1f; / help vulns 控制台命令 …