上班摸鱼不被老板发现:设计模式--观察者模式

观察者模式

观察者模式,又叫做发布–订阅模式(Publish/Subscribe)模式

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生改变时,会通知所有的观察者对象,使他们能够自动更新自己。

观察者模式(Observer)结构图

在这里插入图片描述

  • Subject类:可翻译为主题或抽象通知者,一般用一个抽象类或一个接口实现。它把所有对观察者对象的引用保留在一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
// 抽象通知者
public abstract class Subject {private List<Observer> list = new ArrayList<>();// 状态protected String subjectState;// 增加观察者public void attatch(Observer observer) {list.add(observer);}// 减少观察者public void detach(Observer observer) {list.remove(observer);}// 通知观察者public void notifyObserver() {for (Observer item : list) {item.update();}}public String getSubjectState() {return subjectState;}public void setSubjectState(String subjectState) {this.subjectState = subjectState;}
}
  • Observer类,抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。这个接口叫做更新接口。抽象观察者一般用一个抽象类或一个接口实现。更新接口通常包含一个update()方法,这个方法叫做更新方法。
// 抽象观察者
public abstract class Observer { public abstract void update();
}
  • ConcreteSubject类:具体主题或具体通知者,将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记的观察者发出通知。具体主题角色通常用一个子类实现。
// 具体主题或具体通知者
public class ConcreteSubject extends Subject {   
}
  • ConcreteObserver类:具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。具体观察者角色可以保存一个指向具体主题对象的引用。具体观察者角色通常使用一个子类实现。
// 具体观察者
public class ConcreteObserver extends Observer {private String name;private Subject sub;public ConcreteObserver(String name, Subject sub) {this.name = name;this.sub = sub;}@Overridepublic void update() {System.out.println("观察者"+this.name + "的新状态是"+this.sub.getSubjectState());}
}

客户端调用

Subject subject = new ConcreteSubject();
subject.attatch(new ConcreteObserver("nameX", subject));
subject.attatch(new ConcreteObserver("nameY", subject));
subject.attatch(new ConcreteObserver("nameZ", subject));subject.notifyObserver();

案例

那么现在举一个简单的例子:某公司中,某部门几个员工在上班期间趁着老板不在,炒股、看NBA等等开始摸鱼,并且通过买一些小零食贿赂前台小姐姐,当老板回来时通知他们一声。使用观察者模式如何实现呢?

那么首先分清楚角色划分,具体主题或具体通知者就是我们的前台小姐姐,那么具体观察者就是部门的几个员工。

我们的抽象通知者Subject类基本是不需要改变的,直接拿上面的的代码就行,然后设置个名称区分下就行。

  • Subject抽象通知者
// 抽象通知者
public abstract class Subject {private List<Observer> list = new ArrayList<>();protected String subjectState;protected String name;public Subject(String name) {this.name = name;}// 增加观察者public void attatch(Observer observer) {list.add(observer);}// 减少观察者public void detach(Observer observer) {list.remove(observer);}// 通知观察者public void notifyObserver() {for (Observer item : list) {item.update();}}public String getSubjectState() {return subjectState;}public void setSubjectState(String subjectState) {this.subjectState = subjectState;}
}
  • 具体通知者
// 前台小姐姐
public class Secretary extends Subject {public Secretary(String name) {super(name);}
}
  • 抽象观察者
// 看股票的同事
public class StockObserver extends Observer {public StockObserver(String name, Subject sub) {super(name, sub);}@Overridepublic void update() {System.out.println(super.sub.name + ":"+super.sub.getSubjectState() + "!" + super.name + "快关闭股票行情,开始工作");}
}// 看NBA的同事
public class NBAObserver extends Observer {public NBAObserver(String name, Subject sub) {super(name, sub);}@Overridepublic void update() {System.out.println(super.sub.name + ":"+super.sub.getSubjectState() + "!" + super.name + "快关闭NBA直播,开始工作");}
}
  • 客户端调用
Subject subject = new Secretary("前台小姐姐");
subject.attatch(new StockObserver("张三", subject));
subject.attatch(new StockObserver("李四", subject));
subject.attatch(new NBAObserver("王五", subject));
subject.setSubjectState("老板回来了");subject.notifyObserver();

结果:

前台小姐姐:老板回来了!张三快关闭股票行情,开始工作
前台小姐姐:老板回来了!李四快关闭股票行情,开始工作
前台小姐姐:老板回来了!王五快关闭NBA直播,开始工作

Java内置接口实现

实际上,java已经为观察者模式准备好了相关的接口和抽象类了。观察者接口java.util.Observer和通知者java.util.Observable。有了这些Java内置的代码,我们只需要扩展或继承Observable,并且告诉它什么时候应该通知观察者就OK了。

那么后边考虑到如果有多个通知者的情况下,为了避免需要在update()方法中对通知者进行强转,中间又加了一层Subject类。

