【设计模式】观察者设计模式解析

news2024/11/17 19:51:26

目录

一、观察者模式定义

 二、观察者模式角色

三、观察者模式类图

 四、观察者模式实例

五、观察者模式优缺点

5.1、优点

5.2、缺点

六、观察者模式应用

6.1、Spring 中观察者模式的四个角色

6.2、coding~~~~~~


一、观察者模式定义

观察者模式(Observer Pattern): 定义对象间一种一对多的依赖关系,使得当每一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。

观察者模式是一种对象行为型模式

        观察者模式的别名包括发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。

细究的话,发布订阅和观察者有些不同,可以理解成发布订阅模式属于广义上的观察者模式。

 二、观察者模式角色

  • Subject(目标):被观察者,它是指被观察的对象。 从类图中可以看到,类中有一个用来存放观察者对象的Vector 容器(之所以使用Vector而不使用List,是因为多线程操作时,Vector在是安全的,而List则是不安全的),这个Vector容器是被观察者类的核心,另外还有三个方法:attach方法是向这个容器中添加观察者对象;detach方法是从容器中移除观察者对象;notify方法是依次调用观察者对象的对应方法。这个角色可以是接口,也可以是抽象类或者具体的类,因为很多情况下会与其他的模式混用,所以使用抽象类的情况比较多。

  • ConcreteSubject(具体目标):具体目标是目标类的子类,通常它包含经常发生改变的数据,当它的状态发生改变时,向它的各个观察者发出通知。同时它还实现了在目标类中定义的抽象业务逻辑方法(如果有的话)。如果无须扩展目标类,则具体目标类可以省略。

  • Observer(观察者):观察者将对观察目标的改变做出反应,观察者一般定义为接口,该接口声明了更新数据的方法 update(),因此又称为抽象观察者

  • ConcreteObserver(具体观察者):在具体观察者中维护一个指向具体目标对象的引用,它存储具体观察者的有关状态,这些状态需要和具体目标的状态保持一致;它实现了在抽象观察者 Observer 中定义的 update()方法。通常在实现时,可以调用具体目标类的 attach() 方法将自己添加到目标类的集合中或通过 detach() 方法将自己从目标类的集合中删除。

三、观察者模式类图

 四、观察者模式实例

1、定义观察者接口

interface Observer {
    public void update();
}

2、定义被观察者

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);
    }
    protected void notifyObserver(){
        for(Observer o: obs){
            o.update();
        }
    }
    public abstract void doSomething();
}

3、具体的被观察者

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

4、具体的被观察者

class ConcreteObserver1 implements Observer {
    public void update() {
        System.out.println("观察者1收到信息,并进行处理");
    }
}
class ConcreteObserver2 implements Observer {
    public void update() {
        System.out.println("观察者2收到信息,并进行处理");
    }
}

5、客户端

public class Client {
    public static void main(String[] args){
        Subject sub = new ConcreteSubject();
        sub.addObserver(new ConcreteObserver1()); //添加观察者1
        sub.addObserver(new ConcreteObserver2()); //添加观察者2
        sub.doSomething();
    }
}

输出

被观察者事件发生改变
观察者1收到信息,并进行处理
观察者2收到信息,并进行处理

        通过运行结果可以看到,我们只调用了 Subject 的方法,但同时两个观察者的相关方法都被调用了。仔细看一下代码,其实很简单,就是在 Subject 类中关联一下 Observer 类,并且在 doSomething() 方法中遍历一下 Observerupdate() 方法就行了。

五、观察者模式优缺点

5.1、优点

  • 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系
  • 目标与观察者之间建立了一套触发机制
  • 支持广播通信
  • 符合“开闭原则”的要求

5.2、缺点

  • 主题与观察者之间的依赖关系并没有完全解除。
  • 当观察者对象很多时,通知的发布会花费很长时间,影响程序效率。
  • 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
  • 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。

六、观察者模式应用

1、JDK中的观察者模式

        观察者模式在 Java 语言中的地位非常重要。在 JDK 的 java.util 包中,提供了 Observable 类以及 Observer 接口,它们构成了 JDK 对观察者模式的支持(可以去查看下源码,写的比较严谨)。but,在 Java9 被弃用了。

2、Spring 中的观察者模式

        Spring 事件驱动模型也是观察者模式很经典的应用。就是我们常见的项目中最常见的事件监听器。

