设计模式—观察者模式

news2024/12/22 19:39:57

观察者模式(Observer Pattern)是一种行为型设计模式,它定义了一种一对多的依赖关系,使得当一个对象的状态发生变化时,所有依赖于它的对象都会得到通知并自动更新。

在观察者模式中,有两个核心角色:

  1. Subject(主题):也称为被观察者或可观察对象,它是具有状态的对象,当其状态发生变化时,会通知所有的观察者。

  2. Observer(观察者):也称为订阅者或观察者,它定义了一个接口,用于接收主题的通知,并进行相应的处理。

观察者模式的工作原理如下:

  1. 主题维护一个观察者列表,用于存储所有订阅该主题的观察者。

  2. 当主题的状态发生变化时,会遍历观察者列表,依次调用每个观察者的更新方法。

  3. 观察者收到通知后,根据主题的状态进行相应的处理。

观察者模式的优点包括:

  • 解耦性:主题和观察者之间是松耦合的,它们可以独立地进行扩展和修改,互不影响。

  • 可扩展性:可以方便地增加新的观察者,以及在不修改主题代码的情况下增加新的主题。

  • 易于维护:由于主题和观察者之间的关系是明确的,代码的维护和调试相对容易。

举个例子:被观察者=新闻,观察者有“播放新闻”和“打印新闻”,当有新的新闻产生时,立刻播放和打印新闻。(建议尽量面向抽象编程、接口编程)

先定义观察者和被观察者的抽象类。

/**
 * 观察者抽象
 */
public abstract class Observer {

    /**
     * 处理新闻内容
     * @param msg
     */
    public abstract void handle(String msg);

    /**
     * 观察者的身份
     * @return
     */
    public abstract String identity();
}
/**
 * 被观察者抽象
 */
public abstract class Subject {

    /**
     * 观察者列表
     */
    public List<Observer> observerList;

    /**
     * 加入观察者
     * @param observer
     */
    public abstract void register(Observer observer);

    /**
     * 剔除观察者
     * @param observer
     */
    public abstract void cancel(Observer observer);

    /**
     * 通知内容
     * @param msg
     */
    public abstract void notice(String msg);
}

再定义新闻类,继承了被观察者抽象类,表示自己是某个具有实际业务含义的被观察者。

public class News extends Subject {

    public News() {
        this.observerList = new ArrayList<>();
    }

    @Override
    public void register(Observer observer) {
        this.observerList.add(observer);
    }

    @Override
    public void cancel(Observer observer) {
        for (Observer ob : this.observerList) {
            if (ob.identity().equals(observer.identity())) {
                this.observerList.remove(ob);
            }
        }
    }

    @Override
    public void notice(String msg) {
        for (Observer ob : this.observerList) {
            ob.handle(msg);
        }
    }
}

定义播放新闻类和打印新闻类,表示具体的观察者。

public class PlayNews extends Observer {

    Logger logger = LoggerFactory.getLogger(PlayNews.class);

    @Override
    public void handle(String msg) {
        logger.info("播放新闻:{}", msg);
    }

    @Override
    public String identity() {
        return "play";
    }
}


public class PrintNews extends Observer {

    Logger logger = LoggerFactory.getLogger(PrintNews.class);

    @Override
    public void handle(String msg) {
        logger.info("打印新闻:{}", msg);
    }

    @Override
    public String identity() {
        return "print";
    }
}

测试一下:

public class Test {

    public static void main(String[] args) {
        News news = new News();
        PlayNews playNews = new PlayNews();
        PrintNews printNews = new PrintNews();
        news.register(playNews);
        news.register(printNews);
        news.notice("油价微涨1元");

        news.cancel(playNews);
        news.notice("油价暴跌1分");
    }
}

个人理解:

1、观察者模式类似于发布-订阅,需要被观察者发出信息让观察者去处理,但是又与发布-订阅模式不同,观察者与被观察者是直接联系,互相有感知,中间没有第三方角色存在,而发布-订阅模式中,发布者与订阅者之间有第三方角色存在,比如kafka中,生产者发送消息到 topic,消费者从topic获取消息,而不是生产者直接发消息给消费者,更多地是体现发布者与订阅者之间的相互无感知,解耦,以及异步机制。

