Java设计模式 | 观察者模式解析与实战

2023-05-16

概述

  • 观察者模式是一个使用率非常高的模式,
    它最常用的地方是 GUI 系统、订阅——发布系统。

  • 这个模式的一个重要作用就是解耦
    被观察者观察者解耦,
    使得它们之间的依赖性更小,甚至做到毫无依赖。

  • 以GUI系统来说,应用的UI具有易变性,
    尤其是前期随着业务的改变或者产品的需求修改,
    应用界面也会经常性变化,但是业务逻辑基本变化不大,
    此时,GUI系统需要一套机制来应对这种情况,
    使得UI层具体的业务逻辑解耦,观察者模式此时就派上用场了。

!!!
因为观察者仅负责调度被观察者更新方法
并提供一个业务数据给被观察者
被观察者具体实现更新方法【可以实现UI、数据更新】,
更新方法具体实现的内容观察者业务逻辑基本毫无依赖!

定义

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

使用场景

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

UML类图

 
 

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

观察者模式实现思路总结

观察者接口准备更新(数据或UI的)方法;
被观察者接口准备三个抽象方法;

观察者实现类具体实现更新逻辑,可以有参数,参数为更新需要的数据;

被观察者实现类准备一个观察者List以及实现三个方法:
1.观察者注册方法:
参数为某观察者,功能是把观察者参数加到观察者List中;
2.注销观察者方法:
参数为某观察者,功能是把观察者参数从观察者List中移除;
3.通知观察者方法:无参数或者把需要通知的数据作为参数,
功能是遍历所有已注册的观察者,
即遍历 注册添加到 观察者List中的观察者,逐个调用List中所有观察者的更新方法;即一次性更新所有已注册的观察者!

使用时,
实例化一个被观察者和若干个观察者,
将所有观察者注册到被观察者处,
调用被观察者的通知方法,一次性更新所有已注册的观察者!

案例

来自 Android | TCP的C(Java|Android)/S(Java)通信实战经典聊天室案例(文末附本案例代码实现概述、观察者模式实现小结)

  • 准备一个消息队列,
    每一个Client发送过来的消息,
    都会被加入到队列当中去,
    队列中默认有一个子线程,
    专门从队列中,死循环,不断去取数据(取出队列的队头),
    取到数据就做相关处理,比如分发给其他的socket;
/**
 * <pre>
 *     desc   :每一个Client发送过来的消息,
 *             都会被加入到队列当中去,
 *             队列中默认有一个子线程,
 *             专门从队列中,死循环,不断去取数据,
 *             取到数据就做相关处理,比如分发给其他的socket;
 * </pre>
 */
public class MsgPool {

    private static MsgPool mInstance = new MsgPool();

    /*
        这里默认消息是String类型,
        或者可以自行封装一个Model 类,存储更详细的信息

        block n.块; 街区;障碍物,阻碍
        顾名思义,这是一个阻塞的队列,当有消息过来时,就把消息发送给这个队列,
        这边会起一个线程专门从队列里面去取消息,
        如果队列中没有消息,就会阻塞在原地
     */
    private LinkedBlockingQueue<String> mQueue = new LinkedBlockingQueue<>();

    public static MsgPool getInstance() {
        return mInstance;
    }

    private MsgPool() {
    }