6.1、Spring 中观察者模式的四个角色

  1. 事件:ApplicationEvent 是所有事件对象的父类。ApplicationEvent 继承自 jdk 的 EventObject, 所有的事件都需要继承 ApplicationEvent, 并且通过 source 得到事件源。

    Spring 也为我们提供了很多内置事件,ContextRefreshedEventContextStartedEventContextStoppedEventContextClosedEventRequestHandledEvent

  2. 事件监听:ApplicationListener,也就是观察者,继承自 jdk 的 EventListener,该类中只有一个方法 onApplicationEvent。当监听的事件发生后该方法会被执行。

  3. 事件源:ApplicationContextApplicationContext 是 Spring 中的核心容器,在事件监听中 ApplicationContext 可以作为事件的发布者,也就是事件源。因为 ApplicationContext 继承自 ApplicationEventPublisher。在 ApplicationEventPublisher 中定义了事件发布的方法:publishEvent(Object event)

  4. 事件管理:ApplicationEventMulticaster,用于事件监听器的注册和事件的广播。监听器的注册就是通过它来实现的,它的作用是把 Applicationcontext 发布的 Event 广播给它的监听器列表。

6.2、coding~~~~~~

1、定义事件

public class MyEvent extends ApplicationEvent {
    public MyEvent(Object source) {
        super(source);
        System.out.println("my Event");
    }
}

2、实现事件监听器

@Component
class MyListenerA implements ApplicationListener<MyEvent> {
    public void onApplicationEvent(MyEvent AyEvent) {
        System.out.println("ListenerA received");
    }
}

@Component
class MyListenerB implements ApplicationListener<MyEvent> {
    public void onApplicationEvent(MyEvent AyEvent) {
        System.out.println("ListenerB received");
    }
}

3、事件发布者

@Component
public class MyPublisher implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext=applicationContext;
    }
    
    public void publishEvent(ApplicationEvent event){
        System.out.println("publish event");
        applicationContext.publishEvent(event);
    }
}

4、测试,先用注解方式将 MyPublisher 注入 Spring

@Configuration
@ComponentScan
public class AppConfig {

    @Bean(name = "myPublisher")
    public MyPublisher myPublisher(){
        return new MyPublisher();
    }
}
public class Client {

    @Test
    public void main() {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        MyPublisher myPublisher = (MyPublisher) context.getBean("myPublisher");
        myPublisher.publishEvent(new MyEvent(this));
    }
}

5、输出

my Event
publish event
ListenerA received
ListenerB received

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

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

相关文章

【Unity细节】关于NotImplementedException: The method or operation is not implemented

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 秩沅 原创 收录于专栏&#xff1a;unity细节和bug ⭐关于NotImplementedException: The method or operation is not implemented.⭐…

【MATLAB第61期】基于MATLAB的GMM高斯混合模型回归数据预测

【MATLAB第61期】基于MATLAB的GMM高斯混合模型回归数据预测 高斯混合模型GMM广泛应用于数据挖掘、模式识别、机器学习和统计分析。其中&#xff0c;它们的参数通常由最大似然和EM算法确定。关键思想是使用高斯混合模型对数据&#xff08;包括输入和输出&#xff09;的联合概率…

MFC自定义控件使用

用VS2005新建一个MFC项目,添加一个Custom Control控件在窗体 我们需要为自定义控件添加一个类。项目,添加类,MFC类 设置类名字,基类为CWnd,你也可以选择CDialog作为基类 类创建完成后,在它的构造函数中注册一个新的自定义窗体,取名为"MyWindowClass" WNDCL…

3ds max 烘培世界坐标到贴图/顶点色

设置Diffuse 为ObjectNormal Normalize(objectNormal) * 0.5 0.5 把Diffuse烘培到顶点色 烘培Diffuse到贴图 模型按UV展开 右键复制 &#xff0c; 到mesh上粘贴 烘培到贴图 UE使用 贴图导入为BC7 float3 n ObjectNormal*2-1; return float3(n.x,n.z,n.y); // x ,z ,y

【深度学习Week3】ResNet+ResNeXt

ResNetResNeXt 一、ResNetⅠ.视频学习Ⅱ.论文阅读 二、ResNeXtⅠ.视频学习Ⅱ.论文阅读 三、猫狗大战Lenet网络Resnet网络 四、思考题 一、ResNet Ⅰ.视频学习 ResNet在2015年由微软实验室提出&#xff0c;该网络的亮点&#xff1a; 1.超深的网络结构&#xff08;突破1000层&…

叶工好容5-日志与监控

目录 前言 平台维度 docker运行状态 cAdvisor-日志采集者 Heapster-日志收集 metrics-server-出生决定成败 kube-state-metrics-不完美中的完美 应用维度 日志 部署方式 输出方式 工具选择 日志接入 监控 serviceMonitor Annotation Prometheus扩展性 Thanos …

StackOverFlow刚刚宣布推出自己的AI产品!

StackOverFlow刚刚宣布要推出自己的AI产品&#xff01; OverflowAI是StackOverFlow即将推出自己AI产品的名字&#xff0c;据称也是以VSCode插件的形式&#xff0c;计划在8月发布。我们来看看都有些什么功能&#xff0c;通过目前的信息看&#xff0c;OverflowAI的主要功能就是&…

漂亮的TAB标签切换