代码结构图

在这里插入图片描述

那么看看具体实现过程:

  • 抽象通知者类
// 抽象通知者
public abstract class Subject extends Observable {private List<Observer> list = new ArrayList<>();protected String subjectState;protected String name;public Subject(String name) {this.name = name;}public String getSubjectState() {return subjectState;}public void setSubjectState(String subjectState) {this.subjectState = subjectState;super.setChanged(); // 改变通知者的状态super.notifyObservers(); // 调用父类的方法,通知所有的观察者}
}
  • 具体通知者
// 前台小姐姐
public class Secretary extends Subject {public Secretary(String name) {super(name);}
}
  • 具体观察者类
// 看股票的同事
public class StockObserver implements Observer {protected String name;public StockObserver(String name) {this.name = name;}@Overridepublic void update(Observable o, Object arg) {Subject sub = (Subject) o;System.out.println(sub.name + ":"+sub.getSubjectState() + "!" + this.name + "快关闭股票行情,开始工作");}}// 看NBA的同事
public class NBAObserver implements Observer {   protected String name;public NBAObserver(String name) {this.name = name;}@Overridepublic void update(Observable o, Object arg) {Subject sub = (Subject) o;System.out.println(sub.name + ":"+sub.getSubjectState() + "!" + this.name + "快关闭NBA直播,开始工作");}
}
  • 客户端调用
Subject subject = new Secretary("前台小姐姐");
subject.addObserver(new StockObserver("张三"));
subject.addObserver(new StockObserver("李四"));
subject.addObserver(new NBAObserver("王五"));
subject.setSubjectState("老板回来了");subject.notifyObservers();

特点

使用观察者模式的动机是什么呢?

  • 将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。

  • 当一个对象的改变需要同时改变其他对象时,而它不知道具体有多少个对象有待改变时,应该考虑使用观察者模式。

  • 抽象模型有两个方面,其中一方面依赖于另一方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。

