观察者模式(Observer Pattern)

article/2025/9/24 17:47:29

一、什么是观察者模式

观察者模式是一种对象行为模式。它定义对象间的一种一对多的依赖关系,当一个对象(目标对象)的状态发生改变时,所有依赖于它的对象(观察对象)都得到通知并被自动更新。特点:被观察者和观察者一般是一对多的关系,一个被观察者对应多个观察者,当一个被观察者的状态发生改变时,被观察者通知观察者,然后可以在观察者内部进行业务逻辑的处理。

JDK 提供了 一套观察者模式的实现,在java.util包中, java.util.Observable类和java.util.Observer接口。Observable 是被观察者,Observer 是观察者。

在观察者模式中,主题是通知的发布者,它发出通知时并不需要知道谁是它的观察者,可以有任意数目的观察者订阅并接收通知。观察者模式不仅被广泛应用于软件界面元素之间的交互,在业务对象之间的交互、权限管理等方面也有广泛的应用。

面向对象设计的一个原则是:系统中的每个类将重点放在某一个功能上,而不是其他方面。一个对象只做一件事情,并且将其做好。观察者模式完美的将观察者和被观察的对象分离开,在模块之间划定了清晰的界限,提高了应用程序的可维护性和重用性。

观察者模式,又叫发布-订阅模式(Publish/Subscribe Pattern)。UML 结构图如下:

1️⃣【主题 Subject通知的发布者】它把所有对观察者对象的引用文件存在了一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供了一个接口,可以增加和删除观察者对象。

首先定义一个观察者数组,并实现增、删及通知操作。它的职责很简单,就是定义谁能观察,谁不能观察,用 Vector 是线程同步的,比较安全,也可以使用 ArrayList,是线程异步的,但不安全。

public class Subject {//观察者数组private Vector<Observer> oVector = new Vector<>();//增加一个观察者public void addObserver(Observer observer) {this.oVector.add(observer);}//删除一个观察者public void deleteObserver(Observer observer) {this.oVector.remove(observer);}//通知所有观察者public void notifyObserver() {for(Observer observer : this.oVector) {observer.update();}}
}

2️⃣【抽象观察者 Observer】为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。它的每一个实现类都是具体观察者。

public interface Observer {//更新public void update();
}

3️⃣【具体主题 ConcreteSubject】将有关状态存入具体观察者对象,在具体主题内部状态改变时,给所有登记过的观察者发出通知。继承 Subject 类,在这里实现具体业务,在具体项目中,该类会有很多变种。

public class ConcreteSubject extends Subject {//具体业务public void doSomething() {//...super.notifyObserver();}
}

4️⃣【具体观察者 ConcreteObserver】实现抽象观察者 Observer 所要求的更新接口,以便使本身的状态与主题的状态相协同。

public class ConcreteObserver implements Observer {@Overridepublic void update() {System.out.println("收到消息,进行处理");}
}

5️⃣【客户端 Client】首先创建一个主题,然后定义一个观察者,将该观察者添加到该主题的观察者数组中,进行测试。