    //这是一个阻塞的队列,
    // 当有消息过来时,即客户端接收到消息时,
    // 就把消息发送(添加)到这个队列中
    //现在所有的客户端都可以发送消息到这个队列中
    public void sendMsg(String msg) {
        try {
            mQueue.put(msg);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    //要一早就调用本方法,
    // 启动这个读取消息的线程,在后台不断运行
    public void start() {
        //开启一个线程去读队列的数据
        new Thread() {
            @Override
            public void run() {
                //无限循环读取信息
                while (true) {
                    try {
                        //取出并移除队头;没有消息时,take()是阻塞的
                        String msg = mQueue.take();
                        notifyMsgComing(msg);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }

    //被观察者方法,遍历所有已注册的观察者,一次性通知更新
    private void notifyMsgComing(String msg) {
        for (MsgComingListener listener : mListeners) {
            listener.onMsgComing(msg);
        }
    }

    //观察者接口
    public interface MsgComingListener {
        void onMsgComing(String msg);//更新方法
    }

    //被观察者,存放观察者
    private List<MsgComingListener> mListeners = new ArrayList<>();

    //被观察者方法,添加观察者到列表
    public void addMsgComingListener(MsgComingListener listener) {
        mListeners.add(listener);
    }
}

案例小结:
所有的客户端都可发送消息到队列中,
然后所有的客户端都在等待
消息队列的消息新增(mQueue.put())这个时刻,
消息队列一新增消息,
即一接收到某个客户端发送过来消息(mQueue.put()),
则消息都会一次性转发给所有客户端,
所以这里涉及到一个观察者设计模式,
消息队列(MsgPool)或消息(Msg)是被观察者,
所有客户端处理线程(ClientTask)都是观察者








 


参考:

  • 《Android源码设计模式解析与实战》
  • 慕课网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java设计模式 | 观察者模式解析与实战 的相关文章

随机推荐

  • React + TS + Mobx 示例

    一 创建项目 方式一 xff1a create react app todo React ts demo scripts version 61 react scripts ts cd todo React ts demo npm start
  • AMD IOMMU与Linux (2) -- IVRS及AMD IOMMU硬件初始化

    介绍AMD IOMMU driver基于IVRS的硬件初始化情况 1 I O Virtualization ACPI table 2 drivers iommu amd init c 1 I O Virtualization ACPI ta
  • AMD IOMMU与Linux (3) -- DMA

    Linux中DMA会使用硬件IOMMU如AMD IOMMU INTEL VT D xff0c 也会使用软件的SWIOTLB 这篇梳理一下LINUX内核在有AMD IOMMU的情况下 xff0c 是如何做DMA的 xff0c 内容包括如下 1
  • AMD IOMMU与Linux (4) -- Domain, Group, Device

    1 domain的本质是一个页表 xff0c 1对1的关系 2 IOMMU DOMAIN UNMANAGED vs IOMMU DOMAIN DMA a IOMMU DOMAIN UNMANAGED DMA mappings managed
  • 第三篇:知其然,知其所以然-USB音频设备的开发过程

    最近 xff0c 有朋友正好在开发一个USB音频设备 xff0c 所以询问我一些USB音频设备开发方面的技术细节问题 xff1b 也和音响发烧友聊到USB音频设备的实现方式与其优缺点 xff1b 后来 xff0c 也和人谈到实现一个USB音
  • 第七篇:风起于青萍之末-电源管理请求案例分析(下)

    第五篇 风起于青萍之末 电源管理请求案例分析 上 http blog csdn net u013140088 article details 18180249 第六篇 风起于青萍之末 电源管理请求案例分析 中 http blog csdn
  • 第十九篇:USB Audio/Video Class设备协议

    转发请注明出处 随着项目的不断进行 我想在网上查找了一下USB Audio Video的最新资料 看看有没有业内人士的更新 由于我们的项目一直在技术的最前延 而且这个USB IF官方发布的协议 也非常非常新 结果找了半天 都是我这篇文章的转
  • 《网络架构系列2-Http详解》

    不诗意的女程序媛不是好厨师 转载请注明出处 xff0c From李诗雨 https blog csdn net cjm2484836553 article details 104136511 网络架构系列2 Http详解 1 Http的协议
  • 第三十二篇:Windbg中USB2.0调试环境的搭建

    2011年的时候 xff0c 为了开发USB Mass storage UASP USB attached SCSI Protocol 的设备驱动程序 xff0c 从米国买了两个USB2 0的调试小设备 xff08 如下图 xff0c 每个
  • 理解SerDes 之一

    理解SerDes FPGA发展到今天 xff0c SerDes Serializer Deserializer 基本上是标配了 从PCI到PCI Express 从ATA到SATA xff0c 从并行ADC接口到JESD204 从RIO到S
  • 理解SerDes 之二

    理解SerDes 之二 2012 11 11 21 17 12 转载 标签 xff1a dfe serdes it 2 3 接收端均衡器 Rx Equalizer 2 3 1 线形均衡器 Linear Equalizer 接收端均衡器的目标
  • USB3.0的物理层测试探讨

    USB简介 USB Universal Serial Bus 即通用串行总线 xff0c 用于把键盘 鼠标 打印机 扫描仪 数码相机 MP3 U盘等外围设备连接到计算机 xff0c 它使计算机与周边设备的接口标准化 在USB1 1版本中支持
  • ARM SoC漫谈

    作者 xff1a 重走此间路 链接 xff1a https zhuanlan zhihu com p 24878742 来源 xff1a 知乎 著作权归作者所有 商业转载请联系作者获得授权 xff0c 非商业转载请注明出处 芯片厂商向客户介
  • μC/OS III - 任务调度 Ⅰ:调度过程和调度点

    这是 C OS III任务调度的第一篇文章 xff1a 调度过程和调度点 基于Cortex M系列的处理器 xff0c 从最简单的创建任务开始 xff0c 分析 C OS III的任务调度过程 包括上下文切换的详细过程 任务的栈分配详情 引
  • 使用CANAPE脚本script周期性发送报文

    1 新建一个drive 选择devices选项卡中的device configuration 选择device菜单 xff0c new一个新驱动CAN 名称可自定义 Next 配置通道 xff0c 导入DBC Next 选择CANAPE对应
  • 【图像处理】MATLAB:基本原理

    前言 兜兜转转 xff0c 越发意识到夯实基础的重要性 不积跬步无以至千里 xff0c 想要深入学习图像处理 xff0c 就得安下心来踏实学习 xff0c 掌握基本理论知识 xff0c 切不可再得过且过 吊儿郎当 谨记两个词 刻苦 创新 x
  • 【Kalman】卡尔曼滤波Matlab简单实现

    本节卡尔曼滤波Matlab实现是针对线性系统估计的 xff0c 仅为简单仿真 1 离散时间线性动态系统的状态方程 线性系统采用状态方程 观测方程及其初始条件来描述 线性离散时间系统的一般状态方程可描述为 其中 xff0c X k 是 k 时
  • 安装Airsim并在Airsim仿真环境下进行DDPG DQN强化学习算法无人机训练

    微软开源了基于虚幻4引擎的一款用于模拟无人机飞行的工具AirSim 用户可以用在虚幻引擎下模拟无人机的飞行并进行数据采集 非常适合做视觉算法的测试以及仿真环境的训练等等 xff0c 下面介绍如何快速使用次仿真环境完成project的运行和使
  • Android面试专题 (十一):显式Intent & 隐式Intent

    面试官 xff1a 来 xff0c 说一下Android中的显式Intent 和 隐式Intent吧 xff01 嗯 xff0c 乍一听觉得这么简单你让我说什么呢 xff1f 但是 xff0c 没办法 xff0c 面试往往面的就是基础不是嘛
  • Java设计模式 | 观察者模式解析与实战

    概述 观察者模式是一个使用率非常高的模式 xff0c 它最常用的地方是 GUI 系统 订阅 发布系统 这个模式的一个重要作用就是解耦 xff0c 将被观察者和观察者解耦 xff0c 使得它们之间的依赖性更小 xff0c 甚至做到毫无依赖 以