2、观察者模式的亮点在于它的“加入观察者”、“剔除观察者”思想,适合那些观察者随时可变的场景,否则就没什么特点可言了,还不如策略模式。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue-如何实现带参数跳转页面

文/朱季谦 在vue框架的前端页面上&#xff0c;若要实现页面之间的带参数跳转&#xff0c;可参考以下实现过程&#xff1a; 例如&#xff0c;点击截图中的“查看试卷”&#xff0c;可实现带参跳转到相应的试卷页面&#xff0c;该功能类似查看试卷的详情页面&#xff1a; 假如以…

C#多线程总结

目录 前言 一、异步线程 使用async和await关键字 基于委托实现 二、同步线程 三、Thread线程 开启线程 设置线程优先级 Thread拓展封装 四、ThreadPool线程池 常规使用 设置线程数 线程等待 Thread和ThreadPool比较 通过线程池做一些扩展&#xff08;定时器类&am…

解析视频美颜SDK的算法:美肤、滤镜与实时处理

如今&#xff0c;美颜技术在视频处理中扮演着关键的角色&#xff0c;为用户提供更加精致的视觉体验。本文将深入探讨视频美颜SDK的算法&#xff0c;聚焦于美肤、滤镜与实时处理等方面&#xff0c;揭示背后的科技奥秘。 一、美肤算法的魅力 视频美颜的一个核心功能就是美肤&am…

被央视报道过的AIGC产品-贝塔创作(BetaCreator)使用指南

产品地址&#xff1a;betacreator.com 真人图 人台图 商品图 商品变色 建议使用浅色服装进行变色&#xff0c;效果更好 如果没有浅色服装&#xff0c;可以先把服装颜色变为白色

redis-学习笔记(Jedis 前置知识)

自定义的 Redis 客户端 咱们可以实现编写出一个自定义的 Redis 客户端 因为 Redis 公开了自己使用的自定义协议 ---- RESP 协议清楚了, 那么通信数据格式就清除了, 就能完成各层次之间的数据传输, 就能开发服务器和客户端 RESP — Redis 的 序列化 协议 特点: 简单好实现快读进…

App防止恶意截屏功能的方法:iOS、Android和鸿蒙系统的实现方案

防止应用被截图是一个比较常见的需求&#xff0c;主要是出于安全考虑。下面将分别为iOS&#xff08;苹果系统&#xff09;、Android&#xff08;安卓系统&#xff09;及HarmonyOS&#xff08;鸿蒙系统&#xff09;提供防止截屏的方法和示例代码。 在企业内部使用的应用中&…

python学习:浅拷贝与深拷贝详解

copy 一、 & is二、浅拷贝 & 深拷贝(一)、浅拷贝(二)、深拷贝 三、问题 一、’ ’ & ‘is’ ’ 和is是python对象比较常用的两种方式,简单来说,‘ ‘操作符比较对象之间的值是否相等,如 a b 而’is’操作符比较的是对象的身份标识是否相等,即它们是否是同一个…

HNU数据库大作业-世界杯比赛系统

前言 之前做的那个版本bug较多&#xff0c;后进行了大量优化。 此项目是一个前后端分离的项目&#xff0c;前端主要使用htmlcssjs搭建&#xff0c;使用的是layui框架 后端使用php语言&#xff0c;仅实现了简单的查询数据库功能&#xff0c;无法实现多并发查询等复杂情况 数…

jetpack compose 学习(-)

年底了,无聊的时间总是缓慢的,找个事情做一做,打发打发时间,刚好看到jetpack compose 学习学习,毕竟androidStudio 默认创建的项目都带上了这个,学习网站:https://developer.android.com/jetpack/compose/modifiers?hlzh-cn 1. 首先androidStudio创建一个新项目 喜欢kotlin的,…

亚马逊、速卖通自养号测评的安全稳定性与成本优势分析

在跨境电商平台的运营中&#xff0c;买家评价的重要性不言而喻。很多买家在购买产品前都会查看评论&#xff0c;比较同类产品的买家口碑&#xff0c;以做出更明智的购买决策。 因此&#xff0c;测评一直是各大跨境电商平台的一种重要推广方式&#xff0c;测评同时也是很多卖家…

