设计模式之观察者模式

文章目录

  • 一、介绍
  • 二、实现思路
  • 三、基本角色
  • 四、案例
    • 1. 不使用观察者模式
    • 2. 使用观察者模式
  • 五、java中的观察者模式
  • 六、spring中的观察者模式
  • 七、优缺点

一、介绍

观察者模式(Observer Pattern),又称监听器模式(Listener Pattern)发布-订阅模式(Publish-Subscribe Pattern),属于行为型设计模式。该模式定义了一种一对多的依赖关系,当一个对象状态发生改变或执行某个逻辑时,所有依赖于该动作的对象都会收到通知并作出相应的动作。

该模式的目的就是为了使系统中的主线业务与各个与主线业务相关的支线业务之间实现解耦

比如我们社会中,各行各业的所有企业发展都离不开国家政策的支持,当国家发布某一个政策时,社会上各行各业所有企事业单位都会根据该政策对内部作出相应的调整。在这个例子中,国家发布政策相当于主线业务,依赖于该政策的各个企事业单位做出的相应调整就相当于支线业务了。

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

二、实现思路

在观察者模式中,存在两个重要角色:观察者被观察者

被观察者中维护一组观察者的集合,当调用被观察者的某个行为时,其内部对观察者集合进行遍历并调用各个观察者的对应逻辑。

三、基本角色

