Java 设计模式——观察者模式

目录

  • 1.概述
  • 2.结构
  • 3.案例实现
    • 3.1.抽象观察者
    • 3.2.观察对象
    • 3.3.具体观察者
    • 3.4.具体观察对象
    • 3.5.测试
  • 4.优缺点
  • 5.使用场景
  • 6.JDK 源码解析——Observable / Observer
    • 6.1.Observable 类
    • 6.2.Observer 接口
    • 6.3.案例

1.概述

观察者模式 (Observer Pattern) 是一种行为型设计模式,又被称为发布-订阅 (Publish/Subscribe) 模式,它定义了对象之间的一种一对多的依赖关系,使得当一个对象的状态发生变化时,所有依赖于它的对象都会自动收到通知并更新

2.结构

在观察者模式中有如下角色:

  • Subject:观察对象,定义了注册观察者和删除观察者的方法。此外,它还声明了“获取现在的状态”的方法。
  • ConcreteSubject:具体观察对象,当自身状态发生变化后,它会通知所有已经注册的 Observer 角色。
  • Observer:抽象观察者,负责接收来自 Subject 角色的状态变化的通知,为此,它声明了 update 方法。
  • ConcrereObserver:具体观察者,当它的 update 方法被调用后,会去获取要观察的对象的最新状态。

3.案例实现

【例】微信公众号。在使用微信公众号时,大家都会有这样的体验,当你关注的公众号中有新内容更新的话,它就会推送给关注公众号的微信用户端。我们使用观察者模式来模拟这样的场景,微信用户就是观察者,微信公众号是观察对象,有多个的微信用户关注了程序猿这个公众号。 具体类图如下:
在这里插入图片描述
具体实现代码如下:

3.1.抽象观察者

Observer.java

public interface Observer {void update(String message);
}

3.2.观察对象

Subject.java

//观察对象
public interface Subject {//添加订阅者(观察者对象)void attach(Observer observer);//删除订阅者void detach(Observer observer);//通知订阅者更新消息void notify(String message);
}

3.3.具体观察者

WeiXinObserver.java

//具体的观察者角色类
public class WeiXinObserver implements Observer{private String name;public WeiXinObserver(String name) {this.name = name;}@Overridepublic void update(String message) {System.out.println(name + " : " + message);}
}

3.4.具体观察对象

SubscriptionSubject.java

import java.util.ArrayList;
import java.util.List;//具体主题角色类
public class SubscriptionSubject implements Subject{//定义一个集合,用来存储多个观察者对象private List<Observer> weiXinUserList = new ArrayList<>();@Overridepublic void attach(Observer observer) {weiXinUserList.add(observer);}@Overridepublic void detach(Observer observer) {weiXinUserList.remove(observer);}@Overridepublic void notify(String message) {//遍历集合for (Observer observer : weiXinUserList) {//调用观察者对象中的 update 方法observer.update(message);}}
}

3.5.测试

Client.java

public class Client {public static void main(String[] args) {//1.创建公众号对象SubscriptionSubject subject = new SubscriptionSubject();//2.订阅公众号subject.attach(new WeiXinObserver("Tom"));subject.attach(new WeiXinObserver("Mike"));subject.attach(new WeiXinObserver("Jerry"));//3.公众号更新subject.notify("Java专栏更新了!");}
}

4.优缺点

(1)优点

  • 解耦性:观察者模式将观察者和主题(被观察者)解耦,使得它们之间的依赖关系变得松散。主题只需要知道观察者的抽象接口,而不需要知道具体的观察者对象。
  • 可扩展性:通过新增或删除观察者,可以方便地扩展系统的功能。主题可以随时添加或删除观察者,而不会影响到其他部分的代码。
  • 广播通信:主题通知所有的观察者对象,实现事件的广播通信,方便多个对象对事件做出响应。
  • 符合开闭原则:新增观察者时无需修改主题的代码,只需实现新的观察者接口即可,符合开闭原则的要求。

(2)缺点

  • 观察者过多可能导致性能问题:如果观察者过多,或者通知频繁,可能会导致系统性能下降。因此在设计时需注意对观察者的控制。
  • 观察者和主题关系不明确:观察者模式中的观察者和主题之间的关系是松散的,观察者无法准确知道有哪些其他观察者存在。可能存在一些潜在的问题和维护的困难。

5.使用场景

(1)观察者模式适用于以下场景:

