设计模式之观察者(Observer)

news2025/1/20 14:58:43

事件处理模型

小朋友睡醒了就哭,饿

写程序模拟这个过程:
v1:最简单的就是写程序一直观察着,什么时候哭了就进行处理

/**
 * 披着面向对象外衣的面向过程
 */
public class Main1 {
    public static void main(String[] args) {
        boolean cry = false;

        while(!cry) {
            //进行处理
        }
    }
}

v2: 面向对象,至少抽象出child类来:

/**
 * 面向对象的傻等
 * 一直观察,直到有线程调用了wakeUp方法
 * 程序没写完,涉及到线程同步,没有写完
 */

class Child {
    private boolean cry = false;

    public boolean isCry() {
        return cry;
    }

    public void wakeUp() {
        System.out.println("Waked Up! Crying wuwuwuwu...");
        cry = true;
    }
}

public class Main {
    public static void main(String[] args) {
        Child child = new Child();
        while(!child.isCry()) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("observing...");
        }

    }
}

v3: 加入观察者Dad到被观察者Child里面,什么时候醒了,直接调用观察者的方法

/**
 * 加入观察者
 */

class Child {
    private boolean cry = false;
    private Dad d = new Dad(); //加入观察者Dad到被观察者Child里面

    public boolean isCry() {
        return cry;
    }

    public void wakeUp() {
        cry = true;
        d.feed(); // 直接调用观察者的方法
    }
}

class Dad {
    public void feed() {
        System.out.println("dad feeding...");
    }
}

public class Main {
    public static void main(String[] args) {
        Child c = new Child();
        //do sth
        c.wakeUp();
    }
}

v4:加入多个观察者,当一个事件发生的时候,每个观察者的处理方式不同

/**
 * 加入多个观察者
 */

class Child {
    private boolean cry = false;
    // 多个观察者
    private Dad dad = new Dad();
    private Mum mum = new Mum();
    private Dog dog = new Dog();


    public boolean isCry() {
        return cry;
    }

    public void wakeUp() {
        cry = true;
        // 每个观察者的处理方式不同
        dad.feed();
        dog.wang();
        mum.hug();
    }
}

class Dad {
    public void feed() {
        System.out.println("dad feeding...");
    }
}

class Mum {
    public void hug() {
        System.out.println("mum hugging...");
    }
}

耦合度太高,观察者的处理方法不一定只适合当前的被观察者,例如小狗汪汪叫这个出来方法,可能也适合于鸡叫的事件
而且添加新的观察者比较麻烦 --> 扩展性不好,耦合度高

v5:分离观察者与被观察者。定义一个Observer接口,里面有actionWeakup方法,指的是小孩醒了之后要采取的动作,不同的观察者实现这Observer个接口

/**
 * 分离观察者与被观察者
 */
class Child {
    private boolean cry = false;
    // 将观察者放到list中
    private List<Observer> observers = new ArrayList<>();

    {
        observers.add(new Dad());
        observers.add(new Mum());
        observers.add(new Dog());
    }


    public boolean isCry() {
        return cry;
    }

    public void wakeUp() {
        cry = true;
        // 当wakeup事件发生的时候,遍历观察者进行处理
        for(Observer o : observers) {
            o.actionOnWakeUp();
        }
    }
}

// 定义一个Observer接口
interface Observer {
    void actionOnWakeUp();
}

//不同的观察者实现这个Observer接口  
class Dad implements Observer {
    public void feed() {
        System.out.println("dad feeding...");
    }

    @Override
    public void actionOnWakeUp() {
        feed();
    }
}

class Mum implements Observer {
    public void hug() {
        System.out.println("mum hugging...");
    }

    @Override
    public void actionOnWakeUp() {
        hug();
    }
}

class Dog implements Observer {
    public void wang() {
        System.out.println("dog wang...");
    }

    @Override
    public void actionOnWakeUp() {
        wang();
    }
}

public class Main {
    public static void main(String[] args) {
        Child c = new Child();
        //do sth
        c.wakeUp();
    }
}

一个事件发生,会经过一系列的观察者的处理,这个事件才算完成

