3分钟看懂设计模式02:观察者模式

news2025/1/25 9:24:36

一、什么是观察者模式

观察者模式又叫做发布-订阅模式或者源-监视器模式

结合它的各种别名大概就可以明白这种模式是做什么的。

其实就是观察与被观察,一个对象(被观察者)的状态改变会被通知到观察者,并根据通知产生各自的不同的行为。

以下为《设计模式的艺术》中给出的定义:

观察者模式(Observer Pattern):定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新

二、观察者模式的4个角色

Subject(目标)

Subject是被观察的对象。

Subject可以是接口、抽象类或者具体类。

它一般有4个要素

• 一个观察者集合,一般用Vector。

• 增加观察者的方法。

• 删除观察者的方法。

• 通知方法notify()。

ConcreteSubject(具体目标)

是Subject的子类,没什么特殊的,如果有抽象方法需要实现就实现,没有的话这个类不写也行。

Observer(观察者)

一般是个接口,声明一个update()方法。

ConcreteObserver(具体观察者)

刚开始学的话先会最简单的形式就可以了,就直接实现Observer接口,实现update()方法就完了。

三、观察者模式2个代码实现案例

通过2个案例,基本就可以知道观察者模式是怎么回事了,对照观察者模式的4个角色,敲一遍。

单纯熟悉4个角色的案例

先写一个被观察者Subject

public abstract class Subject {
    // 存放观察者的集合
    private Vector<Observer> obs = new Vector<>();

    public void addObserver(Observer obs) {
        this.obs.add(obs);
    }
    public void delObserver(Observer obs) {
        this.obs.remove(obs);
    }

    // 通知方法,写成抽象方法让ConcreteSubject实现也一样的
    protected void notifyObserver() {
        for (Observer ob : obs) {
            ob.update();
        }
    }
    // 这里也是可写可不写,根据业务需求
    public abstract void doSomething();

}

再写一个ConcreteSubject

public class ConcreteSubject extends Subject {
    @Override
    public void doSomething() {
        System.out.println("被观察者事件发生改变");
        this.notifyObserver();
    }
}

观察者接口Observer

public interface Observer {
    public void update();
}

观察者实现类ConcreteObserver,我们这里给出2个观察者

public class ConcreteObserver01 implements Observer {
    @Override
    public void update() {
        System.out.println("观察者01收到状态变化信息,并进行处理...");
    }
}
public class ConcreteObserver02 implements Observer {
    @Override
    public void update() {
        System.out.println("观察者02收到状态变化信息,并进行处理...");
    }
}

最后给出一个测试类,测试运行一个看看效果,看不明白代码就debug一下捋一捋。

以下代码的意思就是:

你得有东西被观察被监视吧?

所以先创建一个被观察者,比如我的账户余额。

你得设置哪些对象在观察、监视我的账户吧?

那就添加2个,我和我老婆。

然后就坐等账户余额有变化。

一旦发工资,状态变化!!!

图片

dosomething()!

notifyObserver()!

遍历观察者列表!

update()!

这个时候我和我老婆分别对应各自实现的update()方法。

我马上去买了游戏。

我老婆马上去买了化妆品。

(不过这个例子好像不太合适,因为update方法里又会导致账户余额的变化,循环起来了,不过大概明白咋回事就行了。)

    public class Client {
        public static void main(String[] args) {
            ConcreteSubject subject = new ConcreteSubject();
            subject.addObserver(new ConcreteObserver01());
            subject.addObserver(new ConcreteObserver02());
            subject.doSomething();
        }
    }

盟友受攻击发通知,其他盟友做出响应

这个例子也是《设计模式的艺术》给出的案例。

我们的需求如下:

联盟成员收到攻击→发送通知给盟友→盟友做出响应。

如果按照上述思路设计,则每个成员必须持有其他所有成员的状态信息,导致系统开销过大。所以我们引入一个战队控制中心来统一维护所有战队成员信息。

图片

依然是我们四步走:

先创建一个Subject被观察者,这里是AllyControlCenter控制中心

    public abstract class AllyControlCenter {

        /**
         * 战队名称
         */
        protected String allyName;
        /**
         * 定义一个集合,用来存储具体观察者,也就是战队成员
         */
        protected Vector<Observer> players = new Vector<Observer>();

        /**
         * 加入战队
         */
        public void join(Observer observer) {
            System.out.println(observer.getName() + "加入" + this.allyName + "战队!");
            players.add(observer);
        }

        /**
         * 退出战队
         */
        public void quit(Observer observer) {
            System.out.println(observer.getName() + "退出" + this.allyName + "战队!");
            players.remove(observer);
        }

        /**
         * 声明抽象通知方法
         * @param name
         */
        public abstract void notifyObserver(String name);

        /**
         * 设置成员变量方法
         * @param allyName
         */
        public void setAllyName(String allyName) {
            this.allyName = allyName;
        }

        /**
         * 获取成员变量方法
         * @return
         */
        public String getAllyName() {
            return this.allyName;
        }
    }

再创建一个ConcreteSubject具体被观察者ConcreteAllyControlCenter

    public class ConcreteAllayControlCenter extends AllyControlCenter {
        public ConcreteAllayControlCenter(String name) {
            System.out.println(name + "战队组建成功!");
            System.out.println("-------------");
            this.allyName = name;
        }

        /**
         * 实现通知方法
         * @param name
         */
        @Override
        public void notifyObserver(String name) {
            System.out.println(this.allyName + "战队紧急通知,盟友" + name + "遭受敌人攻击");
            // 遍历观察者集合,调用每一个盟友(除了自己)的支援方法
            for (Observer obs : players) {
                if (!obs.getName().equalsIgnoreCase(name)) {
                    obs.help();
                }
            }
        }
    }

创建一个抽象观察者Observer

    public interface Observer {
        public String getName();

        public void setName(String name);

        /**
         * 声明支援盟友的方法
         */
        public void help();

        /**
         * 声明遭受攻击的方法
         */
        public void beAttacked(AllyControlCenter acc);
    }

在创建具体观察者Player

public class Player implements Observer {
    private String name;

    public Player(String name) {
        this.name = name;
    }
    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 支援盟友的方法实现
     */
    @Override
    public void help() {
        System.out.println("坚持住,"+this.name +"来救你了!");
    }

    /**
     * 遭受攻击的方法实现
     * 当遭受攻击时,调用战队控制中心类的通知方法notifyObserver()来通知盟友
     * @param acc
     */
    @Override
    public void beAttacked(AllyControlCenter acc) {
        System.out.println(this.name + "被攻击!");
        acc.notifyObserver(name);
    }
}

测试运行

public class Client {
    public static void main(String[] args) {
        // 定义被观察者
        AllyControlCenter acc = new ConcreteAllayControlCenter("金庸群侠");

        // 定义4个观察者
        Observer player1 = new Player("杨过");
        Observer player2 = new Player("令狐冲");
        Observer player3 = new Player("张无忌");
        Observer player4 = new Player("段誉");
        acc.join(player1);
        acc.join(player2);
        acc.join(player3);
        acc.join(player4);

        // 某成员遭受攻击
        player1.beAttacked(acc);

    }
}

四、我什么时候用观察者模式?

  1. 1. 事件处理系统:例如,用户界面框架中,当用户进行某些操作(如点击按钮、移动鼠标等)时,可以使用观察者模式来通知相关的处理程序。

  2. 2. 数据订阅与发布系统:在需要向多个客户端发布数据更新的场景中,例如股票行情显示、新闻更新等,可以使用观察者模式。

  3. 3. 跨系统的消息交换:例如,在微服务架构中,服务间的事件可以通过观察者模式进行通信,确保各服务间的解耦。

  4. 4. 状态监控和警报系统:在需要监控某些状态并在特定条件下发送警报的系统中,观察者模式可以用来实现监控对象和警报系统之间的通信。

  5. 5. 配置管理:当系统配置信息发生变更时,使用观察者模式可以实时通知各个使用配置的组件进行相应的调整。


往期推荐:

