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

article/2025/9/24 17:51:10

目录

观察者模式(Observer)

理解

UML图

优缺点

观察者模式在各语言中的支持

Java

C#

实例

小丑表演

办公室摸鱼

投资者与股票


观察者模式(Observer)

本质:触发联动

目标对象变化时,会通知所有登记的观察者对象行动。

别名:

发布-订阅(Publish/Subscribe)模式
模型-视图(Model/View)模式
源-监听器(Source/Listener)模式
从属者(Dependents)模式。

理解

  • 是一对多的单向依赖:多个观察者对象同时观察一个目标对象, 观察者依赖于目标。
  • 相互观察时注意死循环
    • 若 A、B观察C,B、C观察A,则需要两套观察模式,否则死循环
  • 通知的顺序:同时
    • 多个观察者是平行的(虽然是循环,但几乎同时完成)

减少对象之间的耦合有利于系统的复用,但同时需要使这些低耦合度的对象行动协调一致(观察者对象行动的方法要保持参数一致)

UML图:

在这里插入图片描述

Subject:抽象被观察者(目标)

  • 一个目标可以被多个观察者观察。目标变化时,目标会通知所有登记的观察者
  • 拥有一个抽象观察者的引用

Observer:抽象观察者

  • 得到目标的通知时更新自己。更新方法,图上Updata()

ConcreteSubject:具体被观察者

  • 将状态存入具体现察者对象。自己变化时,给所有登记的观察者发出通知

ConcreteObserver:具体观察者

  • 拥有一个指向具体目标的引用


优缺点

优点:

  • 实现了观察者和被观察者之间的抽象耦合。
  • 动态联动
  • 广播通信。被观察者会向所有的登记的观察者发出通知。

缺点:

可能会引起无谓的操作。

由于采用广播方式,不管观察者需不需要,每个观察者都会被调用update方法

观察者模式在各语言中的支持

Java

1.Observer接口
Observer为java.util包下的一个接口,源码如下:

public interface Observer {void update(Observable o, Object arg);
}

该接口约定了观察者的行为

所有观察者需要在被观察对象发生变化时做出反应,所做的具体反应就是实现Observer接口的update方法。

  • 当前接口代表观察者,要与被观察对象交互,需要持有被观察对象Observable的引用,因此update方法一个参数的类型是Observable
  • 与调用者通信,则是使用Object类型的参数。该参数是调用者调用Observable实例的notifyObservers(Object obj)方法时传入的,也可以不传。

第一个参数是为观察者提供了一种拉取数据的方式,update中的业务可以根据所需去拉自己想要的被观察对象的信息(一般被观察对象中提供getter),第二个参数则是由调用者调用notifyObservers(Object obj)将一些信息推过来。通过这两个参数,观察者,被观察对象,调用者(调用通知刷新方法的可能是被观察对象本身,此时只存在观察者与被观察者两者)三者就联系起来了。

2.Observable类
java.util.Observable

该类实现了被观察者的功能

该类的成员变量和方法如下:

public class Observable {private boolean changed = false;private Vector<Observer> obs;public Observable(){};protected synchronized void setChanged(){};protected synchronized void clearChanged(){};public synchronized void addObserver(Observer o){};public synchronized void deleteObserver(Observer o) {};public synchronized void deleteObservers(){};public synchronized boolean hasChanged(){};public synchronized int countObservers(){};public void notifyObservers(){};public void notifyObservers(Object arg){};
}

C#

.NET中提供了Delegate与Event机制
 


实例


小丑表演

镇上来了一位小丑,为大家表演节目,所有观看的观众会根据小丑表演的精彩与否来做出相应的反应,比如表演的好就鼓掌喝彩,表演的不好就倒喝彩,表演完毕观众就退场。

分析:

  • 观众是观察者,应该实现Observer接口
  • 小丑是被观察者,应该继承Observable类

观众会根据小丑表演的状态改变自己的行为