v5:一个事件发生之后,观察者需要根据事件的具体情况,做出处理。例如小孩哭的厉不厉害,时间等
该怎么把小孩哭的事件的具体情况传递给观察者呢?
封装一个事件类,事件的所有情况都在事件里面:

/**
 * 有很多时候,观察者需要根据事件的具体情况来进行处理
 */

class Child {
    private boolean cry = false;
    private List<Observer> observers = new ArrayList<>();

    {
        observers.add(new Dad());
        observers.add(new Mum());
        observers.add(new Dog());
    }


    public boolean isCry() {
        return cry;
    }

    public void wakeUp() {
        cry = true;
        // 醒了之后,创建一个事件对象
        wakeUpEvent event = new wakeUpEvent(System.currentTimeMillis(), "bed");

        // 事件对象传递给观察者
        for(Observer o : observers) {
            o.actionOnWakeUp(event);
        }
    }
}

//事件类 fire Event
class wakeUpEvent{
    long timestamp;
    String loc;

    public wakeUpEvent(long timestamp, String loc) {
        this.timestamp = timestamp;
        this.loc = loc;
    }
}

// 事件的所有情况都在事件里面,封装好传给观察者
interface Observer {
    void actionOnWakeUp(wakeUpEvent event);
}

// Observer里面可以写if,判断event的情况,来进行处理,这块代码里面为了简便就没写
class Dad implements Observer {
    public void feed() {
        System.out.println("dad feeding...");
    }

    @Override
    public void actionOnWakeUp(wakeUpEvent event) {
        feed();
    }
}

class Mum implements Observer {
    public void hug() {
        System.out.println("mum hugging...");
    }

    @Override
    public void actionOnWakeUp(wakeUpEvent event) {
        hug();
    }
}

class Dog implements Observer {
    public void wang() {
        System.out.println("dog wang...");
    }

    @Override
    public void actionOnWakeUp(wakeUpEvent event) {
        wang();
    }
}

public class Main {
    public static void main(String[] args) {
        Child c = new Child();
        //do sth
        c.wakeUp();
    }
}

观察者模式三要素:
Source事件源对象 --> 是谁发出的事件
Observer观察者 --> 是谁在等着这个事件
Event事件 --> 事件本身
事件源对象有一堆的观察者观察着它,当事件源对象发出事件的时候,观察者会对事件进行处理
在这里插入图片描述

V7:一个事件源可以发出不同的事件,一个事件也可以被多个观察者处理,大多数时候,我们处理事件的时候,需要事件源对象
为什么需要事件源对象:因为有时候需要根据事件源对象的不同,采用不同的处理方法。
Sping的AOP其实可以语义上理解成观察者模式,在程序执行的过程中,弄一个切面,当代码执行到这个切面的时候,先执行指定好的一系列方法,
然后继续往下,这就相当于观察者模式,观察着这个切面,当到达这个切面的时候,先执行一系列定义好的方法

/**
 * 有很多时候,观察者需要根据事件的具体情况来进行处理
 * 大多数时候,我们处理事件的时候,需要事件源对象
 */

class Child {
    private boolean cry = false;
    private List<Observer> observers = new ArrayList<>();

    {
        observers.add(new Dad());
        observers.add(new Mum());
        observers.add(new Dog());
    }


    public boolean isCry() {
        return cry;
    }

    public void wakeUp() {
        cry = true;

        wakeUpEvent event = new wakeUpEvent(System.currentTimeMillis(), "bed", this);

        for(Observer o : observers) {
            o.actionOnWakeUp(event);
        }
    }
}

class wakeUpEvent{
    long timestamp;
    String loc;
    // 将事件源对象,绑定到事件本身
    Child source;

    public wakeUpEvent(long timestamp, String loc, Child source) {
        this.timestamp = timestamp;
        this.loc = loc;
        this.source = source;
    }

    // 往往事件对象都会有一个getSource方法,例如系统提供的WindowEvent
    public Child getSource() {
        return this.source;
    }
}

interface Observer {
    void actionOnWakeUp(wakeUpEvent event);
}

class Dad implements Observer {
    public void feed() {
        System.out.println("dad feeding...");
    }

    @Override
    public void actionOnWakeUp(wakeUpEvent event) {
        feed();
    }
}

