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

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: Java设计模式 | 观察者模式解析与实战

概述

  • **观察者模式是一个使用率非常高的模式,

它最常用的地方是 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源码设计模式解析与实战》
  • 慕课网
相关文章
|
12天前
|
存储 Java 计算机视觉
Java二维数组的使用技巧与实例解析
本文详细介绍了Java中二维数组的使用方法
30 15
|
8天前
|
设计模式
「全网最细 + 实战源码案例」设计模式——模式扩展(配置工厂)
该设计通过配置文件和反射机制动态选择具体工厂,减少硬编码依赖,提升系统灵活性和扩展性。配置文件解耦、反射创建对象,新增产品族无需修改客户端代码。示例中,`CoffeeFactory`类加载配置文件并使用反射生成咖啡对象,客户端调用时只需指定名称即可获取对应产品实例。
66 40
|
3天前
|
设计模式 Java 开发者
「全网最细 + 实战源码案例」设计模式——适配器模式
适配器模式(Adapter Pattern)是一种结构型设计模式,通过引入适配器类将一个类的接口转换为客户端期望的另一个接口,使原本因接口不兼容而无法协作的类能够协同工作。适配器模式分为类适配器和对象适配器两种,前者通过多重继承实现,后者通过组合方式实现,更常用。该模式适用于遗留系统改造、接口转换和第三方库集成等场景,能提高代码复用性和灵活性,但也可能增加代码复杂性和性能开销。
51 28
|
3天前
|
设计模式 缓存 Java
「全网最细 + 实战源码案例」设计模式——代理模式
代理模式(Proxy Pattern)是一种结构型设计模式,通过代理对象控制对目标对象的访问并添加额外功能。它分为静态代理和动态代理,后者包括JDK动态代理和CGLIB动态代理。JDK动态代理基于接口反射生成代理类,而CGLIB通过继承目标类生成子类。代理模式适用于延迟初始化、访问控制、远程服务、日志记录和缓存等场景,优点是职责分离、符合开闭原则和提高安全性,缺点是增加系统复杂性。
54 25
|
7天前
|
设计模式 存储 Java
「全网最细 + 实战源码案例」设计模式——原型模式
原型模式(Prototype Pattern)是一种创建型设计模式,通过复制现有对象来创建新对象,适用于创建成本高或复杂的对象场景。其核心思想是“克隆”,避免直接实例化类。结构上分为抽象原型类、具体原型类和客户端。优点包括减少对象创建成本、隐藏复杂性、简化实例创建;缺点是处理循环引用的复杂对象时较为麻烦。实现步骤为定义原型类、重写`clone()`方法并调用。注意事项包括浅拷贝与深拷贝的区别及`Cloneable`接口的使用。
48 20
|
11天前
|
运维 Shell 数据库
Python执行Shell命令并获取结果:深入解析与实战
通过以上内容,开发者可以在实际项目中灵活应用Python执行Shell命令,实现各种自动化任务,提高开发和运维效率。
43 20
|
9天前
|
设计模式 关系型数据库
「全网最细 + 实战源码案例」设计模式——简单工厂模式
简单工厂模式是一种创建型设计模式,通过工厂类根据传入参数创建不同类型的对象,也称“静态工厂方法”模式。其结构包括工厂类、产品接口和具体产品类。优点是封装性强、代码复用性好;缺点是扩展性差,增加新产品时需修改工厂类代码,违反开闭原则。适用于对象种类较少且调用者无需关心创建细节的场景。
45 19
|
8天前
|
设计模式
「全网最细 + 实战源码案例」设计模式——抽象工厂模式
抽象工厂模式是一种创建型设计模式,提供接口用于创建一系列相关或依赖的对象,无需指定具体类。它解决了产品族问题,管理和创建一组相关产品。结构上包括抽象工厂、具体工厂、抽象产品和具体产品。适用于创建相关对象、产品族固定但种类变化的场景。优点是分离接口与实现、管理产品族方便且扩展性好;缺点是产品族扩展困难且代码复杂度增加。通过配置文件和反射机制可进一步改进,使系统更灵活易扩展。
37 17
|
7天前
|
设计模式 Java
「全网最细 + 实战源码案例」设计模式——生成器模式
生成器模式(Builder Pattern)是一种创建型设计模式,用于分步骤构建复杂对象。它允许用户通过控制对象构造的过程,定制对象的组成部分,而无需直接实例化细节。该模式特别适合构建具有多种配置的复杂对象。其结构包括抽象建造者、具体建造者、指挥者和产品角色。适用于需要创建复杂对象且对象由多个部分组成、构造过程需对外隐藏或分离表示与构造的场景。优点在于更好的控制、代码复用和解耦性;缺点是增加复杂性和不适合简单对象。实现时需定义建造者接口、具体建造者类、指挥者类及产品类。链式调用是常见应用方式之一。
41 12
|
9天前
|
设计模式 关系型数据库
「全网最细 + 实战源码案例」设计模式——工厂方法模式
简单工厂模式是一种创建型设计模式,通过一个工厂类根据传入参数创建不同类型的产品对象,也称“静态工厂方法”模式。其结构包括工厂类、产品接口和具体产品类。适用于创建对象种类较少且调用者无需关心创建细节的场景。优点是封装性强、代码复用性好;缺点是扩展性差,增加新产品时需修改工厂类代码,违反开闭原则。
34 15

推荐镜像

更多