【设计模式】03-理解常见设计模式-行为型模式(专栏完结)

前言

前面我们介绍完创建型模式和创建型模式,这篇介绍最后的行为型模式,也是【设计模式】专栏的最后一篇。


一、概述

行为型模式主要用于处理对象之间的交互和职责分配,以实现更灵活的行为和更好的协作。

二、常见的行为型模式

1、观察者模式(Observer Pattern)

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都会得到通知并自动更新。

解释:可以把它想象成一个明星和粉丝的关系,明星(被观察对象)的一举一动(状态改变)都会被粉丝(观察者)关注到,当明星有新动态时,粉丝会收到消息。

代码示范以及解释 

// 被观察对象类
class Subject {
    constructor() {
        this.observers = [];
    }

    // 添加观察者
    addObserver(observer) {
        this.observers.push(observer);
    }

    // 移除观察者
    removeObserver(observer) {
        const index = this.observers.indexOf(observer);
        if (index!== -1) {
            this.observers.splice(index, 1);
        }
    }

    // 通知所有观察者
    notify() {
        this.observers.forEach(observer => observer.update());
    }
}

// 观察者类
class Observer {
    constructor(name) {
        this.name = name;
    }

    update() {
        console.log(`${this.name} has been notified.`);
    }
}

// 使用示例
const subject = new Subject();
const observer1 = new Observer('Fan1');
const observer2 = new Observer('Fan2');

subject.addObserver(observer1);
subject.addObserver(observer2);

subject.notify(); 
// 输出:
// Fan1 has been notified.
// Fan2 has been notified.

 被观察的对象Subject

  • 构造函数 constructor
    • 初始化一个空数组 this.observers,用于存储所有注册的观察者。
  • addObserver 方法
    • 接收一个 observer 对象作为参数,将其添加到 this.observers 数组中。这就相当于有一个新的观察者开始关注这个被观察对象了。
  • removeObserver 方法
    • 接收一个 observer 对象作为参数,首先使用 indexOf 方法查找该观察者在 this.observers 数组中的索引。
    • 如果索引不为 -1(说明该观察者存在于数组中),则使用 splice 方法将其从数组中移除,意味着这个观察者不再关注被观察对象了。
  • notify 方法
    • 遍历 this.observers 数组,对每个观察者调用其 update 方法。这样就可以通知所有注册的观察者,让它们执行相应的更新操作。

 观察者Observer

  • 构造函数 constructor
    • 接收一个 name 参数,用于标识这个观察者,将其存储在 this.name 中。
  • update 方法
    • 当被观察对象调用 notify 方法通知观察者时,这个 update 方法会被执行。它会打印出一条消息,表明该观察者已经收到了通知。

代码执行结果解释

  • 首先创建一个 Subject 类的实例 subject,表示被观察对象。
  • 接着创建两个 Observer 类的实例 observer1 和 observer2,分别命名为 'Fan1' 和 'Fan2'
  • 调用 subject.addObserver 方法将 observer1 和 observer2 添加到 subject 的观察者列表中。
  • 最后调用 subject.notify() 方法,subject 会遍历其观察者列表,依次调用每个观察者的 update 方法,因此控制台会输出 'Fan1 has been notified.' 和 'Fan2 has been notified.'

2、策略模式(Strategy Pattern):

定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。策略模式让算法的变化独立于使用算法的客户。

解释:策略模式定义了一系列的算法,把它们一个个封装起来,并且使它们可相互替换。策略模式让算法的变化独立于使用算法的客户。这就好比你要去上班,有多种出行方式(策略)可供选择,如坐公交、打车、骑自行车等,你可以根据当天的实际情况(如时间、天气等)来动态地选择合适的出行方式,而上班这个行为本身不受具体出行方式的影响。

 代码示范以及解释 

// 定义不同的策略类
// 公交出行策略
class BusStrategy {
    travel() {
        console.log('Taking the bus to work.');
    }
}
//这是一个公交出行策略类,包含一个 travel 方法。
//当调用 travel 方法时,会在控制台打印出 Taking the bus to work.,表示选择乘坐公交去上班。

// 打车出行策略
class TaxiStrategy {
    travel() {
        console.log('Taking a taxi to work.');
    }
}
//这是一个打车出行策略类,同样有一个 travel 方法。
//调用 travel 方法时,会在控制台打印出 Taking a taxi to work.,表示选择打车去上班。

// 自行车出行策略
class BicycleStrategy {
    travel() {
        console.log('Riding a bicycle to work.');
    }
}
//这是一个自行车出行策略类,travel 方法会在控制台打印出 Riding a bicycle to work.,表示选择骑自行车去上班。

// 环境类,负责使用策略
class Commute {
    constructor(strategy) {
        this.strategy = strategy;
    }