class Mum implements Observer {
    public void hug() {
        System.out.println("mum hugging...");
    }

    @Override
    public void actionOnWakeUp(wakeUpEvent event) {
        hug();
    }
}

class Dog implements Observer {
    public void wang() {
        System.out.println("dog wang...");
    }

    @Override
    public void actionOnWakeUp(wakeUpEvent event) {
        wang();
    }
}

public class Main {
    public static void main(String[] args) {
        Child c = new Child();
        //do sth
        c.wakeUp();
    }
}

v8:java里面的事件继承自EventObject这个类。
这里也自己定义了一个事件类,里面有getSource方法
钩子函数,hook,callback,listener都是观察者模式,都是一回事

/**
 * 有很多时候,观察者需要根据事件的具体情况来进行处理
 * 大多数时候,我们处理事件的时候,需要事件源对象
 * 事件也可以形成继承体系
 */
class Child {
    private boolean cry = false;
    private List<Observer> observers = new ArrayList<>();

    {
        observers.add(new Dad());
        observers.add(new Mum());
        observers.add(new Dog());
        // 钩子函数hook,钩在那里,当事件发生的时候,钩子函数被执行
        // 钩子函数就是Observer模式
        // 传过去的是个函数
        observers.add((e)->{
            System.out.println("ppp");
        });
        //hook callback function
    }

    public boolean isCry() {
        return cry;
    }

    public void wakeUp() {
        cry = true;

        wakeUpEvent event = new wakeUpEvent(System.currentTimeMillis(), "bed", this);

        for(Observer o : observers) {
            o.actionOnWakeUp(event);
        }
    }
}
// 定义抽象Event方法,里面有getSource方法
abstract class Event<T> {
    abstract T getSource();
}

class wakeUpEvent extends Event<Child>{
    long timestamp;
    String loc;
    Child source;

    public wakeUpEvent(long timestamp, String loc, Child source) {
        this.timestamp = timestamp;
        this.loc = loc;
        this.source = source;
    }

    @Override
    Child getSource() {
        return source;
    }
}

interface Observer {
    void actionOnWakeUp(wakeUpEvent event);
}

class Dad implements Observer {
    public void feed() {
        System.out.println("dad feeding...");
    }

    @Override
    public void actionOnWakeUp(wakeUpEvent event) {
        feed();
    }
}

class Mum implements Observer {
    public void hug() {
        System.out.println("mum hugging...");
    }

    @Override
    public void actionOnWakeUp(wakeUpEvent event) {
        hug();
    }
}

class Dog implements Observer {
    public void wang() {
        System.out.println("dog wang...");
    }

    @Override
    public void actionOnWakeUp(wakeUpEvent event) {
        wang();
    }
}

public class Main {
    public static void main(String[] args) {
        Child c = new Child();
        //do sth
        c.wakeUp();
    }
}

v9:java jwt里面用了观察者模式,点击button,所有的观察者listener会被触发

public class TestFrame extends Frame {
	public void launch() {
		Button b = new Button("press me");
		b.addActionListener(new MyActionListener());
		b.addActionListener(new MyActionListener2());
		this.add(b);
		this.pack();
		
		this.addWindowListener(new WindowAdapter(){
			@Override
			public void windowClosing(WindowEvent e) {
				System.exit(0);
			}
		});
		this.setLocation(400, 400);
		this.setVisible(true);
	}
	
	public static void main(String[] args) {
		new TestFrame().launch();
	}
	
	private class MyActionListener implements ActionListener { //Observer
		public void actionPerformed(ActionEvent e) {
			((Button)e.getSource()).setLabel("press me again!");
			System.out.println("button pressed!");
		}
		
	}
	
	private class MyActionListener2 implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			System.out.println("button pressed 2!");
		}
		
	}
}

jvm运行着,监听着操作系统的键盘事件,当按键发生的时候,jvm会创建好KeyEvent事件

js里面也有event对象,事件源对象是event.traget。当按下button的时候,会自动生成event对象,把事件源对象传过去

在很多系统中,Observer模式往往和责任链共同负责对于事件的处理,其中的某一个observer负责是否将事件进一步传递
Observer不存在往回传的情况 --> 责任链部分讲到

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

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

