Java特性之设计模式【观察者模式】

news/2024/12/12 22:42:45/

一、观察者模式

概述

当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)

观察者模式是一种对象行为模式。它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。在观察者模式中,主体是通知的发布者,它发出通知时并不需要知道谁是它的观察者,可以有任意数目的观察者订阅并接收通知。观察者模式不仅被广泛应用于软件界面元素之间的交互,在业务对象之间的交互、权限管理等方面也有广泛的应用

主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作

何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知

观察者模式(Observer) 也叫做"发布-订阅模式"(Publish / Subscribe)

优缺点

优点:

1、观察者和被观察者是抽象耦合的

2、建立一套触发机制

缺点:

1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间

2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃

3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化

1. 各个角色介绍

1.1 Observer (观察者)

Observer是1个抽象类,我们可以理解它是一个抽象观察者类

它只有1个update()方法,也就是说它可以通过这个方法来执行某些动作

1.2 Subject(通知者/被观察者)

Subject是一个抽象类,我们可以理解为一个抽象被观察者类

它有5个方法

  • attach() 和 detach() 方法用来增删观察者的数量,也就是指当前被观察者对象到底有多少个观察者在观察着它

  • setState() 和 getState() 用于获取和设置通知者对象本身的状态, 这个状态通常是传送给观察者们的信息或参数也就是说观察者模式到底在观察什么,无非就是观察被观察者的这个状态

  • notifyAllObservers(),被观察者通知所有观察者,让观察者根据自己的当前状态 getState() 执行自己的 update() 方法

1.3 ConcreteSubject (具体通知者/被观察者)

这个(些)就是具体的被观察者类,只要继承了Subject抽象类,就可以添加一些对象作为自己的观察者

通过以上讲解,可以体会到这个类里面肯定需要一个容器,用于存放观察者的对象,而这个容器可以根据需要由具体的被观察者选择,通常是无序不能重复的Set容器(例如Set集合)

而 notifyAllObservers() 方法无非就是遍历自己的观察者容器,逐个执行观察者的update()方法

1.4 ConcreteObserver(具体观察者类)

这个(些)就是具体的观察这类,但是它们都必须继承Observer抽象类

一旦这些对象被通知者加入自己的容器,就相当于观察者正在观察某个被观察者

注意,观察者可以被多个被观察者加入自己的容器,也就是相当于观察者观察了多个被观察者(但这就打破了观察者模式)

2. UML图

在这里插入图片描述

3. 具体例子和代码

角色分配

  • AbstractSubject:被观察者抽象类
  • AbstractObserver:观察者抽象类
  • Commander:指挥官—被观察者
  • ArcherObserver:弓箭手观察者
  • ConnoneerObserver:炮手观察者
  • CavalrymanObserver:骑兵观察者

3.1 抽象类

  • AbstractSubject
package com.vinjcent.pattern.observer;/*** @author vinjcent* @description 被观察者抽象类*/
public abstract class AbstractSubject {/*** 被观察者状态*/protected int state;/*** 用于获取通知者对象本身的状态** @return 被观察者的状态变量*/public abstract int getState();/*** 用于设置通知者对象本身的状态** @param state 被观察者的状态变量*/public abstract void setState(int state);/*** 用于绑定观察者** @param observer 观察者对象*/public abstract void attach(AbstractObserver observer);/*** 接触绑定观察者** @param observer 观察者对象*/public abstract void detach(AbstractObserver observer);/*** 被观察者通知所有观察者,让观察者根据绑定的被观察者的当前状态getState()执行自己的update()方法*/public abstract void notifyAllObservers();
}
  • AbstractObserver
package com.vinjcent.pattern.observer;/*** @author vinjcent* @description 观察者抽象类*/
public abstract class AbstractObserver {/*** 观察对象*/protected AbstractSubject subject;/*** 观察者观察之后的行为*/public abstract void update();
}

3.2 被观察者

  • Commander
package com.vinjcent.pattern.observer;import java.util.HashSet;
import java.util.Set;/*** @author vinjcent* @description 指挥官---被观察者*/
public class Commander extends AbstractSubject {/*** 士兵集合*/private final Set<AbstractObserver> soldiers = new HashSet<>();@Overridepublic int getState() {return this.state;}@Overridepublic void setState(int state) {this.state = state;notifyAllObservers();}@Overridepublic void attach(AbstractObserver soldier) {this.soldiers.add(soldier);}@Overridepublic void detach(AbstractObserver soldier) {this.soldiers.remove(soldier);}@Overridepublic void notifyAllObservers() {// 通知所有观察者if (this.soldiers.isEmpty()) {return;}this.soldiers.forEach(AbstractObserver::update);}
}

3.3 观察者