通过以上介绍和实现思路,我们围绕观察者模式中观察者和被观察者两个部分定义出以下四个基本角色:

  • 被观察者的抽象接口(Observable)

    该接口定义被观察者的基本功能,如添加观察者addObserver()、向观察者发送提醒notifyObs()、获取被观察者中的内容getContent()等方法。

    public interface Observable {/** 添加观察者 */void addObserver(Observer observer);/** 向观察者发送提醒 */void notifyObs();/** 获取内容 */String getContent();
    }
  • 被观察者的抽象类(AbsObservable)

    实现抽象接口Observable,对接口中定义的方法实现基本逻辑,并在其内部维护一个观察者集合。该抽象类是观察者模式中的关键角色,其内部定义了观察者模式中的关键代码逻辑。

    public abstract class AbsObservable implements Observable{// 观察者集合private final List<Observer> observerList = new ArrayList<>();// 被观察者的内容private final String content;// 强制其子类重写构造函数public AbsObservable(String content) {this.content = content;}@Overridepublic void addObserver(Observer observer) {observerList.add(observer);}@Overridepublic void notifyObs() {// 通知集合中的所有观察者,对当前被观察者作出相应更新for (Observer observer : observerList) {observer.update(this);}}// 获取当前被观察者的内容@Overridepublic String getContent() {return content;}}
    
  • 被观察者的具体实现类(ObservableImpl)

    继承于抽象类AbsObservable,并显示定义其构造方法以定义该被观察者的内容content

    由于被观察者的基本实现逻辑都已经在抽象类AbsObservable中实现,因此无需对其重写。

    public class ObservableA extends AbsObservable {public ObservableA(String content) {super(content);}
    }public class ObservableB extends AbsObservable {public ObservableB(String content) {super(content);}
    }
    
  • 观察者的抽象接口(Observer)

    定义观察者的基本功能,如根据其观察的被观察者做出响应update()

    public interface Observer {// 对被观察者对象做出响应void update(AbsObservable observable);
    }
    
  • 观察者的具体实现类(ObserverImpl)

    实现抽象接口Observer,对其定义的方法进行实现。

    public class ObserverA implements Observer{@Overridepublic void update(AbsObservable observable) {System.out.println("ObserverA接收到内容:" + observable.getContent());}
    }public class ObserverB implements Observer{@Overridepublic void update(AbsObservable observable) {System.out.println("ObserverB接收到内容:" + observable.getContent());}
    }
    
  • 新建测试类(ObserverDemo)

    public class ObserverDemo {public static void main(String[] args) {// 定义两个观察者:observerA 和 observerBObserver observerA = new ObserverA();Observer observerB = new ObserverB();// 定义被观察者observableA,向其添加观察者,Observable observableA = new ObservableA("来自ObservableA的消息");observableA.addObserver(observerA);observableA.addObserver(observerB);observableA.notifyObs();System.out.println("=====================");// 定义被观察者observableB,向其添加观察者,Observable observableB = new ObservableB("来自ObservableB的消息");observableB.addObserver(observerA);observableB.addObserver(observerB);observableB.notifyObs();}
    }
    

    运行该演示代码,输出如下

    在这里插入图片描述

观察者模式的通用UML类图如下所示

在这里插入图片描述

四、案例

我们以政府发布政策,各企事业单位响应政策为例。

我们政府需要根据社会实际发展状况针对各行各业发布各种政策,有教育行业政策、互联网行业政策等,我们熟知的公司比如教育行业的新东方、互联网行业的阿里和百度会根据各个政策作出响应,处于教育行业的新东方对教育行业政策十分关注,而处于互联网行业的阿里百度则对互联网行业政策十分关注,无论什么公司,当自己关注的新政策发布时,都会做出响应。

我们把这个案例结合观察者模式进行分析,政府针对各行各业发布的政策就相当于被观察者,新东方阿里百度这三家公司就相当于观察者。

下面我们通过不使用观察者模式和使用观察者模式两种实现方案的对比,对观察者模式有个更加深入的了解。

1. 不使用观察者模式

在不使用观察者模式的情况下,要实现上面案例,代码如下所示

  • 公司抽象接口类Company

    每个具体的公司都应当具备**响应政策(responsePolicy)**的功能

    public interface Company {/** 响应政策 */void responsePolicy();
    }
    
  • 公司实现类AliBaiduXinDongFang

    public class Ali implements Company{@Overridepublic void responsePolicy() {System.out.println("阿里巴巴响应新政策");}
    }public class Baidu implements Company{@Overridepublic void responsePolicy() {System.out.println("百度响应新政策");}
    }public class XinDongFang implements Company{@Overridepublic void responsePolicy() {System.out.println("新东方响应新政策");}
    }
    
  • 政策抽象接口类Policy

    public interface Policy {/** 发布新政策 */void publish();
    }
    
  • 政策实现类EduPolicyInternetPolicy

    在调用政策的发布方法publish()后,需要调用每一个公司的响应政策方法,比如在教育政策发布后,需要调用教育行业公司的响应政策方法,这种方式需要在政策类内部维护大量相关的公司,以便于在发布政策时对这些公司逐个调用其响应政策方法,而根据单一职责原则,这种方式明显使方法变得臃肿。

    public class EduPolicy implements Policy{private final String content;public EduPolicy(String content) {this.content = content;}@Overridepublic void publish() {System.out.println("教育政策发布:" + content);// 公司响应政策Company xinDongFang = new XinDongFang();xinDongFang.responsePolicy();}
    }public class InternetPolicy implements Policy{private final String content;public InternetPolicy(String content) {this.content = content;}@Overridepublic void publish() {System.out.println("互联网政策发布:" + content);// 公司响应政策Company ali = new Ali();ali.responsePolicy();Company baidu = new Baidu();baidu.responsePolicy();}
    }
    
  • 代码演示

    public class NoObserverDemo {public static void main(String[] args) {Policy eduPolicy = new EduPolicy("教育行业新政策");eduPolicy.publish();System.out.println("======================");Policy internetPolicy = new InternetPolicy("互联网行业新政策");internetPolicy.publish();}
    }
    
  • 结果输出

    在这里插入图片描述

在不使用观察者模式时,会出现主线业务代码中混入大量分支业务代码,形成不相关逻辑之间的强耦合,使得代码变得十分臃肿,违背单一职责原则。

2. 使用观察者模式

在使用观察者模式后,主线业务代码与分值业务代码解耦,代码变得简洁明了。

下面我们对上面代码进行改进

  • 公司抽象接口类Company

    与上面不同的是,响应政策方法添加了一个政策参数。这就要求各行各业所有公司无差别的对任何政策进行响应,可以积极响应,当然也可以消极响应(啥也不干)。

    public interface Company {/** 响应政策 */void responsePolicy(Policy policy);
    }
    
  • 公司实现类AliBaiduXinDongFang

    在下面的改进代码中,每一个公司都可以接收到新政策,但具体怎么做,可以根据接收到的新政策是否是该公司关注的。比如互联网公司只关注互联网新政策,教育公司只关注教育行业新政策。

    public class Ali implements Company{@Overridepublic void responsePolicy(Policy policy) {if (policy instanceof InternetPolicy) {System.out.println("阿里巴巴响应新政策");}}
    }public class Baidu implements Company{@Overridepublic void responsePolicy(Policy policy) {if (policy instanceof InternetPolicy) {System.out.println("百度响应新政策");}}
    }public class XinDongFang implements Company{@Overridepublic void responsePolicy(Policy policy) {if (policy instanceof EduPolicy) {System.out.println("新东方响应新政策");}}
    }
    
  • 政策抽象接口类Policy

    改进后的Policy接口,除了发布政策publish()功能,还应具备一些被观察者的其他特征,如添加关注政策的公司addCompany(),获取政策内容getContent()等。

    public interface Policy {void addCompany(Company company);/** 政策发布 */void publish();String getContent();
    }
    
  • 政策抽象类AbsPolicy

    抽象类是观察者模式中十分重要的一个角色。该类定义了观察者模式中的基本代码逻辑,例如在publish()方法中,定义了政策发布的基本逻辑,即通知所有公司响应政策,这样一来,在其具体子类发布政策时,只需要关注其主线逻辑即可,在最后直接通过super.publish()将政策传播给各个公司即可。

    public abstract class AbsPolicy implements Policy{// 观察者集合private final List<Company> companyList = new ArrayList<>();// 被观察者的内容protected final String content;// 强制其子类重写构造函数public AbsPolicy(String content) {this.content = content;companyList.add(new Ali());companyList.add(new Baidu());companyList.add(new XinDongFang());}@Overridepublic void addCompany(Company company) {companyList.add(company);}@Overridepublic void publish() {// 通知集合中的所有观察者,对当前被观察者作出相应更新for (Company company : companyList) {company.responsePolicy(this);}}// 获取当前被观察者的内容@Overridepublic String getContent() {return content;}
    }
    
  • 政策实现类EduPolicyInternetPolicy

    在应用观察者模式后,发布政策的主线逻辑与公司对政策的响应逻辑实现解耦,只需要通过super.publish()将政策传播给各个公司即可。

    public class EduPolicy extends AbsPolicy {public EduPolicy(String content) {super(content);}@Overridepublic void publish() {System.out.println("教育政策发布:" + content);super.publish();}
    }public class InternetPolicy extends AbsPolicy {public InternetPolicy(String content) {super(content);}@Overridepublic void publish() {System.out.println("互联网政策发布:" + content);super.publish();}
    }
    
  • 代码演示

    public class PolicyDemo {public static void main(String[] args) {Policy eduPolicy = new EduPolicy("教育行业新政策");eduPolicy.publish();System.out.println("==============");Policy internetPolicyolicy = new InternetPolicy("互联网行业新政策");internetPolicyolicy.publish();}
    }
    

    输出如下

    在这里插入图片描述

五、java中的观察者模式

在java中,java.util包中同样为我们提供了观察者模式的框架供我们使用。有兴趣的朋友可以亲自查看源码,超级简单,该框架只提供一个具体类Observable和一个抽象接口类Observer

  • java.util.Observable
  • java.util.Observer

这两个类的用法与我们上面的讲解代码相似度高达80%,最大的不同就是在JDK的实现中还增加了一个布尔类型的changed域,通过设置这个变量来确定是否通知观察者。也就是说java提供的观察者模式框架我们只需要学习了解即可,在实际生产工作中,不建议使用。

六、spring中的观察者模式

如今我们开发使用的几乎都是spring全家桶了,那么spring是否给我们提供了观察者模式的相关框架呢?答案是肯定的。

在spring中,通过上下文发布指定事件ApplicationEvent,监听该事件的**监听器ApplicationListener**处理对应逻辑。这种实现更贴合 发布-订阅模式这个称呼,其中事件ApplicationEvent相当于观察者模式中的Observable,而监听器ApplicationListener相当于观察者模式中的Observer

下面我们学习一下如何使用

  • 定义一个事件

    public class CarEvent extends ApplicationEvent {/*** Create a new {@code ApplicationEvent}.** @param source the object on which the event initially occurred or with*               which the event is associated (never {@code null})*/public CarEvent(Object source) {super(source);}
    }
    
  • 定义一个针对于该事件的监听器

    • 方式一

      通过实现ApplicationListener接口定义监听器

      @Component
      public class CarEventListener implements ApplicationListener<CarEvent> {@Overridepublic void onApplicationEvent(CarEvent event) {// 监听器监听到指定事件// 针对该事件处理逻辑...}
      }
      
    • 方式二

      通过注解@EventListener定义监听器

      @Slf4j
      @Component
      public class GlobalListener {@EventListenerpublic void carEvent(CarEvent carEvent) {log.info("监听到了car事件");}}
      
  • 发布事件

    应用上下文ApplicationContext具有发布事件的能力

    @Service
    public class OrderService {@Autowiredprivate ApplicationContext context;public void doSomething() {// 使用上下文发布一个CarEventcontext.publishEvent(new CarEvent(this));}
    }
    

七、优缺点

优点:

  • 观察者和被观察者是松耦合的,符合单一职责原则。
  • 定义了一对多的依赖关系。

缺点:

  • 如果观察者数量过多,则事件通知会耗时较长。可通过异步通知解决。
  • 如果观察者和被观察者之间存在循环依赖,则有可能导致系统崩溃。


纸上得来终觉浅,绝知此事要躬行。

————————我是万万岁,我们下期再见————————

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OpenFeign的简单介绍和功能实操

前言 本文主要做一下OpenFeign的简单介绍和功能实操&#xff0c;实操主要是OpenFeign的超时和重试&#xff0c;在阅读本文章前&#xff0c;请完成《Nacos 注册中心介绍与实操》内的Nacos多模块生产消费者项目 什么是OpenFeign OpenFeign全名Spring Cloud OpenFeign&#xff…

树结构及其算法-二叉查找树

目录 树结构及其算法-二叉查找树 C代码 树结构及其算法-二叉查找树 二叉树在建立的过程中是根据“左子树 < 树根 < 右子树”的原则建立的&#xff0c;因此只需从树根出发比较键值即可&#xff0c;如果比树根大就往右&#xff0c;否则往左而下&#xff0c;直到相等就找…

基于OR-Tools的装箱问题模型求解(PythonAPI)

装箱问题 一、背包问题&#xff08;Knapsack problem&#xff09;1.1 0-1背包模型基于OR-Tools的0-1背包问题求解&#xff08;PythonAPI&#xff09;导入pywraplp库数据准备声明MIP求解器初始化决策变量初始化约束条件目标函数调用求解器打印结果 1.2 多重背包问题&#xff08;…

diffusers-Load pipelines,models,and schedulers

https://huggingface.co/docs/diffusers/using-diffusers/loadinghttps://huggingface.co/docs/diffusers/using-diffusers/loading 有一种简便的方法用于推理是至关重要的。扩散系统通常由多个组件组成&#xff0c;如parameterized model、tokenizers和schedulers&#xff0c…

SpringBoot整合自签名SSL证书,转变HTTPS安全访问(单向认证服务端)

前言 HTTP 具有相当优秀和方便的一面,然而 HTTP 并非只有好的一面&#xff0c;事物皆具两面性&#xff0c;它也是有不足之处的。例如&#xff1a; 通信使用明文&#xff08;不加密&#xff09;&#xff0c;内容可能会被窃听。不验证通信方的身份&#xff0c;因此有可能会遭遇…

多线程锁的升级原理是什么

在 Java 中&#xff0c;锁共有 4 种状态&#xff0c;级别从低到高依次为&#xff1a;无状态锁&#xff0c;偏向锁&#xff0c;轻量级锁和重量级锁状态&#xff0c;这几个状态会随着竞争情况逐渐升级。锁可以升级但不能降级。 多线程锁锁升级过程 如下图所示 多线程锁的升级过程…

Ha-NeRF源码解读 train_mask_grid_sample

目录 背景&#xff1a; &#xff08;1&#xff09;Ha_NeRF论文解读 &#xff08;2&#xff09;Ha_NeRF源码复现 &#xff08;3&#xff09;train_mask_grid_sample.py 运行 train_mask_grid_sample.py解读 1 NeRFSystem 模块 2 forward()详解 3 模型训练tranining_st…

windows和docker环境下springboot整合gdal3.x

链接: gdal官网地址 gdal gdal的一个用c语言编写的库&#xff0c;用于处理地理信息相关的数据包括转换&#xff0c;识别数据&#xff0c;格式化数据以及解析 同时提供第三方语言的SDK包括python&#xff0c;java上述需要编译后使用 java是需要使用jni接口调用实现方法在wind…

SAR 系统基本原理

目录 1.真实孔径雷达 2.合成孔径雷达 本文由CSDN点云侠原创&#xff0c;爬虫网站请自重。 1.真实孔径雷达 RADAR 中文名称雷达&#xff0c;是 Radio Detection And Ranging&#xff08;无线电探测与定位&#xff09;的缩写。雷达发射机产生足够的电磁能量&#xff0c;经过收发…

人工智能-多层感知机

隐藏层 该模型通过单个仿射变换将我们的输入直接映射到输出&#xff0c;然后进行softmax操作。 如果我们的标签通过仿射变换后确实与我们的输入数据相关&#xff0c;那么这种方法确实足够了。 但是&#xff0c;仿射变换中的线性是一个很强的假设。 线性模型可能会出错 例如&…

MySQL8.0安装

安装过程 一.官网下载离线tar包二.安装MySQL三.验证 一.官网下载离线tar包 点击此处下载 链接&#xff1a;https://pan.baidu.com/s/1_p2esJax95Ow39wfgM_paw 提取码&#xff1a;g415 –来自百度网盘超级会员V2的分享 二.安装MySQL 1.上传到/usr/local目录下 2.解压安装包…

Tigger绕过激活锁/屏幕锁隐藏工具,支持登入iCloud有消息通知,支持iOS12.0-14.8.1。

绕过激活锁工具Tigger可以用来帮助因为忘记自己的ID或者密码而导致iPhone/iPad无法激活的工具来绕过自己的iPhone/iPad。工具支持Windows和Mac。 工具支持的功能&#xff1a; 1.Hello界面两网/三网/无基带/乱码绕过&#xff0c;可以完美重启&#xff0c;支持iCloud登录、有消…

香港高端人才通行证计划入围高校/全球百强大学综合名单公布!

香港高端人才通行证计划入围高校/全球百强大学综合名单公布&#xff01; 香港高才通计划希望吸引世界各地具备丰富工作经验及高学历的人才到香港探索机遇&#xff0c;这些高端人才包括高收入人士和在世界顶尖大学毕业的学生。 此计划并不适用于阿富汗、古巴、老挝、朝鲜、尼泊尔…

Python框架之Flask入门和视图

一、Flask入门和视图 需要安装Pycharm专业版 1. Flask简介 Python后端的2个主流框架 Flask 轻量级框架Django 重型框架 Flask是一个基于Python实现的web开发微框架 官方文档&#xff1a;https://flask.palletsprojects.com/ 中文文档&#xff1a;https://dormousehole.readthe…

WPF RelativeSource属性-目标对象类型易错

上一篇转载了RelativeSource的三种用法&#xff0c;其中第二种用法较常见&#xff0c;这里记录一下项目中曾经发生错误的地方&#xff0c;以防自己哪天忘记了&#xff0c;又犯了同样错误—WPF RelativeSource属性-CSDN博客 先回顾一下&#xff1a; 控件关联其父级容器的属性—…

物联网AI MicroPython传感器学习 之 QMC5883指南针罗盘传感器

学物联网&#xff0c;来万物简单IoT物联网&#xff01;&#xff01; 一、产品简介 QMC5883是一款表面贴装的集成了信号处理电路的三轴磁性传感器&#xff0c;应用场景主要包括罗盘、导航、无人机、机器人和手持设备等一些高精度的场合。 引脚定义 VCC&#xff1a;3V3&#…

在Qt中List View和List Widget的区别是什么,以及如何使用它们

2023年10月29日&#xff0c;周日晚上 目录 List View和List Widget的区别 如何使用QListView 如何使用QListWidget List View和List Widget的区别 在Qt中&#xff0c;QListView 和 QListWidget 是用于显示列表数据的两个常用控件&#xff0c;它们有一些区别和特点。 1. 数…

遥遥领先,免费开源的django4-vue3项目

星域后台管理系统前端介绍 &#x1f33f;项目简介 本项目前端基于当下流行且常用的vue3作为主要技术栈进行开发&#xff0c;融合了typescript和element-plus-ui&#xff0c;提供暗黑模式和白昼模式两种主题以及全屏切换&#xff0c;开发bug少&#xff0c;简单易学&#xff0c…

新手必看的Facebook广告投放基础思路

一、广告账号要求 如果您还没有Facebook账号&#xff0c;那么第一步是准备Facebook账号。 1、配置正确的网络环境 Facebook账号需要在稳定安全的网络环境中运行&#xff0c;否则很容易导致封禁。像我们常用的是Maskfog指纹浏览器&#xff0c;可以通过自定义浏览器指纹与为环…

私有网络的安全保障,WorkPlus Meet内网视频会议助力企业高效会议

在企业内部沟通与协作中&#xff0c;视频会议成为了一种必不可少的沟通方式。然而&#xff0c;传统的互联网视频会议往往受制于网络不稳定因素&#xff0c;给企业带来不便与困扰。WorkPlus Meet作为一款专注内网视频会议的软件&#xff0c;致力于为企业打造高效、稳定的内网视频…