public class Client {public static void main(String[] args) {//创建一个主题ConcreteSubject subject = new ConcreteSubject();//定义一个观察者Observer observer = new ConcreteObserver();//观察subject.addObserver(observer);//开始活动subject.doSomething();}
}

运行结果如下:

二、观察者模式的应用简例

假设上班时间有一部分同事在看股票,一部分同事在看 NBA,这时老板回来了,前台通知了部分同事,这些同事没被发现,而没被通知到的同事被抓现行,被老板亲自“通知”关闭网页,UML 图如下:

1️⃣主题 Subject通知的发布者

public interface Subject {//增加public void attach(Observer observer);//删除public void detach(Observer observer);//通知public void notifyObservers();//状态public void setAction(String action);public String getAction();
}

2️⃣观察者

public abstract class Observer {protected String name;protected Subject subject;public Observer(String name, Subject subject) {this.name = name;this.subject = subject;}public abstract void update();
}

3️⃣具体通知者
前台 Secretary 和老板 Boss 作为具体通知者,实现 Subject 接口。Secretary 类的代码如下:

public class Secretary implements Subject {//同事列表private List<Observer> observers = new LinkedList<>();private String action;//添加@Overridepublic void attach(Observer observer) {observers.add(observer);}//删除@Overridepublic void detach(Observer observer) {observers.remove(observer);}//通知@Overridepublic void notifyObservers() {for(Observer observer : observers) {observer.update();}}//前台状态@Overridepublic String getAction() {return action;}@Overridepublic void setAction(String action) {this.action = action;}
}

4️⃣具体观察者
StockObserver 是看股票的同事,NBAObserver 是看 NBA 的同事,作为具体观察者,继承 Observer 类。StockObserver 类的代码如下:

public class StockObserver extends Observer {public StockObserver(String name, Subject subject) {super(name, subject);}@Overridepublic void update() {System.out.println(subject.getAction() + "\n" + name + "关闭股票行情,继续工作");}
}

5️⃣Client:前台作为通知者,通知观察者。这里添加 adam 和 tom 到通知列表,并从通知列表中删除了 adam,测试没在通知列表中的对象不会收到通知。

public class Client {public static void main(String[] args) {//前台为通知者Secretary secretary = new Secretary();StockObserver observer = new StockObserver("adam", secretary);NBAObserver observer2 = new NBAObserver("tom", secretary);//前台通知secretary.attach(observer);secretary.attach(observer2);//adam没被前台通知到,所以被老板抓了个现行secretary.detach(observer);//老板回来了secretary.setAction("小心!Boss回来了!");//发通知secretary.notifyObservers();}
}

运行结果如下,只有 tom 接收到了通知:

6️⃣Client:老板作为通知者,通知观察者。这里将 tom 从老板的通知列表中移除,老板只通知到了 adam。

public class Client {public static void main(String[] args) {//老板为通知者Boss boss = new Boss();StockObserver observer = new StockObserver("adam", boss);NBAObserver observer2 = new NBAObserver("tom", boss);//老板通知boss.attach(observer);boss.attach(observer2);//tom没被老板通知到,所以不用挨骂boss.detach(observer2);//老板回来了boss.setAction("咳咳,我大Boss回来了!");//发通知boss.notifyObservers();}
}

运行结果如下,只有 adam 挨骂了:

三、观察者模式的特性

1️⃣优点

  1. 一个对象状态改变,所有的依赖对象都将得到通知。
  2. 使用面向对象技术。
  3. 观察者和被观察者是抽象耦合的
  4. 建立了一套触发机制

2️⃣缺点

  1. 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间
  2. 如果观察者和观察目标间有循环依赖,可能导致系统崩溃
  3. 没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的

3️⃣使用场景

  1. 关联行为场景
  2. 事件多级触发场景
  3. 跨系统的消息变换场景,如消息队列的处理机制。

4️⃣应用实例

  1. 手机丢了,委托别人给其他人发消息通知。
  2. 通知老师/老板来了。
  3. 拍卖,拍卖师观察最高标价,然后通知给其它竞价者竞价。
  4. 在一个目录下建立一个文件,会同时通知目录管理器增加目录,并通知磁盘减少空间,文件是被观察者,目录管理器和磁盘管理器是观察者。
  5. 猫叫了一声,吓着了老鼠,也惊到了主人,猫是被观察者,老鼠和人是观察者。

5️⃣注意事项