  • 观察者模式所作的工作其实就是在解耦合。让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化。

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

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

相关文章

【网络安全】网络防护之旅 - 非对称密钥体制的解密挑战

&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《网络安全之道 | 数字征程》⏰墨香寄清辞&#xff1a;千里传信如电光&#xff0c;密码奥妙似仙方。 挑战黑暗剑拔弩张&#xff0c;网络战场誓守长。 目录 &#x1f608;1. 初识网络安…

vue-实现高德地图-省级行政区地块显示+悬浮显示+标签显示

<template><div><div id"container" /><div click"showFn">显示</div><div click"removeFn">移除</div></div> </template><script> import AMapLoader from amap/amap-jsapi-load…

Spring Boot业务代码中使用@Transactional事务失效总结

1、概述 我们知道 Spring 声明式事务功能提供了极其方便的事务配置方式&#xff0c;配合 Spring Boot 的自动配置&#xff0c;大多数 Spring Boot 项目只需要在方法上标记 Transactional注解&#xff0c;即可一键开启方法的事务性配置。当然后端开发人员对数据库事务这个概念并…

模型评估指标

1.回归模型 回归模型常常使用MSE均方误差&#xff0c;预测值与真实值之间的平均差距 2.分类模型 2.1 Accuracy正确率 分类正确的数目的占比 但在类别不平衡的情况下&#xff0c;模型可能倾向于预测占多数的类别&#xff0c;导致Acc高但对少数类别的预测效果其实比较差的。…

Vue脚手架 Vue CLI安装

目录 0.为什么要安装Vue CLI脚手架 1.配置方法 1.全局安装 (一次) 2.查看Vue版本&#xff08;一次&#xff09; 报错&#xff1a;出现禁止运行脚本 3.创建项目架子&#xff08;可多次&#xff09; 报错npm err! 问题&#xff1a;已知npm换过国内源&#xff0c;且进度条…

go-zero目录结构和说明

. ├── code-of-conduct.md 行为准则 ├── CONTRIBUTING.md 贡献指南 ├── core 框架的核心组件 │ ├── bloom 布隆过滤器&#xff0c;用于检测一个元素是否在一个集合中 │ ├── breaker 熔断器&am…

MicroPython相关教程

WebRepl MicroPython-ESP32之WebRepl-1Z实验室 - 简书 https://www.jianshu.com/p/c2ddd4fd05be ESP32上面webrepl的开启与连接 - 简书 https://www.jianshu.com/p/f4163eae4a05 Esp32安装micropython和配置webrepl记录备忘 - 哔哩哔哩 https://www.bilibili.com/read/cv121…

工资计算_分支结构 C语言xdoj63

问题描述 小明的公司每个月给小明发工资&#xff0c;而小明拿到的工资为交完个人所得税之后的工资。假设他一个月的税前工资为S元&#xff0c;则他应交的个人所得税按如下公式计算&#xff1a; 1&#xff09; 个人所得税起征点为3500元&#xff0c;若S不超过3500&#xff0c;则…

数据挖掘目标(客户价值分析)

import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as snsIn [2]: datapd.read_csv(r../教师文件/air_data.csv)In [3]: data.head()Out[3]: Start_timeEnd_timeFareCityAgeFlight_countAvg_discountFlight_mileage02011/08/182014/0…

android 13.0 app应用安装黑名单

1.概述 在13.0系统rom定制化开发中,客户需求要实现应用安装黑名单功能,在白名单之中的应用可以安装,其他的app不准安装,实现一个 控制app安装的功能,这需要从app安装流程入手就可以实现功能 PMS就是负责管理app安装的,功能就添加在这里就可以了,接下来看具体实现这个功能…

uniapp播放 m3u8格式视频 兼容pc和移动端

支持全自动播放、设置参数 自己摸索出来的,花了一天时间,给点订阅支持下,订阅后,不懂的地方可以私聊我。 代码实现 代码实现 1.安装dplayer组件 npm i dplayer2. static/index.html下引入 hls 引入hls.min.js 可以存放在static项目hls下面<script src="/static…

PyGame图形绘制函数详解

文章目录 五种图形矩形圆形 五种图形 除了直线之外&#xff0c;pygame中提供了多种图形绘制函数&#xff0c;除了必要的绘图窗口、颜色以及放在最后的线条宽度之外&#xff0c;它们的参数如下表所示 函数图形参数/类型rect矩形Rectellipse椭圆Rectarc椭圆弧Rect, st, edcircl…

Pytorch当中的.detach()操作是什么意思

.detach() 是 PyTorch 中用于从计算图中分离张量的方法。当我们在PyTorch中进行张量运算时&#xff0c;操作会构建一个计算图来跟踪计算历史&#xff0c;这个计算图用于自动求导和反向传播来计算梯度。 使用.detach()方法可以将一个张量从当前的计算图中分离出来&#xff0c;使…

如何实现填表后分配序列号、活动抢票抽奖、自助分配座位号?

&#x1f4f1;发布者想要实现让用户在填表后自动分配序列号、座位号&#xff0c;或制作活动抢票抽奖系统&#xff0c;该如何实现&#xff1f; &#x1f4cc;使用教程 &#x1f4d6;案例1&#xff1a;制作活动抽奖系统 使用预置数据分配的随机分配功能&#xff0c;以活动抽奖为例…

《C++新经典设计模式》之第9章 命令模式

《C新经典设计模式》之第9章 命令模式 命令模式.cpp 命令模式.cpp #include <iostream> #include <list> #include <memory> using namespace std;// 5种角色 // Receiver&#xff08;接收者&#xff09;&#xff0c;Cook&#xff0c;提供请求业务的处理接口…

标书整体实施方案

一、标书整体实施方案概述 标书整体实施方案是一种全面、系统的方法&#xff0c;旨在确保标书的顺利完成和成功实施。它涵盖了从项目背景到招标文件分析&#xff0c;再到投标文件编制和投标的全过程。实施方案不仅提供了明确的工作流程和分工&#xff0c;还为整个标书制作团队…

针对网页html中插入动图gif不能循环播放只播放一次的解决方案

针对网页html中插入动图gif不能循环播放只播放一次的解决方案 原因分析解决方案 原因分析 使用图片编辑软件制作的过程中未启用“循环播放”功能&#xff0c;这里以Photoshop为例&#xff0c;演示设置GIF图片循环播放的操作流程&#xff1a;所需材料&#xff1a;PS。第一步&am…

MongoDB——模糊查询的两种方法

方法一&#xff1a;类似于结构性数据库的like db.users.find({fname: /zhangsan/}); 对应mysql的like用法&#xff1a;select * from users where fname like %zhangsan%; &#xff08;1&#xff09;如果要模糊查询以什么开头&#xff0c;方法如下&#xff1a; db.users.fi…

图灵日记之Leetcode删除有序数组中的重复项合并两个有序数组移除链表元素

题目 删除有序数组中的重复项题目入口题目内容思路代码c版本c嘎嘎版本 合并两个有序数组题目链接题目内容思路代码c版本(c嘎嘎版本与c版本内容一样) 移除链表元素题目链接题目内容思路1代码1思路2代码2思路3代码3 删除有序数组中的重复项 题目入口 题目内容 给你一个 非严格…

抖音ip地址切换什么原因

在如今的互联网世界中&#xff0c;抖音已经成为了一个非常受欢迎的应用程序&#xff0c;让人们可以通过短视频来分享生活点滴。然而&#xff0c;有时候我们可能会遇到一些问题&#xff0c;比如在使用抖音时需要进行IP地址切换。那么&#xff0c;为什么要切换IP地址呢&#xff1…