设计模式之观察者(Observer)

事件处理模型

小朋友睡醒了就哭,饿

写程序模拟这个过程:
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不存在往回传的情况 --> 责任链部分讲到

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Mabitys总结

一、ORM ORM(Object/Relation Mapping)&#xff0c;中文名称&#xff1a;对象/关系 映射。是一种解决数据库发展和面向对象编程语言发展不匹配问题而出现的技术。 使用JDBC技术时&#xff0c;手动实现ORM映射&#xff1a; 使用ORM时&#xff0c;自动关系映射&#xff1a; &am…

动态表单获取某一项值

<template><div><el-form :model"form" :rules"rules" ref"form"><el-row v-for"(item,index) in form.list" :key"index"><el-col :span"6"><el-form-item label"用户名称…

URI参数在Netty WebSocket中的处理方式,你了解吗?

URI参数传递 概述起因方法1方法2pom 主页传送门&#xff1a;&#x1f4c0; 传送 概述 WebSocket 是 HTML5 中实现实时通信的一种技术&#xff0c;它建立在 HTTP 协议上&#xff0c;但与传统的 HTTP 请求不同&#xff0c;WebSocket 连接一旦建立&#xff0c;就可以在客户端和服…

选择 REST ,还是 GraphQL

在本文中&#xff0c;简单比较 REST 和 GraphQL 的优点和缺点&#xff0c;以便您可以决定哪种 API 架构最适合您的项目 当我们要创建数据驱动的 Web 或移动应用程序&#xff0c;需要开发后台 API&#xff0c;通过它可以从后端服务器来访问或操作数据。目前最流行的 API 架构是…

Flutter的专属Skia引擎解析+用法原理

Skia是一款跨平台的2D图形库&#xff0c;是Google公司开发的&#xff0c;可以用于开发各种应用程序&#xff0c;如浏览器、游戏、移动应用程序等。Skia引擎的主要特点是速度快、可移植性强、占用的内存少、稳定性佳&#xff0c;适用于多种硬件平台。 Skia的目标是提供快速、高…

在Spring Boot中使用MyBatis访问数据库

MyBatis&#xff0c;这个对各位使用Java开发的开发者来说还是蛮重要的&#xff0c;我相信诸位在企业开发项目的时候&#xff0c;大多数采用的是Mybatis。使用MyBatis帮助我们解决各种问题&#xff0c;实际上这篇文章&#xff0c;基本上默认为可以跳过的一篇&#xff0c;但是为了…

什么是代理IP池?如何判断IP池优劣?

代理池充当多个代理服务器的存储库&#xff0c;提供在线安全和匿名层。代理池允许用户抓取数据、访问受限制的内容以及执行其他在线任务&#xff0c;而无需担心被检测或阻止的风险。代理池为各种在线活动&#xff08;例如网页抓取、安全浏览等&#xff09;提高后勤保障。 读完…

使用Python的requests库采集充电桩LBS位置经纬度信息

目录 一、引言 二、采集数据的流程 1、获取充电桩的URL地址 2、发送HTTP请求获取数据 3、解析数据获取经纬度信息 4、存储数据 三、代码实现 四、注意事项和优化建议 五、充电桩数据的后续利用 六、总结 一、引言 随着电动汽车的普及&#xff0c;充电设施的建设也日…

【JavaEESpring】Spring Web MVC⼊⻔

Spring Web MVC 1. 什么是 Spring Web MVC1.1 什么是 MVC ?1.2 是什么 Spring MVC? 2. 学习 Spring MVC2.1 建立连接2.2 请求2.3 响应 3. 相关代码链接 1. 什么是 Spring Web MVC 官⽅对于 Spring MVC 的描述是这样的&#xff1a; 1.1 什么是 MVC ? MVC 是 Model View C…

OpenCV实现手势虚拟拖拽

前言&#xff1a; Hello大家好&#xff0c;我是Dream。 今天来学习一下如何使用OpenCV实现手势虚拟拖拽&#xff0c;欢迎大家一起前来探讨学习~ 一、主要步骤及库的功能介绍 1.主要步骤 要实现本次实验&#xff0c;主要步骤如下&#xff1a; 导入OpenCV库。通过OpenCV读取摄…