  • 发布-订阅模型:当存在一个主题(或发布者)需要通知多个观察者(或订阅者)并进行相应的处理时,可以使用观察者模式。例如,新闻发布机构将新闻发布给多个订阅者。
  • UI 事件处理:在图形用户界面 (GUI) 开发中,观察者模式常用于处理用户界面上的事件。用户操作(如按钮点击、文本框输入等)可以作为主题,而界面元素的更新则可以作为观察者来处理。
  • 实时数据更新:当需要将实时数据的更新通知给多个组件或模块时,可以使用观察者模式。例如,股票交易系统中,股票价格的变化可以作为主题,而界面上的多个组件可以作为观察者。
  • 消息队列系统:在消息队列系统中,观察者模式可以用于消息的发布和订阅的机制。生产者发布消息到主题中,而消费者作为观察者订阅主题,在消息到达时进行相应的处理。
  • MVC 架构:观察者模式常用于应用程序的 MVC(模型-视图-控制器)架构中,用于实现模型与视图之间的通信。当模型的状态发生变化时,观察者模式可以通知相关的视图来刷新界面。
  • 多线程编程中的消息通知:在多线程编程中,观察者模式可以用于实现线程间的消息通知机制。一个线程可以作为主题,而其他线程可以作为观察者,通过观察者模式实现线程间的协同工作。

(2)总之,观察者模式适用于需要一对多的依赖关系,并且对观察者和被观察者之间的耦合度要求较低的场景。它常用于发布-订阅模型、UI 事件处理、实时数据更新、消息队列系统、MVC 架构等应用场景。

6.JDK 源码解析——Observable / Observer

在 Java 中,通过 java.util.Observable 类java.util.Observer 接口定义了观察者模式,只要实现它们的子类就可以编写观察者模式实例。

6.1.Observable 类

其源码如下:

package java.util;public class Observable {/*changed 为一个 boolean 类型的内部标志,注明目标对象是否发生了变化当它为 true 时,notifyObservers() 才会通知观察者*/private boolean changed = false;//存储所有的观察者对象private Vector<Observer> obs;public Observable() {obs = new Vector<>();}//用于将新的观察者对象添加到集合中public synchronized void addObserver(Observer o) {if (o == null)throw new NullPointerException();if (!obs.contains(o)) {obs.addElement(o);}}//从集合中删除一个观察者public synchronized void deleteObserver(Observer o) {obs.removeElement(o);}public void notifyObservers() {notifyObservers(null);}public void notifyObservers(Object arg) {      Object[] arrLocal;synchronized (this) {         if (!changed)return;arrLocal = obs.toArray();clearChanged();}for (int i = arrLocal.length-1; i>=0; i--)//逆序通知集合中的观察者更新消息((Observer)arrLocal[i]).update(this, arg);}//删除所集合中所有的观察者public synchronized void deleteObservers() {obs.removeAllElements();}protected synchronized void setChanged() {changed = true;}protected synchronized void clearChanged() {changed = false;}public synchronized boolean hasChanged() {return changed;}//返回集合中观察者的数量public synchronized int countObservers() {return obs.size();}
}

6.2.Observer 接口

Observer 接口是抽象观察者,它监视目标对象的变化,当目标对象发生变化时,观察者得到通知,并调用 update() 方法,进行相应的工作。其源码如下:

package java.util;public interface Observer {/*** This method is called whenever the observed object is changed. An* application calls an <tt>Observable</tt> object's* <code>notifyObservers</code> method to have all the object's* observers notified of the change.** @param   o     the observable object.* @param   arg   an argument passed to the <code>notifyObservers</code>*                 method.*/void update(Observable o, Object arg);
}

6.3.案例

【例】警察抓小偷。警察抓小偷也可以使用观察者模式来实现,警察是观察者,小偷是被观察者。代码如下:

(1)小偷是一个被观察者,所以需要继承 Observable 类

import java.util.Observable;public class Thief extends Observable {private String name; public Thief(String name) {this.name = name;}public void setName(String name) {this.name = name;}public String getName() {return name;}public void steal() {System.out.println("小偷:我偷东西了,有没有人来抓我!!!");super.setChanged();  //即令changed = truesuper.notifyObservers();}
}

(2)警察是一个观察者,所以需要让其实现 Observer 接口

import java.util.Observable;
import java.util.Observer;public class Policemen implements Observer {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}public Policemen(String name) {this.name = name;}@Overridepublic void update(Observable o, Object arg) {System.out.println("警察:" + ((Thief) o).getName() + ",我已经盯你很久了,你可以保持沉默,但你所说的将成为呈堂证供!!!");}
}
public class Client {public static void main(String[] args) {//创建小偷对象Thief t = new Thief("小偷");//创建警察对象Policemen p = new Policemen("小李");//让警察盯着小偷t.addObserver(p);//小偷偷东西t.steal();}
}

结果如下:

小偷:我偷东西了,有没有人来抓我!!!
警察:小偷,我已经盯你很久了,你可以保持沉默,但你所说的将成为呈堂证供!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/29164.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构与算法——顺序表(顺序存储结构)及初始化详解

顺序表&#xff0c;全名顺序存储结构&#xff0c;是线性表的一种。通过《什么是线性表》一节的学习我们知道&#xff0c;线性表用于存储逻辑关系为“一对一”的数据&#xff0c;顺序表自然也不例外。 不仅如此&#xff0c;顺序表对数据的物理存储结构也有要求。顺序表存储数据…

HIVE SQL实现通过两字段不分前后顺序去重

--数据建表 drop table if exists db.tb_name; create table if not exists db.tb_name ( suj1 string,suj2 string ) ;insert overwrite table db.tb_name values ("语文","数学") ,("语文","英语") ,("数学","语文&…

Linux 漏洞扫描

Linux 漏洞扫描程序会仔细检查基于 Linux 的系统&#xff0c;以减轻潜在的风险和漏洞。 什么是 Linux 漏洞扫描程序 Linux 漏洞扫描程序是一种专门的漏洞扫描工具&#xff0c;旨在识别基于 Linux 的系统中的安全漏洞和弱点,它会扫描配置错误、过时的软件版本和已知漏洞。 为…

何时使用Windbg静态分析?何时使用Windbg动态调试?

目录 1、概述 2、使用Windbg静态分析dump文件 2.1、异常捕获模块自动生成dump文件 2.2、从Windows任务管理器中导出dump文件 2.3、从正在动态调试的Windbg中使用命令导出dump文件 2.4、使用Windbg静态分析dump文件的一般步骤 3、使用Windbg动态调试目标进程 3.1、程序发…

OpenCV的HSV颜色空间在无人车中颜色识别的应用

RGB属于三基色空间&#xff0c;是大家最为熟悉的&#xff0c;看到的任何一种颜色都可以由三基色进行混合而成。然而一般对颜色空间的图像进行有效处理都是在HSV空间进行的&#xff0c;HSV(色调Hue,饱和度Saturation,亮度Value)是根据颜色的直观特性创建的一种颜色空间, 也称六角…

【技巧】Maven重复依赖分析查找

【技巧】Maven重复依赖分析查找 遇到奇葩的错误可以考虑是不是依赖冲突了 比如同一段代码 再这个项目中好好的 另一个项目中不能用等 idea安装插件 maven helper 打开pom文件 输入要查找的依赖 将不用的排除掉 右键排除即可

Pandas Groupby:在Python中汇总、聚合和分组数据

GroupBy是一个非常简单的概念。我们可以创建一个类别分组&#xff0c;并对这些类别应用一个函数。这是一个简单的概念&#xff0c;但它是一种在数据科学中广泛使用的非常有价值的技术。在真实的的数据科学项目中&#xff0c;您将处理大量数据并一遍又一遍地尝试&#xff0c;因此…

旅游卡小程序软件招商加盟代理

旅游卡小程序软件招商加盟代理 我国人民生活水平的提高&#xff0c;旅游业成为了人们生活中必不可少的一部分。旅游卡小程序软件作为旅游行业的重要组成部分&#xff0c;也日益受到人们的关注。如今&#xff0c;旅游卡小程序软件招商加盟代理已经成为了一个热门的投资创业项…

⛳ Java数组

Java数组的目录 ⛳ Java数组&#x1f3a8; 一&#xff0c;一维数组&#x1f463; 1.1&#xff0c;概念&#x1f4e2; 1.2&#xff0c;基本用法1&#xff0c;语法格式2&#xff0c;代码 &#x1f4bb; 1.3&#xff0c;内存结构&#x1f4dd; 1.4&#xff0c;练习 &#x1f381; …

芯片验证板卡设计方案:基于VU440T的多核处理器多输入芯片验证板卡

基于VU440T的多核处理器多输入芯片验证板卡 一、板卡概述 基于XCVU440-FLGA2892的多核处理器多输入芯片验证板卡为实现网络交换芯片的验证&#xff0c;包括四个FMC接口、DDR、GPIO等&#xff0c;板卡用于完成甲方的芯片验证任务&#xff0c;多任务功能验证。 …

GO语言Metex

Mutex互斥锁 type Mutex struct{state int32 // 0表示未加锁 1表示加锁&#xff0c;原子操作sema uint32 // 信号量&#xff0c;用作等待队列 } Mutex正常模式: 尝试加锁的G会先自旋几次,若获不到锁,则加入等待队列. 正常模式下,自选和等待队列的一起竞争 因为G频繁的挂起…

后端(五):JVM

目录 JVM 中的内存区域划分 JVM 的类加载机制 1. 加载 2. 验证 3. 准备 4. 解析 5. 初始化 JVM 中的垃圾回收策略 找&#xff0c;确认垃圾 1. 引用计数 2. 可达行分析 释放”垃圾“对象 1. 标记清除 2. 复制算法 3. 标记整理 分代算法 JVM也就是我们俗称的八股…