BugKu-Web-Simple_SSTI_1Simple_SSTI_2(浅析SSTI模板注入!)

何为SSTI模块注入&#xff1f; SSTI即服务器端模板注入&#xff08;Server-Side Template Injection&#xff09;&#xff0c;是一种注入漏洞。 服务端接收了用户的恶意输入以后&#xff0c;未经任何处理就将其作为Web应用模板内容的一部分&#xff0c;模板引擎在进行目标编译渲…

TCP/IP详解——网络基本概念

文章目录 一、网络基本概念1. OSI 7层模型1.1 每层对应的协议1.2 每层涉及的设备1.2.1 物理层设备1.2.2 数据链路层设备1.2.3 网络层设备1.2.4 传输层设备1.2.5 交换机和路由器的应用1.2.6 问题 2. TCP/IP 4层模型3. 物理层传输介质3.1 冲突域 4. 数据链路层4.1 以太网帧结构4.…

socket 套接字

1、套接字介绍 socket起源于Unix&#xff0c;遵循“一切皆文件”出发点&#xff0c;都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。 在设计模式中&#xff0c;Socket把复杂的TCP/IP协议族隐藏在Socket接口后面&#xff0c;Socket去组织数据&#xf…

Godot导出Android包报错:无效的包名称

问题描述 使用Godot为项目导出Android平台包时报错&#xff0c;提示&#xff1a;“无效的包名称&#xff1a;项目名称不符合包名格式的要求。请显式指定包名。” 解决办法 修改导出配置项“包->唯一名称”。 该项缺省值“org.godotengine.$genname”不能直接使用&#x…

36V/48V转12V 10A直流降压DC-DC芯片-AH1007

AH1007是一款36V/48V转12V 10A直流降压&#xff08;DC-DC&#xff09;芯片&#xff0c;它是一种高性能的降压变换器&#xff0c;常用于工业、汽车和电子设备等领域。 AH1007采用了先进的PWM调制技术和开关电源控制算法&#xff0c;能够高效地将输入电压从36V/48V降低到12V&…

SSM整合——Springboot

1.0 概述 1.1 持久层&#xff1a; DAO层&#xff08;mapper&#xff09; DAO层&#xff1a;DAO层主要是做数据持久层的工作&#xff0c;负责与数据库进行联络的一些任务都封装在此 DAO层的设计首先是设计DAO的接口&#xff0c; 然后在spring-mapper.xml的配置文件中定义此接…

【Unity学习笔记】光照简介

本节主要是简单介绍一些常见的光照组件和渲染设置。 文章目录 灯光类型平行光Directional Light点光源Point Light聚光灯Spot Light面积光 Area Light 阴影设置全局光照明光照模式直接光照与间接光照Mixed Lighting 光照探针Light Probe Group光照探针组 反射探针 灯光类型 在…

00后女孩月薪3200,3年买两套房,这个程序员变现新风口千万要把握住

00后女孩月薪3200&#xff0c;3年买两套房&#xff0c;这个程序员变现新风口千万要把握住 前几天&#xff0c;在网上看到了一份中国90后收入的调查报告&#xff1a; 报告显示&#xff1a; 90后月均收入8000元&#xff0c;三成90后零存款&#xff0c;两成90后存款达到10万以上…

鸿蒙开发之页面与组件生命周期

一、页面间的跳转 创建文件的时候记得选择创建page文件&#xff0c;这样就可以在main->resources->profile->main_pages.json中自动形成页面对应的路由了。如果创建的时候你选择了ArkTS文件&#xff0c;那么需要手动修改main_pages.json文件中&#xff0c;添加相应的…

mysql字段设计规范:使用unsigned(无符号的)存储非负值

如果一个字段存储的是数值&#xff0c;并且是非负数&#xff0c;要设置为unsigned&#xff08;无符号的&#xff09;。 例如&#xff1a; 备注&#xff1a;对于类型是 FLOAT、 DOUBLE和 DECIMAL的&#xff0c;UNSIGNED属性已经废弃了&#xff0c;可能在mysql的未来某个版本去…