观察者模式(Observer) 简介

article/2025/9/24 16:52:41

一, 观察者模式(Observer) 的定义

观察者模式:   定义了一种 1对多 的依赖关系, 让多个观察者对象同时监听1个主题对象.

                        这个主题对象在状态发生变化时, 会通知所有的观察者对象, 使它们能够同时更新自己.

稍微解释一下 这个1 对多 的依赖关系.

 1对多 这个关键词我们常常在DB 表设计里提到,  但是这里的意思是有点区别的.

                    

首先,  1 是1个对象, 而不是1个类,     而多也是指多个对象, 而不是多个类.

其次,  这里的多个对象可以是多个不同的类的对象.  甚至是毫无关系的多个类.

再次, 这个依赖关系, 到底是1个对象依赖多个对象, 还是多个对象依赖1个对象呢.

在这里的定义来讲答案是后者.

但是, 实际上, 1个观察者也可以观察多个被观察者的. (但这就不属于观察者模式了)

所以, 观察者模式(Observer) 也叫做 发布-订阅模式(publish/Subscribe).

相当与, 多个读者同时收听1个电台.

二, 观察者模式(Observer) 的各个角色.

首先睇下Observer 模式的标准UML图.

我们大概得出上图有4个角色.

2.1 Observer (观察者)

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

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

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

Subject是1个接口, 我们可以理解为1个抽象被观察者类.

我们可以见到它有5个方法.

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

setState(), 和 getState() 用于获取和设置通知者对象本身的状态, 这个状态通常是传送给观察者们的信息或参数.

也就是说观察者模式到底在观察什么. 无非就是观察被观察者的这个状态.

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

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

这个(些)就是具体的被观察者类, 只要实现了Subject接口, 就可以添加1些对象作为自己的观察者(或者叫粉丝啦)

写到这里, 大家都会了解到, 这个类里面肯定有1个容器, 用于存放观察者的对象.

这个容器可以根据需要由具体的被观察者选择, 通常是无序不能重复的Set容器(例如 HashSet)

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

2.4 ConcreteObserver (具体观察者类)

这些类可以是毫无关联的类, 但是它们都必须实现Observer接口

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

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

三, 1个具体例子和代码.

下面我们用1个具体例子来简单实现这个模式.

我们假定1个事件有3个角色.

1. 指挥者.(Commander)

            指挥炮手打炮, 他可以让让若干个炮手和炮灰纳入自己的命令范围.

2. 炮手, (CannonShooter)

          一旦指挥者通知目标, 若干个炮手就往哪个目标轰击.

3. 炮灰 (CannonFodder)

         一旦指挥者通知, 炮灰就趴下..

也就是说, 炮手必须知道指挥这的状态(目标信号), 到底打谁.

而炮灰是无序关心到底打哪里的, 一旦接到通知, 趴下就是了.

3.1 UML图

3.2 Subject接口 代码

public interface Subject {public void attach(Observer obs);public void detach(Observer obs);public void sNotify();   //notify is a finel method of Object classpublic int  getState();public void setState(int state);
}


5个方法的意义上面已经解释过了.

通知方法之所以不写成notify(), 是因为notify()本身是Object类的1个finel方法

3.2 Observer接口 代码

public interface Observer {public void update();
}


只有1个抽象方法update()

3.3 Commander 类 代码