中文编程软件视频推荐,自学编程电脑推荐,中文编程开发语言工具下载

中文编程软件视频推荐&#xff0c;自学编程电脑推荐&#xff0c;中文编程开发语言工具下载 给大家分享一款中文编程工具&#xff0c;零基础轻松学编程&#xff0c;不需英语基础&#xff0c;编程工具可下载。 这款工具不但可以连接部分硬件&#xff0c;而且可以开发大型的软件…

使用create-react-app脚手架创建react项目

查看npx版本&#xff1a; npx -v使用如下命令创建项目时&#xff1a; npx create-react-app demo报错&#xff1a; 解决&#xff1a; 以管理员身份运行cmd 然后再次创建项目&#xff0c;又报错&#xff1a; 经查得知&#xff1a;发生此错误是因为用户名中有空格&#xff0c;…

商城免费搭建之java商城 开源java电子商务Spring Cloud+Spring Boot+mybatis+MQ+VR全景+b2b2c

1. 涉及平台 平台管理、商家端&#xff08;PC端、手机端&#xff09;、买家平台&#xff08;H5/公众号、小程序、APP端&#xff08;IOS/Android&#xff09;、微服务平台&#xff08;业务服务&#xff09; 2. 核心架构 Spring Cloud、Spring Boot、Mybatis、Redis 3. 前端框架…

IPv6详解

目录: 第一部分 IPv6的诞生背景和引起的主要变化 第二部分 IPv6数据报的基本首部和扩展首部 第三部分 IPv6地址 第四部分 IPv4向IPv6过渡 第一部分 IPv6的诞生背景和引起的主要变化 一.IPv6的诞生背景 IPv4存在设计缺陷: IPv4的设计者最初并没有想到该协议会在全球范围内广…

NFS文件系统共享服务器实战

架设一台NFS服务器&#xff0c;并按照以下要求配置 准备 两台Linux虚拟机一台作为服务端server&#xff0c;一台作为客户端client server IPV4&#xff1a;192.168.110.136/24 client IPV4&#xff1a;192.168.110.134/24 两台服务器都需要关闭防火墙和seLinux 服…

element-ui中el-table数据合并行和列,应该怎么解决

最近接到一个任务,要实现一个数据报表,涉及到很多合并问题,一开始想着原生会简单点,实际上很麻烦,最后还是用elemen-ui中table自带的合并方法. 最终的效果是要做成这种:1.数据处理,后端返回来的数据是,一个大对象,包含三个数组,既然合并,肯定是要处理成一个数组,并且要把相同的…

Zabbix之部署代理服务器

一、部署代理服务器 分布式监控的作用 分担server的几种式压力解决多机房之间的网络延时问题 1、搭建proxy主机 1.1 关闭防火墙&#xff0c;修改主机名 systemctl disbale --now firewalld setenforce 0 hostnamectl set-hostname zbx-proxy su1.2 设置zabbix下载源 rpm …

FreeRTOS源码阅读笔记2--list.c

list.c中主要完成列表数据结构的操作&#xff0c;有列表和列表项的初始化、列表的插入和移除。 2.1列表初始化vListInitialise() 2.1.1函数原型 void vListInitialise( List_t * const pxList ) pxList&#xff1a;列表指针&#xff0c;指向要初始化的列表。 2.1.2函数框架…

执行mysql-community-libs-8.1.0-1.el8.x86_64.rpm报错依赖检测失败

目录 1.错误信息 2.解决方法 1.错误信息 我是在VMware虚拟机18上的Centos8上的&#xff0c;安装MySQL8.1.0&#xff0c;执行mysql-community-libs-8.1.0-1.el8.x86_64.rpm报错 [rootlocalhost mysql8.1.0]# rpm -ivh mysql-community-libs-8.1.0-1.el8.x86_64.rpm 警告&…

SpringBoot前后端分离整合cas(客户端)

SpringBoot前后端分离整合cas&#xff08;客户端&#xff09; cas认证详细流程&#xff1a; 前后端分离&#xff1a;项目前端使用nginx启动&#xff0c;后端是springBoot服务&#xff1b; nginx可以统一管理Cookie&#xff0c;避免出现跨域问题。 添加依赖 <dependency&g…