当前位置: 首页 > news >正文

设计模式篇---观察者模式

文章目录

    • 概念
    • 结构
    • 实例
    • 总结

概念

观察者模式:定义对象之间的一种一对多的依赖关系,使得每当一个对象状态发生改变时,其他相关依赖对象都得到通知并被自动更新。
观察者模式是使用频率较高的一个模式,它建立了对象与对象之间的依赖关系,当一个对象发生了改变,自动会通知其他对象。发生改变的对象被称为观察目标,被通知的对象被称为观察者。
当我们看到绿灯就会通过,看到红灯就会停止行走,红灯就是观察目标,我们就是观察者,红灯只有一个,而我们是一群人,也就是说一个目标可以对应多个观察者。

结构

观察者模式的类图如下:
在这里插入图片描述
Subject(目标):目标也称为主题,也就是被观察的对象。我们可以在目标中定义一个观察者集合,它提供方法来增加或者删除观察者对象,同时它最主要的方法是通知方法notify,可以通知观察者。
ConcreteSubject(具体目标):它是目标的子类,当它的状态发生改变时,主要是用来向各个观察者发送通知。
Observer(观察者):观察者将对观察目标的改变做出反应,观察者一般定义为接口。
ConcreteObserver(具体观察者):它是观察者的子类,在具体观察者中维护了一个指向具体目标的引用。

实例

在某多人联机对战游戏中,多个玩家可以加入同一战队组成联盟,当战队中的一人遭到敌人攻击时将给所有的其他盟友发送通知,盟友收到通知后将做出反应。
如果不用设计模式,正常的链路是这样的:联盟成员遭到攻击---->通知给盟友---->盟友做出反应,这样的弊端是如果盟友有很多,则每一个成员都需要进行关联,耦合性太严重。加入观察模式的话,以指挥部作为一个新的对象,链路变成这样:联盟成员遭到攻击---->通知指挥部---->指挥部通知所有盟友---->盟友做出反应。

在这里插入图片描述

AllyControlCenter,指挥部中心,充当抽象目标类

@Data
public abstract class AllyControlCenter {protected String allyName;protected ArrayList<Observer> players = new ArrayList<>();public void join (Observer obs){System.out.println(obs.getName()+ "加入"+ this.allyName + "战队");players.add(obs);}public void quit(Observer obs){System.out.println(obs.getName()+ "退出"+ this.allyName + "战队");players.remove(obs);}public abstract void notifyObserver(String name);
}

ConcreteAllyControlCenter类,充当具体目标类

@Data
public class ConcreteAllyControlCenter extends AllyControlCenter {public ConcreteAllyControlCenter(String allyName) {System.out.println(allyName + "战队组建成功");this.allyName = allyName;}@Overridepublic void notifyObserver(String name) {System.out.println(this.allyName + "战队紧急通知,盟友" + name + "遭到敌人攻击");for (Observer player : players) {if (!name.equals(player.getName())) {player.help();}}}
}

Observer,抽象观察者

public interface Observer {String getName();void setName(String name);void help();void beAttacked(AllyControlCenter acc);}

Player,具体观察者

public class Player implements Observer {private String name;public Player(String name) {this.name = name;}@Overridepublic String getName() {return name;}@Overridepublic void setName(String name) {this.name = name;}@Overridepublic void help() {System.out.println("坚持住" + this.name + "来救你");}@Overridepublic void beAttacked(AllyControlCenter acc) {System.out.println(this.name + "被攻击");acc.notifyObserver(name);}
}

客户端

public class Client {public static void main(String[] args) {AllyControlCenter allyControlCenter;allyControlCenter = new ConcreteAllyControlCenter("联盟");Observer play1, play2, play3;play1 = new Player("play1");allyControlCenter.join(play1);play2 = new Player("play2");allyControlCenter.join(play2);play3 = new Player("play3");allyControlCenter.join(play3);play1.beAttacked(allyControlCenter);}}

打印结果:
在这里插入图片描述
具体调用流程:Player.beAttacked()—> AllyControlCenter.notifyObserver()—>Player.help()

另外,jdk 的util 包中自带观察者模式,我们可以直接继承和实现这两个类,使用起来更加方便。
在这里插入图片描述

