• 17【观察者设计模式】



    十七、观察者设计模式

    17.1 观察者设计模式简介

    17.1.1 观察者设计模式概述

    观察者设计模式(Observer Pattern):又称为发布订阅模式(Publish/Subsribe),它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。

    观察者模式的核心就是将观察者与被观察者解耦,类似于消息的广播发送,使得被观察者的变动能通知到观察者

    • 如图所示(订阅与退订):

    在这里插入图片描述

    • 如图所示(广播消息):

    在这里插入图片描述

    17.2 观察者设计模式的UML类图

    观察者模式主要包含如下4个角色:

    • 1)抽象主题(ISubject):被观察的对象,该角色是一个接口,规范主题的方法,如订阅、退订、广播消息等;
    • 2)具体主题(ConcreteSubject):具体的被观察对象,对抽象主题中的方法进行实现
    • 3)抽象观察者(IObserver):抽象的观察者对象,用于观察主题,定义了响应通知的更新方法
    • 4)具体观察者(ConcreteObserver):对抽象观察者进行实现

    在这里插入图片描述

    17.3 观察者设计模式的实现

    17.3.1 手动实现观察者

    【案例】

    定义一个消息处理模型,小灰、小蓝订阅自己喜欢观看的频道,当这些频道退出新的节目时需通知已经订阅过该频道的用户

    • 1)定义抽象主题:
    package com.pattern.demo01_手动实现观察者设计模式;
    
    /**
     * @author lscl
     * @version 1.0
     * @intro: 抽象主题
     */
    public interface ISubject {
    
        // 主题名称
        public String getName();
    
        // 增加订阅者
        public void attach(IObserver observer);
    
        // 删除订阅者
        public void detach(IObserver observer);
    
        // 广播消息
        public void notify(String message);
    
    }
    
    • 定义具体主题:
    package com.pattern.demo01_手动实现观察者设计模式;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author lscl
     * @version 1.0
     * @intro: 具体主题(具体被观察者)
     */
    public class TVSubject implements ISubject {
    
        // 频道的名称
        private String name;
    
        // 储存订阅频道的用户
        private List<IObserver> userList = new ArrayList();
    
        public TVSubject(String name) {
            this.name = name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public String getName() {
            return name;
        }
    
        @Override
        public void attach(IObserver observer) {
            userList.add(observer);
        }
    
        @Override
        public void detach(IObserver observer) {
            userList.remove(observer);
        }
    
        @Override
        public void notify(String message) {
            for (IObserver observer : userList) {
                observer.update(this, message);
            }
        }
    }
    
    • 3)定义抽象观察者:
    package com.pattern.demo01_手动实现观察者设计模式;
    
    /**
     * @author lscl
     * @version 1.0
     * @intro: 抽象观察者类
     */
    public interface IObserver {
    
        // 更新信息方法
        void update(ISubject subject, String message);
    }
    
    • 4)定义具体观察者:
    package com.pattern.demo01_手动实现观察者设计模式;
    
    /**
     * @author lscl
     * @version 1.0
     * @intro: 具体观察者(接收来自观察者的消息)
     */
    public class UserObserver implements IObserver {
        // 用户名
        private String name;
    
        public UserObserver(String name) {
            this.name = name;
        }
    
        @Override
        public void update(ISubject subject, String message) {
            System.out.println("【" + name + "】接到来自【" + subject.getName() + "】的消息【" + message + "】");
        }
    }
    
    • 5)测试类:
    package com.pattern.demo01_手动实现观察者设计模式;
    
    /**
     * @author lscl
     * @version 1.0
     * @intro:
     */
    public class Demo01 {
    
        public static void main(String[] args) {
    
            // 创建主题(频道、被观察者)
            ISubject subject = new TVSubject("CCTV");
    
            // 创建用户(观察者)
            UserObserver user1 = new UserObserver("小灰");
            UserObserver user2 = new UserObserver("小蓝");
    
            // 订阅CCTV频道
            subject.attach(user1);
            subject.attach(user2);
    
            // 发送给那些订阅过CCTV频道的用户
            subject.notify("今晚19.00播出新闻联播,敬请收看~");
        }
    }
    

    运行效果:

    在这里插入图片描述

    17.3.2 采用JDK提供的API实现

    在 Java 中,提供有java.util.Observable类来表示主题,java.util.Observer来表示观察者,借助这两个类我们可以很轻松的实现观察者模式;

    1)Observable

    Observable 类是一个抽象主题角色,它有一个 Vector 集合成员变量,用于保存所有要通知的观察者对象,常用方法如下:

    • void addObserver(Observer o):将新的观察者对象添加到集合中。

    • void notifyObservers(Object arg):调用集合中的所有观察者对象的update方法,通知它们数据发生改变。通常越晚加入集合的观察者越先得到通知。

    • void setChange():Observable类内部有个变量changed,默认为false;调用该方法将其设置为true,表示目标对象发生了变化。然后调用notifyObservers()方法才会通知观察者。

    2)Observer 接口

    Observer接口是抽象观察者角色,它监视目标对象的变化,当目标对象发生变化时,主题将会发送消息到观察者,并调用 update 方法,进行相应的工作。

    3)实现观察者模式
    • 1)定义主题:
    package com.pattern.demo02_采用JDK的API实现;
    
    import java.util.Date;
    import java.util.Observable;
    
    /**
     * @author lscl
     * @version 1.0
     * @intro: 被观察者
     */
    public class TVSubject extends Observable {
    
        private String name;
    
        public TVSubject(String name) {
            this.name = name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public void publish(String message) {
            super.setChanged();                 // 将changed设置为true(只有changed为true才能进行下面的广播消息)
            super.notifyObservers(message);     // 广播消息,观察者将会接收到消息
        }
    }
    
    • 2)定义观察者:
    package com.pattern.demo02_采用JDK的API实现;
    
    import java.util.Observable;
    import java.util.Observer;
    
    /**
     * @author lscl
     * @version 1.0
     * @intro: 具体观察者
     */
    public class UserObserver implements Observer {
    
        private String name;
    
        public UserObserver(String name) {
            this.name = name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        /**
         *
         * @param subject: 订阅的主题
         * @param message: 主题发送过来的消息
         */
        @Override
        public void update(Observable subject, Object message) {
            TVSubject tvSubject = (TVSubject) subject;
    
            System.out.println("【" + getName() + "】接收到来自【" + tvSubject.getName() + "】的一条消息【" + message + "】");
        }
    }
    
    • 3)测试类:
    package com.pattern.demo02_采用JDK的API实现;
    
    /**
     * @author lscl
     * @version 1.0
     * @intro:
     */
    public class Demo01 {
        public static void main(String[] args) {
            TVSubject subject = new TVSubject("马桶台");
    
            subject.addObserver(new UserObserver("小灰"));
            subject.publish("各位马桶粉们~今晚21.30播出《快乐大本营》请大家准时收看~");
        }
    }
    

    运行效果:

    在这里插入图片描述

    17.4 观察者设计模式的优缺点

    • 优点:
      • 1)降低了被观察者与观察者之间的耦合关系,两者之间是抽象耦合关系。
      • 2)实现了一对多的通信机制,支持事件注册机制,当被观察者触发事件时,只有感兴趣(订阅了)的观察者可以接收到通知;
    • 缺点:
      • 1)如果观察者数量过多,那么广播消息通知会耗时较长
      • 2)事件通知是同步的,如果其中一个观察者处理时间消耗过长,则会影响后面的观察者接收该信息
      • 3)如果观察者与被观察者之间存在循环依赖,则可能导致两者之间的循环调用,最终导致系统崩溃
  • 相关阅读:
    最新版校园招聘进大厂系列----------(4)京东篇 -----未完待续
    「高性能响应式 Web 开发实战」 Part I
    【SSM】MyBatis 操作数据库(重点:Mybatis两种使用方式)
    vLLM-prefix浅析(System Prompt,大模型推理加速)
    如何区分快解析内网穿透和Nginx端口映射?
    技术分享| anyRTC低延时直播优化
    软考高级-系统架构师-案例分析-数据库真题考点汇总
    Kotlin协程:MutableSharedFlow的实现原理
    使用maven框架搭建一个IDEA插件项目
    4367. 拍照2
  • 原文地址:https://blog.csdn.net/Bb15070047748/article/details/127095616