样式 CSS样式 .min-tab-box{position: relative;width: 100%;text-align: initial;padding-left: 550px;top: 10px; } .min-tab-item{position: absolute;--transform: perspective(75em) rotateX(0deg) rotateZ(-0deg) translate(calc(var(--x) * 87%), calc(var(--y) * 86.6…

机器学习笔记之优化算法(三)线搜索方法(步长角度;精确搜索)

机器学习笔记之优化算法——线搜索方法[步长角度&#xff0c;精确搜索] 引言回顾&#xff1a;线搜索方法——方向角度关于收敛性的假设关于单调性的假设下降方向与最速方向 从步长角度观察线搜索方法关于方向向量的假设精确搜索过程 引言 上一节介绍了从方向角度认识线搜索方法…

Python 面向对象的属性与方法

1.私有属性 私有属性用两个下划线开头&#xff0c;表示这个属性只能在本类中使用 class demo_private:# 测试一个私有属性__a 3# 测试一个公有属性b 4print(__a)print(b)class demo_test:print(demo_private.b)print(demo_private.__a)运行结果为&#xff1a; 3 4 4 Attrib…

linux系统将OpenSSH升级到最高版本

一、背景&#xff1a; 公司安全扫描到主机的OpenSSH安全漏洞&#xff0c;由于是虚拟机只能由自己修复&#xff0c;很多OpenSSH的漏洞厂商都没有提供补丁&#xff0c;只能通过禁用scp或者端口的方式临时解决&#xff0c;但是后面使用就不方便了&#xff0c;而且也不安全&#x…

Linux系统挂载Windows的共享文件夹

1&#xff09; 首先点击共享的文件夹 C:\data\ 右键----属性—共享 首先创建挂载目录 /data mkdir /data/ 安装mount.cifs软件包 yum install cifs-utils -y 开始挂载 mount.cifs //192.168.132.142/data /data/ -o userzhaozhengweir,pass123456

[玩转AIGC]sentencepiece训练一个Tokenizer(标记器)

目录 一、前言二、安装三、自己训练一个tokenizer四、模型运行五、拓展 一、前言 前面我们介绍了一种字符编码方式【如何训练一个中英翻译模型】LSTM机器翻译seq2seq字符编码&#xff08;一&#xff09; 这种方式是对一个一个字符编码&#xff0c;丢失了很多信息比如“机器学习…

Flutter:滑动面板

前言 无意中发现了这个库&#xff0c;发现现在很多app中都有类似的功能。以手机b站为例&#xff0c;当你在看视频时&#xff0c;点击评论&#xff0c;视频会向上偏移&#xff0c;下方划出评论界面。 sliding_up_panel SlidingUpPanel是一个Flutter插件&#xff0c;用于创建滑…

从Vue 2到Vue 3:深入了解路由配置的变化与升级建议

&#x1f3ac; 岸边的风&#xff1a;个人主页 &#x1f525; 个人专栏:《 VUE 》 《 javaScript 》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 目录 &#x1f4d8; 前言 vue2路由配置 &#x1f4df; 一、控制台安装vue路由 &#x1f4df; 二、项目src文件夹下创建r…

kubernetes搭建GitLab并提供本地域名服务及业务微服务接口集成GitLab

目录 搭建GitLab服务镜像准备数据卷脚本部署脚本服务脚本路由脚本登录及配置 GitLab接口及业务微服务上传代码GitLab接口注意事项业务微服务上传代码注意事项 搭建GitLab服务 业务管理平台需要将本地的代码上传并进行版本管理&#xff0c;选择搭建gitlab私服较为合理&#xff0…

windows 删除无法删除的文件

有两种原因&#xff1a; 文件被占用文件无权限 解决方案 通用解决方案是进入安全模式进行删除 安全模式&#xff1a; 不会启动非必要的进程有最高的系统权限 进入系统配置 安全引导&#xff0c;重启 删除文件 修改系统配置为正常启动 重启

Springer独立出版 | 2023年触觉与虚拟现实国际会议(ICHVR 2023)

会议简介 Brief Introduction 2023年触觉与虚拟现实国际会议(ICHVR 2023) 会议时间&#xff1a;2023年12月15日-17日 召开地点&#xff1a;中国北海 大会官网&#xff1a;www.ichvr.org 2023年触觉与虚拟现实国际会议(ICHVR 2023)由东南大学、上海交通大学联合主办&#xff1b;…

Mybatis 新增/批量新增, 拿到返回的自增主键ID

单个新增 &#xff1a; /** * 插入菜单 * param menuInfo * return */ int insertMenuInfo(MenuInfo menuInfo); xml&#xff1a; <insert id"insertMenuInfo" parameterType"com.XXXX..MenuInfo" keyProperty"id&quo…

MyBatis 快速入门【下】

&#x1f600;前言 本篇博文是一些功能的实现和实现日志输出&#xff0c;希望能够帮到你&#x1f60a; &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章可以帮助到大家&#xff0c;您的满意是…