相关文章

print(torch.cuda.is_available()) False如何解决?GTX3090

首先介绍环境&#xff1a; 保证Cuda与Pytorch的版本对齐就可以了。 nvcc -V 查看原来装的是cuda11.3版本 去Pytorch官网找到相应指令下载即可&#xff1a; CtrlF&#xff1a;cuda11.3 就在诸多版本中找到啦,一定找 torch的版本cuda版本。我之前错误安装的torch&#xff0c;只…

小程序游戏对接广告收益微信小游戏抖音游戏软件

小程序游戏对接广告是一种常见的游戏开发模式&#xff0c;开发者可以通过在游戏中嵌入广告来获取收益。以下是一些与小程序游戏对接广告收益相关的关键信息&#xff1a; 小程序游戏广告平台选择&#xff1a; 选择适合你的小程序游戏的广告平台非常重要。不同的平台提供不同类型…

【Python基础】 模块和包的创建及使用(windows 下制作和发布压缩包超详细)

模块和包的创建及使用 1.模块1.1模块的概念1.2 模块的两种导入方式1.3 模块的搜索顺序[扩展]1.4 原则—— 每一个文件都应该是可以被导入的 2.Package-包2.1制作和发布压缩包2.2包的安装 1.模块 1.1模块的概念 模块是Python 程序架构的一个核心概念 • 每一个以扩展名py 结尾…

茶楼计时茶室时钟计费系统,佳易王共享茶室收银计时收费管理系统软件下载

茶楼计时茶室时钟计费系统&#xff0c;佳易王共享茶室收银计时收费管理系统软件下载 软件功能&#xff1a; 1、计时计费功能&#xff1a;可以按单价计费&#xff0c;可以按时间段计费。时间显示直观&#xff0c;每个桌子用时一目了然。每个桌子价格可以设置相同也可以不相同。…

卡码网语言基础课 |句子缩写

卡码网语言基础课 &#xff5c;句子缩写 字符大小的比较题目分析判断大小写字母与转换为大写字母正确检测词语而非空格 代码实现函数的使用形参和实参引用 字符大小的比较 字符串是一个个字符组合而成的&#xff0c;比如字符串"hello"&#xff0c;是由字符(char)类型…

centos 7.9系统安装老版本jenkins,并解决插件问题

1.初衷 因为jenkins随着时间推移&#xff0c;其版本也越来越新&#xff0c;支持它运行的JDK也越来越新。基于不折腾的目标&#xff0c;我们安装一个老的固定版本就行。以前安装新版本&#xff0c;经常碰到的问题就是插件安装不兼容的问题。现在这个问题&#xff0c;可以把以前…

001. 变量、环境变量

1、在终端中显示输出 shell脚本通常以shebang起始&#xff1a;#&#xff01;/bin/bash/ shebang是一个文本行&#xff0c;其中#!位于解释器路径之前。/bin/bash是Bash的解释器命令路径。bash将以#符号开头的行视为注释。脚本中只有第一行可以使用shebang来定义解释该脚本所使…

[工业自动化-5]:西门子S7-15xxx编程 - PLC系统初识别 :PLC概述与发展史

目录 前言&#xff1a; 一、PLC的由来&#xff1a;自动化产线的大脑 二、PLC发展史 三、常见的PLC厂家&#xff1a;欧洲日本 四、PLC VS 电脑 4.1 PLC VS CPU 4.2 PLC VS 单片机 4.3 PLC VS 工控机 五、PLC系统组成 参考&#xff1a; 前言&#xff1a; 一、PLC的由来…

黑客(网络安全)技术——高效自学

前言 前几天发布了一篇 网络安全&#xff08;黑客&#xff09;自学 没想到收到了许多人的私信想要学习网安黑客技术&#xff01;却不知道从哪里开始学起&#xff01;怎么学 今天给大家分享一下&#xff0c;很多人上来就说想学习黑客&#xff0c;但是连方向都没搞清楚就开始学习…

PTA_乙级_1002

思路&#xff1a;不仅超出int还超出Longlong,直接用string类型定义n&#xff0c;for循环来遍历每一位字符然后转换成数字进行累加&#xff0c;再用to_string把数字和转换成字符串&#xff0c;再用for循环把数字和的每一位定位到pinyin字符串数组上输出 #include <iostream&…