  • ArcherObserver
package com.vinjcent.pattern.observer;/*** @author vinjcent* @description 弓箭手观察者*/
public class ArcherObserver extends AbstractObserver {public ArcherObserver(AbstractSubject subject) {this.subject = subject;this.subject.attach(this);}@Overridepublic void update() {System.out.println("Archer String: "+ Integer.toBinaryString(subject.getState()));}}
  • ConnoneerObserver
package com.vinjcent.pattern.observer;/*** @author vinjcent* @description 炮手观察者*/
public class ConnoneerObserver extends AbstractObserver {public ConnoneerObserver(AbstractSubject subject) {this.subject = subject;this.subject.attach(this);}@Overridepublic void update() {System.out.println("Connoneer String: "+ Integer.toBinaryString(subject.getState()));}
}
  • CavalrymanObserver
package com.vinjcent.pattern.observer;/*** @author vinjcent* @description 骑兵观察者*/
public class CavalrymanObserver extends AbstractObserver {public CavalrymanObserver(AbstractSubject subject) {this.subject = subject;this.subject.attach(this);}@Overridepublic void update() {System.out.println("Cavalryman String: "+ Integer.toBinaryString(subject.getState()));}
}

3.4 测试主函数

package com.vinjcent.pattern.observer;/*** @author vinjcent* 观察者模式*/
public class Main {public static void main(String[] args) {Commander commander = new Commander();new ArcherObserver(commander);new CavalrymanObserver(commander);new ConnoneerObserver(commander);System.out.println("第一次改变: ");commander.setState(22);System.out.println("第二次改变: ");commander.setState(23);}}
  • 测试结果

在这里插入图片描述

4. 使用场景

  • 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用
  • 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度
  • 一个对象必须通知其他对象,而并不知道这些对象是谁
  • 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制

注意事项:

1、JAVA 中已经有了对观察者模式的支持类

2、避免循环引用

3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式


http://www.ppmy.cn/news/22733.html

相关文章

路由器连接WIFI组网

白驹过隙&#xff0c;逝者如斯。经过断断续续几个月的更新&#xff0c;关于无线路由器和Wi-Fi的介绍终于告一段落。 其实&#xff0c;这个话题下还有很多很多的内容没有涉及到&#xff0c;然生有涯而知无涯&#xff0c;只能在此暂且搁笔&#xff0c;后续缘起再续。 下面&…

第九章(14):STL之常用拷贝和替换算法

文章目录前情回顾常用拷贝算法copy常用替换算法replacereplac_ifswap下一座石碑&#x1f389;welcome&#x1f389; ✒️博主介绍&#xff1a;一名大一的智能制造专业学生&#xff0c;在学习C/C的路上会越走越远&#xff0c;后面不定期更新有关C/C语法&#xff0c;数据结构&…

JVM入门知识总结

在学习虚拟机之前我们要知道为什么要学习虚拟机呢?首先就是增加自己的知识,其次就是面试的需要,其实不懂 JVM 也可以照样写出优质的代码&#xff0c;但是不懂 JVM 有可能别被面试官虐得体无完肤.一.虚拟机的概述虚拟机&#xff08;Virtual Machine&#xff09;&#xff0c;就是…

8 个很棒的 Vue 开发技巧

1.路由参数解耦通常在组件中使用路由参数&#xff0c;大多数人会做以下事情。export default {methods: {getParamsId() {return this.$route.params.id}} }在组件中使用 $route 会导致与其相应路由的高度耦合&#xff0c;通过将其限制为某些 URL 来限制组件的灵活性。正确的做…

MySQl学习(从入门到精通 1.5)

MySQl学习&#xff08;从入门到精通 1.5&#xff09;第 08 章_聚合函数1. 聚合函数介绍1. 1 AVG和SUM函数1. 2 MIN和MAX函数1. 3 COUNT函数2. 1 基本使用2. 2 使用多个列分组2. 3 GROUP BY中使用WITH ROLLUP3. HAVING3. 1 基本使用3. 2 WHERE和HAVING的对比4. SELECT的执行过程…

我的网站上线了!

最近有段时间没有写原创文章了&#xff0c;恰好这两天正在翻阅历史文章的时候&#xff0c;发现文章中的图片竟然裂了&#xff1f;顿时冒了一身冷汗&#xff0c;因为每逢遇到这种情况&#xff0c;动辄需要花费一周的时间迁移图片。。。。。。 当我直接访问图片 url 的时候&#…

一文速学-Pandas数据展示选项设置详解+实例代码操作展示

目录 前言 一、获取数据展示参数 二、可选展示选项 1.describe_option&#xff08;&#xff09; 2.get_option()/set_option() 3.reset_option() 4. option_context&#xff08;&#xff09; 三、 在Python/IPython环境中设置启动选项 四、常用选项 1.display.max_rows…

【自学Docker】Docker login logout

大纲 Docker login & logout docker login命令 docker login 命令用于登陆到一个 Docker镜像仓库&#xff0c;如果未指定镜像仓库地址&#xff0c;默认为官方仓库 Docker Hub。 如果用户使用 docker login 命令登录官方仓库&#xff0c;首先我们需要在官方仓库注册一个账…