● 师爷,翻译翻译什么叫AOP

● 翻译,师爷师爷什么叫事务

● 纪念JDBC

● SpringBoot实现动态数据源配置‍

● 聚簇索引、回表与覆盖索引

● Java锁到底是个什么东西

图片

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

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

相关文章

二 线性代数-向量

1、向量的表示方法&#xff1a; 其中的 i、j、k是坐标轴方向的单位向量。 2、向量的模&#xff1a; 用坐标计算的方法&#xff1a; 3、向量的运算&#xff1a; 3.1 向量的加法减法&#xff1a; 3.2 向量的数乘&#xff1a; 拉格朗日乘数法的 基础 公式。 3.3 向量的数量积&a…

conda 导出/导出配置好的虚拟环境

一. 导出环境配置&#xff08;yml文件&#xff09; 1. 在主目录下激活虚拟环境&#xff08;UE4是我的虚拟环境名称&#xff0c;请根据你自己的名称进行修改&#xff09; conda activate UE4 2. 运行此代码 conda env export > environment.yml 二. 导入环境配置&#xf…

oracle官网下载早期jdk版本

Java Downloads | Oracle JDK Builds from Oracle 以上压缩版&#xff0c;以下安装版 Java Downloads | Oracle 该链接往下拉能看到jdk8和jdk11的安装版 -- end

每日一题 — 移动零

力扣链接&#xff1a;283. 移动零 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a;利用双指针将数组分为三个区间&#xff0c;三个区间分别表示的是&#xff1a;非0元素、0、待处理元素 当arr[cur] ! 0时 [0,dest]区间就需要加一&#xff0c;所以dest 然后再交换a…

Java SpringBoot 获取 yml properties 自定义配置信息

Java SpringBoot 获取 yml properties 自定义配置信息 application.yml server:port: 9090servlet:context-path: /app第一种方法 HelloController package com.zhong.demo01.controller;import org.springframework.beans.factory.annotation.Value; import org.springfram…

Python字符串切片操作原来这么简单!

字符串切片是Python中用于从字符串中提取子串的强大工具。通过指定开始和结束下标&#xff0c;以及可选的步长参数&#xff0c;可以轻松地截取字符串的一部分。 1.字符串切片原理 从字符串中复制指定的一段代码&#xff0c;生成一个新的字符串 2.字符串切片语法 字符串[开始…

2018-02-14 新闻内容爬虫【上学时做论文自己爬新闻数据,原谅我自己懒发的图片】

2018-02-14新闻内容爬虫【上学时做论文自己爬新闻数据&#xff0c;原谅我自己懒发的图片】资源-CSDN文库https://download.csdn.net/download/liuzhuchen/88878591爬虫过的站点&#xff1a; 1QQ新闻 1&#xff0c;准备爬取滚动新闻页面 2 通过F12 开发工具查找发现&#xff…

Linux配置JDk环境

下载jdk 官网地址&#xff1a;Java Downloads | Oracle 将下载的jdk上传到服务器 不管你使用什么工具和方法&#xff0c;只要将安装包上传到服务器就可以了 解压文件 tar -xvf jdk-8u341-linux-x64.tar.gz 配置环境变量 用vim /etc/profile进入编辑状态&#xff0c;加入下边…

python中的数字类型Number

Python 数字(Number) Python 数字数据类型用于存储数值。 数据类型是不允许改变的&#xff0c;这就意味着如果改变数字数据类型的值&#xff0c;将重新分配内存空间。 以下实例在变量赋值时 Number 对象将被创建&#xff1a; var1 1 var2 10Python 支持三种不同的数值类型…

2024年阿里云服务器新购、续费、升级优惠政策汇集!

2024年阿里云服务器购买、续费、升级优惠政策整理&#xff0c;阿里云服务器优惠价格表&#xff1a;轻量2核2G3M服务器61元一年、2核4G4M带宽165元1年&#xff0c;云服务器4核16G10M带宽26元1个月、149元半年&#xff0c;阿里云ECS云服务器2核2G3M新老用户均可99元一年续费不涨价…