【C++】AVL树的4中旋转调整

文章目录 前提一、AVL树的结构定义二、AVL的插入&#xff08;重点&#xff09;1. 插入的结点在较高左子树的左侧&#xff08;右单旋&#xff09;2. 新节点插入较高右子树的右侧&#xff08;左单旋&#xff09;3.新结点插入较高右子树的左侧&#xff08;先右单旋再左单旋&#x…

MFC-TCP网络编程服务端-Socket

目录 1、通过Socket建立服务端&#xff1a; 2、UI设计&#xff1a; 3、代码的实现&#xff1a; &#xff08;1&#xff09;、CListenSocket类 &#xff08;2&#xff09;、CConnectSocket类 &#xff08;3&#xff09;、CTcpServerDlg类 1、通过Socket建立服务端&#xff…

ts面试题总结

文章目录 前言ts和js的区别&#xff1f;什么是Typescript的方法重载&#xff1f;Typescript中never 和 void 的区别&#xff1f;typescript 中的 is 关键字有什么用&#xff1f;TypeScript支持的访问修饰符有哪些&#xff1f;如何定义一个数组&#xff0c;它的元素可能是字符串…

11-2 mybatis入门细节

mybatis Mybatis 单表CURD细节 ${} 与#{} 区别(面试题) ${} 拼接sql 造成sql注入 #{} 使用?占位 如果作为值, 推荐使用#{} ${} 实现一些动态排序,使用 #{column} select * from tb_userinfo order by ? desc column: id 赋值 sql: select * from tb_userinfo order by id …

AIGC,ChatGPT 快速批量处理Word文本内容

在文档编辑与创作的过程中,会避免不了,输入错误内容与打错字的情况。 如果我们一个一个手动去修改,会比较费时间。 如下: 进行内容修改与更新的时候,我们知道可以使用Ctrl+H 来查找与替换,但查找与替换一次也只能替换一个值。

博捷芯BJCORE:划片机在划切工艺中需要注意以下几点

划片机在划切工艺中需要注意以下几点&#xff1a; 1. 测高时工作台上不能有任何物品&#xff0c;以免影响测高精度。 2. 切割前检查参数是否正确选择&#xff0c;包括切割速度、切割深度等。 3. 更换刀片时&#xff0c;检查刀片是否平稳旋转&#xff0c;确保刀片安装牢固。 …

开发知识点-stm32/ESP32/Mega2560嵌入式设计

嵌入式设计 STM32四轴飞行器原理图解析小马哥 DragonFly四轴软件开发 13 STM32 SPI总线通讯SPI 总线协议简介SPI 物理层SPI 协议层SPI 通信时序 STM32硬件SPI接口简介SPI接口 利用库函数初始化配置 ESP32 “F:\res\marlin-2.0.x” “F:\res\Marlin-2.1.2” STM32四轴飞行器 小…

深度学习中的“钩子“(Hook):基于pytorch实现了简单例子

目录 基本概念一个详细的示例 基于resnet50的一个hook应用例子前向传播示例反向传播示例 基本概念 在深度学习中&#xff0c;“钩子”&#xff08;Hook&#xff09;是一种机制&#xff0c;可以在神经网络的不同层或模块中插入自定义的代码&#xff0c;以便在网络的前向传播或反…

python开发数字人助理版

Fay数字人助理版是fay开源项目的重要分支&#xff0c;专注于构建智能数字助理的开源解决方案。它提供了灵活的模块化设计&#xff0c;使开发人员能够定制和组合各种功能模块&#xff0c;包括情绪分析、NLP处理、语音合成和语音输出等。Fay数字人助理版为开发人员提供了强大的工…

第三章《补基础:不怕学不懂概率统计》笔记

3.1 什么是概率 概率亦称“或然率”&#xff0c;它反映随机事件出现的可能性大小&#xff0c;在现实生活中有着极其普遍的应用。 3.1.1 最简单的概率的例子 3.1.2 概率论与数理统计的关系 概率论与数理统计的关系可以概括为&#xff0c;概率论是数理统计的理论基础&#xf…