import java.util.HashSet;
import java.util.Iterator;
public class Commander implements Subject{private int targetPlaceID;private HashSet<Observer> gunnerSet = new HashSet<Observer>();@Overridepublic void attach(Observer obs){this.gunnerSet.add(obs);}@Overridepublic void detach(Observer obs) {this.gunnerSet.remove(obs);}@Overridepublic void sNotify() {if (this.gunnerSet.isEmpty()){return;}Iterator itr = this.gunnerSet.iterator();while (itr.hasNext()){Observer obs = (Observer)itr.next();obs.update();}}@Overridepublic int getState() {// TODO Auto-generated method stubreturn this.targetPlaceID;}@Overridepublic void setState(int state) {// TODO Auto-generated method stubthis.targetPlaceID = state;}}


它重写了接口所有方法.

所谓的notify()方法, 无非就是遍历自己容器的所有观察者, 该干嘛的干嘛(遍历调用它们的update())方法

3.4 CannonShooter 类 代码

public class CannonShooter implements Observer{private Subject cmder;public CannonShooter(Subject cmder){this.cmder = cmder;}public Subject getCmder() {return cmder;}public void setCmder(Subject cmder) {this.cmder = cmder;}public void fireCannon(int targetPlace){System.out.println(this.getClass().getSimpleName() + ": fired on target(id:" + targetPlace + ") by Cannon");}@Overridepublic void update() {// TODO Auto-generated method stubfireCannon(cmder.getState());}
}


可以见到,  炮手必须知道指挥者的状态信息, 所以它里面必须有个当前指挥者的对象成员.

3.5 CannonFodder 类 代码

public class CannonFodder implements Observer{private int id;public CannonFodder(int id){this.id = id;}public void getDown(){System.out.println(this.getClass().getSimpleName() +" id:"+ this.id + " getDowned");}@Overridepublic void update() {// TODO Auto-generated method stubthis.getDown();}
}


炮灰无需关心指挥者的状态, 里面只需要重写自己的update()方法就ok.

3.6 CannonFodder 类 代码

public class ClientObserver {public static void f(){Commander cmder = new Commander();CannonShooter cster = new CannonShooter(cmder);CannonFodder cfder1 = new CannonFodder(1);CannonFodder cfder2 = new CannonFodder(2);CannonFodder cfder3 = new CannonFodder(3);cmder.setState(107);cmder.attach(cster);cmder.attach(cfder1);cmder.attach(cfder2);cmder.attach(cfder3);cmder.sNotify();cmder.setState(108);cmder.detach(cfder3);cmder.sNotify();}
}

上面的代码不难看懂.

无非就是实例化1个指挥者, 1个炮手, 3个炮灰

首先, 指挥者通知向107目标打炮,   炮手射了, 3个炮灰趴下了.

后来指挥者想向打击108目标, 但是觉得第3号炮灰不在攻击范围,   所以从自己的观察者容器里移除3号炮灰.

这时, 炮手向108号目标打击,  只有1号2号炮灰听指挥爬下.

相当灵活.

四, 观察者模式的特点和应用范围.

4.1 Observer模式的特点

上面的例子中, 观察者和被观察者的耦合性不大.

1个subject可以有任意数目的观察者Observer,  程序猿令subject发出通知时根本无需知道观察者是谁, 有多少观察者存在.

而单个观察者本身也无需知道到底有几个其他观察者同时存在.

4.2 什么时候应该使用Observer模式

很明显嘛,  就是当1个对象发生改变的同时需要同时改变其他多个对象时.

而且, 观察者模式令到耦合的双方, 依赖与接口(抽象), 而不是依赖于具体(非抽象类), 符合封闭-开放模式.

另1个例子:

package com.home.javacommon.designpattern.observer;import lombok.extern.slf4j.Slf4j;import java.util.ArrayList;
import java.util.List;interface Observer{void sNotify();
}interface Publisher<T extends Observer>{void setObserver(T t);void update();
}class Target{private String target;public String getTarget(){return this.target;}public Target(String target){this.target = target;}
}@Slf4j
class Commander implements Observer{private Target target;public void setTarget(Target target){log.info("Commander set target is {}: ", target.getTarget());this.target = target;}public Target getTarget(){return this.target;}private List<Publisher> publishersList = new ArrayList<>();public void addPublisher(Publisher publisher){this.publishersList.add(publisher);}@Overridepublic void sNotify() {log.info("Commander ordered to fire the target: {}", this.target.getTarget());for (Publisher publisher : this.publishersList) {publisher.setObserver(this);publisher.update();}}
}@Slf4j
class CannonShooter implements Publisher<Commander>{private String name;public CannonShooter(String name){this.name = name;}private Commander commander;@Overridepublic void setObserver(Commander commander) {this.commander = commander;}@Overridepublic void update() {log.info("Canoon Shooter: {} fired the target {}", this.name, this.commander.getTarget().getTarget());}
}public class ObserverClient {public static void main(String[] args){Commander mike = new Commander();mike.setTarget(new Target("The Tree"));CannonShooter jack = new CannonShooter("jack");CannonShooter bill = new CannonShooter("bill");mike.addPublisher(jack);mike.addPublisher(bill);mike.sNotify();}
}

 


http://chatgpt.dhexx.cn/article/4Pa8k978.shtml

相关文章

观察者模式

第23章 观察者模式 一、观察者模式的基本介绍 观察者模式(Observer Pattern)&#xff1a;定义对象间的一种一对多依赖关系&#xff0c;使得每当一个对象状态发生改变时&#xff0c;其相关依赖对象皆得到通知并被自动更新。 观察者模式又叫做发布-订阅&#xff08;Publish/Subsc…

观察者模式(Observer Pattern)

一、什么是观察者模式 观察者模式是一种对象行为模式。它定义对象间的一种一对多的依赖关系&#xff0c;当一个对象(目标对象)的状态发生改变时&#xff0c;所有依赖于它的对象(观察对象)都得到通知并被自动更新。特点&#xff1a;被观察者和观察者一般是一对多的关系&#xf…

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

文章目录 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;只需要修改数据字典表记录…