    setStrategy(strategy) {
        this.strategy = strategy;
    }

    goToWork() {
        this.strategy.travel();
    }
}
//构造函数 constructor:
//接收一个 strategy 参数,将传入的策略对象赋值给 this.strategy,表示初始化时使用该策略。
//setStrategy 方法:
//接收一个新的 strategy 参数,将 this.strategy 更新为新的策略对象,从而实现策略的动态切换。
//goToWork 方法:
//调用当前 this.strategy 对象的 travel 方法,执行具体的出行策略。

// 使用示例
const busStrategy = new BusStrategy();
const commute = new Commute(busStrategy);
commute.goToWork(); // 输出: Taking the bus to work.

const taxiStrategy = new TaxiStrategy();
commute.setStrategy(taxiStrategy);
commute.goToWork(); // 输出: Taking a taxi to work.

执行结果解释

  • 首先创建一个 BusStrategy 类的实例 busStrategy,表示公交出行策略。
  • 接着创建一个 Commute 类的实例 commute,并将 busStrategy 作为参数传入,意味着初始化时使用公交出行策略。
  • 调用 commute.goToWork() 方法,会执行公交出行策略,控制台输出 Taking the bus to work.
  • 然后创建一个 TaxiStrategy 类的实例 taxiStrategy,表示打车出行策略。
  • 调用 commute.setStrategy(taxiStrategy) 方法,将当前的出行策略切换为打车策略。
  • 再次调用 commute.goToWork() 方法,会执行打车出行策略,控制台输出 Taking a taxi to work.

3、发布-订阅模式(Publish - Subscribe Pattern)(重点!!)

发布 - 订阅模式和观察者模式类似,但它引入了一个中间者(消息代理),发布者(发布消息的对象)将消息发布到消息代理,订阅者(接收消息的对象)向消息代理订阅感兴趣的消息。这种模式实现了发布者和订阅者之间的解耦。

简单理解:就像一个报社和读者的关系,报社(发布者)负责发布报纸(消息),读者(订阅者)向报社订阅报纸,当有新报纸出版时,报社将报纸发送给订阅的读者。

 代码示范以及解释 

// 消息代理类
class EventEmitter {
    constructor() {
        this.events = {};
    }

    // 订阅事件
    on(eventName, callback) {
        if (!this.events[eventName]) {
            this.events[eventName] = [];
        }
        this.events[eventName].push(callback);
    }

    // 发布事件
    emit(eventName, ...args) {
        if (this.events[eventName]) {
            this.events[eventName].forEach(callback => callback(...args));
        }
    }

    // 取消订阅事件
    off(eventName, callback) {
        if (this.events[eventName]) {
            const index = this.events[eventName].indexOf(callback);
            if (index!== -1) {
                this.events[eventName].splice(index, 1);
            }
        }
    }
}

// 使用示例
const eventEmitter = new EventEmitter();

// 订阅者的回调函数
const callback1 = (message) => {
    console.log(`Subscriber 1 received: ${message}`);
};

const callback2 = (message) => {
    console.log(`Subscriber 2 received: ${message}`);
};

// 订阅事件
eventEmitter.on('news', callback1);
eventEmitter.on('news', callback2);

// 发布事件
eventEmitter.emit('news', 'A new article is published!'); 
// 输出:
// Subscriber 1 received: A new article is published!
// Subscriber 2 received: A new article is published!

// 取消订阅
eventEmitter.off('news', callback1);
eventEmitter.emit('news', 'Another new article!'); 
// 输出:
// Subscriber 2 received: Another new article!

整体功能概述

EventEmitter 类充当消息代理,它允许用户订阅(on 方法)特定的事件,发布(emit 方法)事件,以及取消订阅(off 方法)事件。当事件被发布时,所有订阅该事件的回调函数都会被执行。

消息代理类 EventEmitter

  • 构造函数 constructor
    • 初始化一个空对象 this.events,用于存储事件及其对应的回调函数数组。每个事件名作为对象的键,对应的值是一个数组,数组中存储着订阅该事件的所有回调函数。
  • on 方法
    • 接收两个参数:eventName 表示事件的名称,callback 是订阅该事件时要执行的回调函数。
    • 首先检查 this.events 对象中是否已经存在该事件名。如果不存在,就为该事件名创建一个空数组。
    • 然后将传入的 callback 函数添加到该事件对应的数组中。这意味着又有一个订阅者订阅了该事件。
  • emit 方法
    • 接收事件名 eventName 和任意数量的参数 ...args
    • 检查 this.events 对象中是否存在该事件名。如果存在,就遍历该事件对应的回调函数数组,依次调用每个回调函数,并将 ...args 作为参数传递给它们。这样就实现了事件的发布,通知所有订阅者执行相应的操作。
  • off 方法
    • 接收事件名 eventName 和要取消订阅的回调函数 callback
    • 检查 this.events 对象中是否存在该事件名。如果存在,使用 indexOf 方法查找该回调函数在事件对应的数组中的索引。
    • 如果索引不为 -1(说明该回调函数存在于数组中),使用 splice 方法将其从数组中移除,从而实现取消订阅的功能。