总结

观察者模式的优点:
1、表示层和数据逻辑层分离,并抽象了更新的接口,便于不同的表示层充当观察者角色。
2、在观察目标和观察者之间建立了一个抽象的耦合。观察者目标只需要维护一个抽象的观察者集合即可,无需了解具体观察者。
3、简化了一对多系统的难度,支持广播通信。
4、符合开闭原则,增加新的观察者无需修改原代码。

观察者模式的缺点:
1、观察者太多的话,有性能问题。
2、如果观察者和观察目标之间存在循环依赖,可能导致系统崩溃。
3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,仅仅是知道目标发生了变化。

适用环境:
1、当一个抽象模型的一方面依赖另一方面时,可以考虑观察者模式。
2、一个对象的改变导致多个对象发生变动时。
3、需要在系统中新建一个触发链,A对象变动影响B对象,B对象变动影响C对象。

相关文章:

  • 4.5.CVAT——视频标注的详细步骤
  • 皇冠测评:网络电视盒子哪个品牌好?电视盒子排行榜
  • 【Linux】实时查看服务器信息
  • 架构面试题汇总(一)
  • 从0到1实现五子棋游戏!!
  • Spring 类型转换、数值绑定与验证(三)— Formatting 与 Validation
  • odoo17 | 核心组件 - 动作(Actions)
  • 第一部由儿科医生执笔的长篇小说出炉
  • ElasticSearch之suggester API
  • 77. 组合(力扣LeetCode)
  • 单片机05__串口USART通信__按键控制向上位机传输字符串
  • 【数据结构】深入探讨二叉树的遍历和分治思想(一)
  • docker容器配置mysql5.7主从复制
  • Mysql REGEXP正则运算符
  • C++面试 -操作系统-安全能力:死锁的危害、出现原因、解决方法
  • 《网管员必读——网络组建》(第2版)电子课件下载
  • 【mysql】环境安装、服务启动、密码设置
  • Java Agent 学习笔记
  • JavaScript服务器推送技术之 WebSocket
  • JSONP原理
  • Linux中的硬链接与软链接
  • Node 版本管理
  • Shadow DOM 内部构造及如何构建独立组件
  • Web Storage相关
  • 聊聊directory traversal attack
  • 使用Maven插件构建SpringBoot项目,生成Docker镜像push到DockerHub上
  • k8s使用glusterfs实现动态持久化存储
  • 不要一棍子打翻所有黑盒模型,其实可以让它们发挥作用 ...
  • ​secrets --- 生成管理密码的安全随机数​
  • (8)STL算法之替换
  • (html5)在移动端input输入搜索项后 输入法下面为什么不想百度那样出现前往? 而我的出现的是换行...
  • (二)Linux——Linux常用指令
  • (附源码)计算机毕业设计ssm本地美食推荐平台
  • (算法二)滑动窗口
  • (一)使用Mybatis实现在student数据库中插入一个学生信息
  • (转) SpringBoot:使用spring-boot-devtools进行热部署以及不生效的问题解决
  • (转)h264中avc和flv数据的解析
  • (转)负载均衡,回话保持,cookie
  • .gitignore文件设置了忽略但不生效
  • .net core webapi Startup 注入ConfigurePrimaryHttpMessageHandler
  • .NET开发不可不知、不可不用的辅助类(三)(报表导出---终结版)
  • .NET设计模式(7):创建型模式专题总结(Creational Pattern)
  • .NET实现之(自动更新)
  • .sh 的运行
  • @JoinTable会自动删除关联表的数据
  • @RequestBody与@ResponseBody的使用
  • [2017][note]基于空间交叉相位调制的两个连续波在few layer铋Bi中的全光switch——
  • [android] 练习PopupWindow实现对话框
  • [android] 切换界面的通用处理
  • [BUUCTF]-PWN:wustctf2020_number_game解析(补码,整数漏洞)
  • [C# 开发技巧]实现属于自己的截图工具
  • [C++]命名空间等——喵喵要吃C嘎嘎
  • [CF482B]Interesting Array
  • [luoguP2401] 不等数列
  • [New Portal]Windows Azure Virtual Machine (3) 在VM上挂载磁盘