【开源】使用opencv进行交互式抠图,让你开发效率翻倍

这是一个简单的交互式图像分割应用程序&#xff0c;由python opencv和pyqt编写。 这个应用程序在opencv中应用Grabcut算法对图像进行抠图。Grabcut是Graphcut算法的改进版本。查看这些论文(paper1, paper2)了解详细信息~~ gui部分主要来自这个伟大的工作labelImg。这是一个非常…

【报错处理】命令提示符安装npm install yarn -g失败两种解决方法

目录 方法1&#xff1a;方法2&#xff1a; 方法1&#xff1a; 等待两分钟后发现总是显示如下报错&#xff1a; 在搜索栏搜索命令提示符&#xff0c;将命令提示符以管理员身份运行&#xff0c;如果弹出窗口选择“是”。 3.接着输入命令&#xff0c;结果还是报错&#xff0c;方…

【手机端测试】adb基础命令

一、什么是adb adb&#xff08;Android Debug Bridge&#xff09;是android sdk的一个工具 adb是用来连接安卓手机和PC端的桥梁&#xff0c;要有adb作为二者之间的维系&#xff0c;才能让用户在电脑上对手机进行全面的操作。 Android的初衷是用adb这样的一个工具来协助开发人…

某电力铁塔安全监测预警系统案例分享

项目概述 电力铁塔是承载电力供应的重要设施&#xff0c;它的安全性需要得到可靠的保障。但是铁塔一般安装在户外&#xff0c;分布广泛&#xff0c;且有很多安装在偏远地区&#xff0c;容易受到自然、人力的影响和破环。因此需要使用辅助的方法实时监控铁塔的安全状态&#xff…

(十八)devops持续集成开发——使用docker安装部署jenkins服务

前言 本节内容介绍如何使用docker容器来部署安装jenkins流水线服务。关于docker容器的安装本节内容不做介绍。请读者提前安装。 正文 ①使用docker查找jenkins官方镜像 ② 拉取jenkins官方镜像jenkins/jenkins&#xff0c;选择一个最新稳定版本&#xff0c;避免一些插件不兼…

ThreadLocal 使用示例

ThreadLocal 使用示例 public class BaseContext {private static ThreadLocal<Long> threadLocal new ThreadLocal<>();/*** 设置值* param id*/public static void setCurrentId(Long id){threadLocal.set(id);}/*** 获取值* return*/public static Long getCur…

LabVIEW光伏逆变器低电压穿越能力测试

LabVIEW光伏逆变器低电压穿越能力测试 随着光伏发电技术的迅速发展&#xff0c;光伏逆变器的低电压穿越&#xff08;LVRT&#xff09;能力日益成为影响电网稳定性的关键因素。为了提升光伏逆变器的并网性能&#xff0c;开发了一套基于LabVIEW的光伏逆变器LVRT测试系统。该系统…

数据结构知识点总结-绪论 数据结构基本术语 算法及评价

要求 (1)对数据结构这么课学了哪些知识有个清楚的认知; (2)掌握目录结构,能复述出来每个知识点下都有哪些内容。 如下图所示,可自行制作思维导图,针对自己薄弱的地方进行复习。 绪论 相关术语 绪论中会介绍数据结构的一些基本概念,要对数据模型有基本的了解。 数据…

简单流程引擎实现

技术栈 vue3gojsprimevuelodashc#sql server 效果展示 参考文档 初学者笔记 官网demo 实现思路 运用官方demo&#xff0c;将gojs的数据结构转换为需要的数据库记录实际数据分为两部分&#xff1a;存储gojs节点、节点关系数据&#xff1b;实际业务数据。gojs数据负责存储流…

VSCode远程开发 Windows11 Linux

问题背景 之前一直用JetBrains的Gateway和本地Linux虚拟机开发&#xff0c;不过笔记本配置不够&#xff0c;太卡了。最近租了个国外的便宜服务器&#xff0c;JetBrains的Gateway总断连&#xff0c;也不知道为什么&#xff0c;所以试试VSCode。 本地 Windows 11 &#xff0c;远…