执行结果解释

const eventEmitter = new EventEmitter();

// 订阅者的回调函数
const callback1 = (message) => {
    console.log(`Subscriber 1 received: ${message}`);
};

const callback2 = (message) => {
    console.log(`Subscriber 2 received: ${message}`);
};

// 订阅事件
eventEmitter.on('news', callback1);
eventEmitter.on('news', callback2);

// 发布事件
eventEmitter.emit('news', 'A new article is published!'); 
// 输出:
// Subscriber 1 received: A new article is published!
// Subscriber 2 received: A new article is published!

// 取消订阅
eventEmitter.off('news', callback1);
eventEmitter.emit('news', 'Another new article!'); 
// 输出:
// Subscriber 2 received: Another new article!
  • 创建一个 EventEmitter 类的实例 eventEmitter
  • 定义两个回调函数 callback1 和 callback2,分别表示两个订阅者在收到事件通知时要执行的操作。
  • 调用 eventEmitter.on 方法,将 callback1 和 callback2 订阅到 'news' 事件上。
  • 调用 eventEmitter.emit 方法发布 'news' 事件,并传递消息 'A new article is published!'。由于 callback1 和 callback2 都订阅了该事件,所以它们都会被执行,控制台会输出相应的消息。
  • 调用 eventEmitter.off 方法取消 callback1 对 'news' 事件的订阅。
  • 再次调用 eventEmitter.emit 方法发布 'news' 事件,并传递消息 'Another new article!'。此时只有 callback2 还订阅着该事件,所以只有 callback2 会被执行,控制台会输出相应的消息。

三、总结

 到此,【设计模式】专栏的篇章已经完结~

文章介绍的是部分常见的设计模式,实际上还有很多设计模式等着大家去学习,后续我有空会补充新的设计模式内容,敬请期待吧~

如果你喜欢这篇文章,留下你的三连+订阅~

关注我,及时获取最新文章消息~

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

matlab欠驱动船舶模型预测控制

1、内容简介 matlab135-欠驱动船舶模型预测控制 可以交流、咨询、答疑 2、内容说明 略 针对在风 、 浪 、 流时变干扰下欠驱动水面船舶的轨迹跟踪控制问题 , 设计了一种基于模型 预测控制的轨迹跟踪控制器 . 考虑到欠驱动船舶在没有横向驱动力情况下…

计算机性能与网络体系结构探讨 —— 基于《计算机网络》谢希仁第八版

(꒪ꇴ꒪ ),Hello我是祐言QAQ我的博客主页:C/C语言,数据结构,Linux基础,ARM开发板,网络编程等领域UP🌍快上🚘,一起学习,让我们成为一个强大的攻城狮&#xff0…

Linux上安装jdk1.8和配置环境变量

步骤一::创建jdk安装目录(该/usr/local/ ,最好把我们自己下载的放到这,容易区分) 可以省略 步骤二:查看安装程序 [rootVM_0_4_centos src]# rpm -qa | grep -i jdk 若之前安装过jdk,下次安装一定把之前的删除干净 下载地址链接…

【Spring+MyBatis】留言墙的实现

目录 1. 添加依赖 2. 配置数据库 2.1 创建数据库与数据表 2.2 创建与数据库对应的实体类 3. 后端代码 3.1 目录结构 3.2 MessageController类 3.3 MessageService类 3.4 MessageMapper接口 4. 前端代码 5. 单元测试 5.1 后端接口测试 5.2 使用前端页面测试 在Spri…

Windows环境安装部署minimind步骤

Windows环境安装部署minimind步骤 必要的软件环境 git git,可下载安装版,本机中下载绿色版,解压到本地目录下(如:c:\soft\git.win64),可将此路径添加到PATH环境变量中,供其他程序…

RocketMQ与kafka如何解决消息丢失问题?

0 前言 消息丢失基本是分布式MQ中需要解决问题,消息丢失时保证数据可靠性的范畴。如何保证消息不丢失程序员面试中几乎不可避免的问题。本文主要说明RocketMQ和Kafka在解决消息丢失问题时,在生产者、Broker和消费者之间如何解决消息丢失问题。 1.Rocket…

基于AIOHTTP、Websocket和Vue3一步步实现web部署平台,无延迟控制台输出,接近原生SSH连接