package com.test;import java.util.Observable;
import java.util.Observer;//观众类
public class Viewer implements Observer{private int id;public Viewer(int id) {super();this.id = id;}public int getId() {return id;}public void setId(int id) {this.id = id;}@Overridepublic void update(Observable o, Object arg) {Integer state=(Integer) arg;switch(state) {case Clown.good:applause();break;case Clown.bad:cheerback();break;case Clown.complete:exit();break;}}private void applause() {System.out.println(id+"号观众鼓掌了");}private void cheerback() {System.out.println(id+"号观众喝倒彩");}private void exit() {System.out.println(id+"号观众退场了");}}
package com.test;import java.util.Observable;
import java.util.Random;
//小丑类
public class Clown extends Observable{public static final int good=0;public static final int bad=1;public static final int complete=2;public void perform() {System.out.println("小丑开始表演");//0-2随机值int random=new Random().nextInt(2);switch(random) {case good:System.out.println("小丑表演得好");break;case bad:System.out.println("小丑表演得不好");break;}setChanged();//小丑的状态传给观众的update方法的第二个参数notifyObservers(random);}public void exit() {System.out.println("小丑表演结束");setChanged();//小丑的状态传给观众的update方法的第二个参数notifyObservers(complete);}}
package com.test;public class Test {public static void main(String[] args) {Clown c=new Clown();for(int i=1;i<4;i++) {Viewer v=new Viewer(i);c.addObserver(v);System.out.println(v.getId()+"号观众入席");}c.perform();c.exit();}}

办公室摸鱼

办公时间摸鱼, 在老板到来时,前台负责通知同事进入工作状态