  1. 避免循环引用
  2. 如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
  3. 当一个对象的改变需要同时改变其它对象,并且它不知道具体有多少对象有待改变的时候,应该考虑使用观察者模式。
  4. 将一个系统分割成一系列相互协作的类有一个很不好的副作用,就是需要维护相关对象间的一致性。为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便,而观察者模式所做的工作就是在解除耦合。

http://chatgpt.dhexx.cn/article/ZnnQKmL9.shtml

相关文章

设计模式之观察者模式详解(附应用举例实现)

文章目录 1 观察者模式介绍2 观察者模式详解2.1 观察者模式结构2.2 观察者模式实现2.3 观察者模式应用举例 3 观察者模式与MVC 1 观察者模式介绍 “红灯停&#xff0c;绿灯行”。在这个过程中&#xff0c;交通信号灯是汽车的观察目标&#xff0c;而汽车则是观察者。随着交通信…

23种设计模式——观察者模式

目录 观察者模式&#xff08;Observer&#xff09; 理解 UML图 优缺点 观察者模式在各语言中的支持 Java C# 实例 小丑表演 办公室摸鱼 投资者与股票 观察者模式&#xff08;Observer&#xff09; 本质&#xff1a;触发联动 目标对象变化时&#xff0c;会通知所有登…

设计模式(五)观察者模式

相关文章 设计模式系列 1.观察者模式模式简介 定义 观察者模式&#xff08;又被称为发布-订阅&#xff08;Publish/Subscribe&#xff09;模式&#xff0c;属于行为型模式的一种&#xff0c;它定义了一种一对多的依赖关系&#xff0c;让多个观察者对象同时监听某一个主题对…

Win64 驱动内核编程-28.枚举消息钩子

枚举消息钩子 简单粘贴点百度的解释&#xff0c;科普下消息钩子&#xff1a; 钩子是WINDOWS中消息处理机制的一个要点&#xff0c;通过安装各种钩子&#xff0c;应用程序能够设置相应的子例程来监视系统里的消息传递以及在这些消息到达目标窗口程序之前处理它们。 钩子的种类很…

Anti 消息钩子注入

MSDN上对消息钩子的描述&#xff1a; The SetWindowsHookEx function installs an application-defined hook procedure into a hook chain. You would install a hook procedure to monitor the system for certain types of events. These events are associated either wit…

注入(4)--消息钩子注入(SetWindowsHookEX)

SetWindowsHookEx函数是微软提供给程序开发人员进行消息拦截的一个API。不过,他的功能不仅可以用作消息拦截,还可以进行DLL注入。 SetWindowsHookEx原型声明如下: WINUSERAPI HHOOK WINAPI SetWindowsHookExW(_In_ int idHook,_In_ HOOKPROC lpfn,_In_opt_ HINSTANCE hmod,…

DLL注入技术之消息钩子注入

消息钩子注入原理是利用Windows 系统中SetWindowsHookEx()这个API&#xff0c;他可以拦截目标进程的消息到指定的DLL中导出的函数&#xff0c;利用这个特性&#xff0c;我们可以将DLL注入到指定进程中。主要流程如下图所示 1&#xff0e;准备阶段 需要编写一个DLL&#xff…

关于采用消息钩子机制的透明加密的简单破解

采用消息钩子机制的透明加密方式在各大企业中很常见&#xff0c;简单实用。文件在磁盘上以密文方式存储&#xff0c;打开时首先被加密软件客户端注入的钩子&#xff08;hook&#xff09;截获&#xff0c;解密成明文后再提交给相应程序&#xff1b;保存时同样被钩子截获&#xf…

VC 消息钩子编程

分享一下我老师大神的人工智能教程&#xff01;零基础&#xff0c;通俗易懂&#xff01;http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章。分享知识&#xff0c;造福人民&#xff0c;实现我们中华民族伟大复兴&#xff01; 一、消息钩子的概念 1、基本概念 Windows应…

Windows消息钩子[键盘监控]

之前看书&#xff0c;看到一眼消息钩子&#xff0c;一直没实践&#xff0c;现在有空弄了下&#xff0c; 主要原理是利用windows自带SetWindowsHookEx API函数 HHOOK SetWindowsHookEx( int idHook, //hook形式 HOOKPROC lpfn //hook过程 HI…

一、C++反作弊对抗实战 (基础篇 —— 4.利用消息钩子注入DLL)

这里将主要用到Windows API函数SetWindowsHookEx,它是微软提供给程序开发人员进行消息拦截的一个API,不过它的功能不仅用作消息拦截,还可以进行DLL注入。 一、关于SetWindowsHookEx 提这个函数在微软官网MSDN文档中原型声明如下: SetWindowsHookExA function (winuser.h…

windows10 记事本进程 键盘消息钩子 dll注入

看了很多文档&#xff0c;垮了很多坎&#xff0c;终于完成了这个demo&#xff1b; 有很多个人理解&#xff0c;可能不完全正确&#xff0c;见谅&#xff1b; 先上实现的图片&#xff1a; 如图&#xff0c;我通过SetWindowsHookEx()函数向记事本进程中当前窗口线程注入了自己写…

消息钩子学习工程

前奏 近来一直在自学Windows Hook相关的知识&#xff0c;已经尝试多种注入方式。尤其对消息钩子方式很感兴趣&#xff0c;因为看到Spy能够截获系统中绝大多数应用的消息流&#xff0c;就很想知道它的工作原理&#xff0c;打算制作属于自己的Spy。 消息钩子简介&#xff1a; 消息…

DLL注入技术之消息钩子注入(HOOK简单的实现)

低头不是认输&#xff0c;是要看清自己的路。仰头不是骄傲&#xff0c;是看见自己的天空。——致自己 Hook&#xff0c;是Windows消息处理机制的一个平台&#xff0c;应用程序可以在上面设置子程序以监视指定窗口的某种消息&#xff0c;而且所监视的窗口可以是其他进程所创建的…

powerdesigner制作数据字典

powerdesigner制作数据字典TOC 配置好数据库连接之后&#xff0c;点击File→New Model 这步是默认的点OK就行。 数据传输完之后点击保存。 选择好保存路径后&#xff0c;关闭软件&#xff08;关闭时注意选择保存&#xff09;&#xff0c;然后再重现打开。 重新打开Po…

设计 - 数据字典

文档分类 写文档目的 你有没有遇到过开晨会、周会的时候某个问题已经讨论的很清晰。 但是几天后或者临近周末的时候再说这个问题的时候&#xff0c;团队中有的童鞋会说:“我不知道&#xff0c;没有说过这个问题或者这个方案”&#xff0c;因此而造成的BB事很伤神费心。 为了避免…

Oracle 数据字典详解

Oracle 数据字典详解 什么叫数据字典&#xff1f; 数据字典指的是描述数据的数据。 举个例子&#xff1a;我们在数据库里面创建了一个表&#xff0c;这个表位于哪个数据文件、这个表有哪些列、这个表的每一个列的数据类型、这个表的约束等等。这些信息都是描述这个表的&#…

数据字典及其使用(方案一)

1 数据字典 1.1 什么是数据字典 将如下这些具有相同类型的配置项&#xff0c;配置到系统的数据字典表中&#xff0c;方便系统维护&#xff0c;由超级管理员统一在后台进行数据字典维护&#xff0c;如果用户需求要增加变更配置项&#xff0c;只需要修改数据字典表记录…

什么是mysql数据字典_数据字典是什么?

展开全部 数据e69da5e6ba9062616964757a686964616f31333366306434字典是指对数据的数据项、数据结构、数据流、数据存储、处理逻辑等进行定义和描述,其目的是对数据流程图中的各个元素做出详细的说明,使用数据字典为简单的建模项目。简而言之,数据字典是描述数据的信息集合,…

matlab新建数据字典及如何导入

一、如何创建 点击model explorer 依次点击file->new->Data dictionary 输入数据字典的名字 创建之后为一下界面&#xff1a; 点击 进行创建 右键save change 进行保存 没保存时候带星号 &#xff0c;保存之后信号消失。 二、如何导入 点击file—>model proper…