背景:笔者是一名Javaer,但是最近因为某些原因迷上了Python和它的Asyncio,至于什么原因?请往下看。在着迷”犯浑“的过程中,也接触到了一些高并发高性能的组件,通过简单的学习和了解,aiohttp这个…

Golang的代码结构规划

Golang的代码结构规划 是一种具有高效性能的开发语言,其代码结构规划对于项目的可维护性和可扩展性至关重要。在Golang中,合理的代码结构可以使代码更加清晰易懂,方便团队协作和项目维护。本文将介绍Golang代码结构规划的最佳实践&#xff0c…

【算法与数据结构】并查集详解+题目

目录 一,什么是并查集 二,并查集的结构 三,并查集的代码实现 1,并查集的大致结构和初始化 2,find操作 3,Union操作 4,优化 小结: 四,并查集的应用场景 省份…

服务器部署DeepSeek,通过Ollama+open-webui部署

1. 安装ollama 1.1. linux 安装 Ollama是目前常用的AI模式部署的第三方工具,能一键部署deepSeek Ollama官方网址https://ollama.com/ 选择Download下载对应的服务版本 服务器选择Linux,下面是下载代码 curl -fsSL https://ollama.com/install.…

(三)Axure制作转动的唱片

效果图 属性: 图标库:iconfont-阿里巴巴矢量图标库 方形图片转为圆角图片,裁剪,然后加圆角, 唱片和底图是两个图片,点击播放,唱片在旋转。 主要是播放按钮和停止按钮,两个动态面板…

5G时代的运维变革与美信监控易的深度剖析

一、5G普及后的网络运维新变化:数据驱动的挑战与机遇 (一)数据流量的爆炸式增长 在2025年,5G技术已经如同汹涌的浪潮席卷全球。据相关科技数据显示,5G网络的普及使得数据流量呈现出令人咋舌的增长态势。 这种海量的数…

BGP配置华为——RR反射器配置

实验拓扑 与之前实验同理将loop0作为routerID使用,且R1和R2上用loop1接口用于模拟用户其他网段 实验要求 1,在AS100内运行OSPF协议 2.配置路由反射器,使得从R1进入的数据能够反射到全局网络 3.在R1和R2上分别宣告自己的loop1口网段用于观…

记录第一次在windows环境编译libuvc库 踩的坑

最近遇到windows下编译libuvc库,实现经usb连接的摄像头拍摄采集。绕了一大圈,记录一下。 首先,作为新手,肯定需要参考大神资料,但是还是踩了坑。 要在windows 环境下安装libuvc的驱动并确保可用,需要经过一系列流程&a…

Mybatisplus——Mybatisplus3.5.2版本使用Page分页插件查询,records有数据但是total显示0

目录 一、问题背景 debug 执行Mybatisplus使用Page分页插件查询时,发现 Page 里面的records有数据但是total显示0。 二、问题产生的原因 未配置MybatisPlus的分页插件拦截器导致的或者因mybatis-plus版本3.4或3.5版本导致原先的分页插件paginationInterceptor无法…

安全筑基,智能赋能:BeeWorks IM引领企业协同新纪元

在数字经济高速发展的今天,企业通讯系统已从单纯的信息传递工具演变为支撑业务创新的核心平台。传统通讯工具在安全性、智能化、协同性等方面的不足,严重制约着企业的数字化转型进程。BeeWorks IM系统以其创新的技术架构和智能化功能,正在重新…

SSM课设-学生选课系统

【课设者】SSM课设-学生选课系统 分为 管理员 和 老师 和 学生端 技术栈 前端: HtmlCssJavaScriptAjax 后端: Spring、Spring MVC、MyBatis、MySQL、JSP 学生端 --选课 选课 搜索 --查看选课结果 --退选 --查看已修课程 --管理个人信息 老师端 --添加教学课程 添加 …

记使用AScript自动化操作ios苹果手机

公司业务需要自动化操作手机,本来以为很困难,没想到使用AScript工具出乎意料的简单,但是还有很多坑存在,写个博客记录一下。 工具信息: 手机:iphone7 系统版本:ios15 AScript官方文档链接&a…

linux 安装ftp

1、安装vsftpd sudo yum install -y vsftpd 2、运行以下命令,启动FTP服务,并设置开机自启动。 sudo systemctl start vsftpdsudo systemctl enable vsftpd 3、运行以下命令,查看FTP服务监听的端口。 sudo netstat -antup | grep ftp 出现…

[AI]从零开始的llama.cpp部署与DeepSeek格式转换、量化、运行教程

一、前言 在上一次的DeepSeek的部署教程中,我们使用Ollama与LM Studio很轻松的部署了DeepSeek并且也完成了相关API的调用,如果还有不会的小伙伴请看下面的教程: DeepSeek本地部署:[AI]从零开始的DeepSeek本地部署及本地API调用教…