  • 观察者:两个同事,需要实现Observer接口
  • 被观察者有两个:前台和老板,应该继承Observeable类
    • 前台通知:摸鱼的人改为工作状态
    • 老板通知:摸鱼的人被老板发现并且批评

package qiqi.work;import java.util.Observable;public class Boss extends Observable {private int state=0;//离开public void com() {System.out.println("我大老板回来了");state=2;//回来setChanged();//必须要写notifyObservers(state);//发出通知}}
package qiqi.work;import java.util.Observable;public class Secretary extends Observable{private int state=0;//老板离开public void see() {System.out.println("老板回来了");state=1;//老板回来啦setChanged();notifyObservers(state);}}
package qiqi.work;import java.util.Observable;
import java.util.Observer;public abstract class Observers implements Observer{protected String name;public Observers(String name) {super();this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic abstract void update(Observable o, Object arg);public abstract void see();public abstract void exit();public abstract void criticize();}
package qiqi.work;import java.util.Observable;
import java.util.Observer;public class NBAObserver extends Observers{public NBAObserver(String name) {super(name);// TODO Auto-generated constructor stub}@Overridepublic void update(Observable o, Object arg) {// TODO Auto-generated method stubInteger state=(Integer)arg;switch(state){case 0:see();break;case 1:exit();break;case 2:criticize();break;//老板通知}}@Overridepublic void see() {// TODO Auto-generated method stubSystem.out.println(name+"看NBA");}@Overridepublic void exit() {// TODO Auto-generated method stubSystem.out.println(name+"停止看NBA,开始工作");}@Overridepublic void criticize() {// TODO Auto-generated method stubSystem.out.println(name+"你居然在工作时间看NBA,快工作!");}}
package qiqi.work;import java.util.Observable;public class StockObserver extends Observers{public StockObserver(String name) {super(name);// TODO Auto-generated constructor stub}@Overridepublic void see() {// TODO Auto-generated method stubSystem.out.println(name+"看股票");}@Overridepublic void exit() {// TODO Auto-generated method stubSystem.out.println(name+"停止看股票,开始工作");}@Overridepublic void criticize() {// TODO Auto-generated method stubSystem.out.println(name+"你居然在工作时间看股票,快工作!");}@Overridepublic void update(Observable o, Object arg) {// TODO Auto-generated method stubInteger state=(Integer)arg;switch(state){case 0:see();break;case 1:exit();break;//秘书通知case 2:criticize();break;//老板通知}}}
package qiqi.work;public class Test {public static void main(String[] args) {NBAObserver nba=new NBAObserver("张三");StockObserver stock=new StockObserver("李四");Secretary s=new Secretary();s.addObserver(nba);s.addObserver(stock);s.see();Boss b=new Boss();b.addObserver(nba);b.addObserver(stock);b.com();}}

投资者与股票

注册的投资者在股票市场发生变化时,可以自动得到通知的功能

当观察者数量无法确定时,可以用一个List来保存观察者

不使用Observer类和接口,代码如下:

package com.invest;//观察者接口
public interface IInvestor {void update(Stock stock);
}
package com.invest;//具体观察者
public class Investor implements IInvestor{private String name;private Stock stock;public Investor(String name) {super();this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Stock getStock() {return stock;}public void setStock(Stock stock) {this.stock = stock;}@Overridepublic void update(Stock stock) {System.out.println(name+" "+stock.symbol+" "+stock.price);}}
package com.invest;import java.util.ArrayList;//抽象被观察者
public abstract class Stock {protected String symbol;protected double price;private ArrayList<Investor> investors=new ArrayList<Investor>();public Stock(String symbol, double price) {super();this.symbol = symbol;this.price = price;}public String getSymbol() {return symbol;}public void setSymbol(String symbol) {this.symbol = symbol;}public double getPrice() {return price;}public void setPrice(double price) {this.price = price;Notify();//状态改变,触发通知}public void Attach(Investor investor) {investors.add(investor);}public void Detach(Investor investor) {investors.remove(investor);}public void Notify() {for(Investor i:investors)i.update(this);}}
package com.invest;//具体被观察者
public class QQ extends Stock{public QQ(String symbol, double price) {super(symbol, price);// TODO Auto-generated constructor stub}}
package com.invest;public class Test {public static void main(String[] args) {// TODO Auto-generated method stubInvestor i1=new Investor("张三");Investor i2=new Investor("李四");Stock s=new QQ("腾讯",120.1);s.Attach(i1);s.Attach(i2);s.setPrice(120.4);s.setPrice(121.1);s.Detach(i2);s.setPrice(122.2);}}

使用Observer接口和Observerable类,代码如下:

package com.invest1;import java.util.Observable;
import java.util.Observer;//观察者
public class Investor implements Observer{private String name;public Investor(String name) {super();this.name = name;}@Overridepublic void update(Observable o, Object arg) {Integer a=(Integer)arg;if(a==Stock.getState()) System.out.println(name+"买的股票:"+Stock.getSymble()+"价格变为"+Stock.getPrice());}}

package com.invest1;import java.util.Observable;public class Stock extends Observable{private static String symble;private static double price;private static int state=0;//1表示状态变化,0表示不变public Stock(String symble, double price) {super();this.symble = symble;this.price = price;}public static String getSymble() {return symble;}public void setSymble(String symble) {this.symble = symble;}public static double getPrice() {return price;}public void setPrice(double price) {this.price = price;change();//价格改变,被观察者的状态改变}public void change() {setChanged();setState(1);notifyObservers(getState());//1表示状态变化,0表示不变}public static int getState() {return state;}public static void setState(int state) {Stock.state = state;}}
package com.invest1;public class Main {public static void main(String[] args) {Investor i1=new Investor("张三");Investor i2=new Investor("李四");Stock s=new Stock("谷歌",120.1);s.addObserver(i1);s.addObserver(i2);s.setPrice(122.1);}}


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

相关文章

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

相关文章 设计模式系列 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…

数据治理浅谈之数据字典

导读 数据字典作为数据治理整体体系中重要的一环&#xff0c;理解和使用数据字典尤为重要。本文将从数据字典概念、定义、结构、应用、治理等方面进行详细阐述。 01概念 数据字典&#xff08;Data Dictionary&#xff09;是一个容器&#xff0c;用于包含有组织定义和使用的所…

【数据库管理】⑩数据字典

1. 数据字典的概述 数据字典&#xff08;Data Dictionary&#xff09;是数据库管理系统中的一个重要组成部分&#xff0c;它是一个存储数据库元数据的集合&#xff0c;包含了数据库中所有对象的定义和描述信息。数据字典可以帮助用户了解数据库中的各种对象和数据结构&#xff…