【设计模式】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 会被执行,控制台会输出相应的消息。

三、总结

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

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

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

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

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

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

相关文章

DeepSeek 助力 Vue 开发:打造丝滑的卡片(Card)

前言:哈喽,大家好,今天给大家分享一篇文章!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏关注哦 💕 目录 Deep…

基于单片机的开关电源设计(论文+源码)

本次基于单片机的开关电源节能控制系统的设计中,在功能上设计如下: (1)系统输入220V; (2)系统.输出0-12V可调,步进0.1V; (3)LCD液晶显示实时电压&#xff…

SpringBoot(7)-Swagger

目录 一、是什么 二、SpringBoot集成Swagger 三、配置Swagger 3.1 配置文档信息 3.2 配置扫描接口 3.3 配置Swagger开关 3.4 配置API分组 3.5 实体配置 四、常用注解 五、总结 一、是什么 是一款API框架,API文档和API定义同步更新,可以在线测…

Atlassian工具集:Jira与Confluence集成优势、使用技巧、更新功能等

本文由Atlassian全球白金合作伙伴-龙智翻译整理,深入探讨了Jira和Confluence最受欢迎的集成功能与技巧,期待为您新一年的团队协作开个好头。 此前,来自K15t 的Customer Advocate Matt Reiner 和Atlassian副产品经理David Olive在一场学习会议…

Qt QOpenGLShaderProgram详解

1. 概述 QOpenGLShaderProgram 是 Qt 框架中用于管理 OpenGL 着色器程序的类。它提供了一种方便的方式来创建、编译和链接着色器,以及管理着色器程序中的属性和统一变量。通过 QOpenGLShaderProgram,开发者可以轻松地将顶点着色器和片段着色器组合成一个…

小爱音箱控制手机和电视听歌的尝试

最近买了小爱音箱pro,老婆让我扔了,吃灰多年的旧音箱。当然舍不得,比小爱还贵,刚好还有一台红米手机,能插音箱,为了让音箱更加灵活,买了个2元的蓝牙接收模块Type-c供电3.5接口。这就是本次尝试起…

ESXi安装【真机和虚拟机】(超详细)

项目简介: ESXi(Elastic Sky X Integrated)是VMware公司开发的一种裸机虚拟化管理程序,允许用户在单一物理服务器上运行多个虚拟机(VM)。它直接安装在服务器硬件上,而不是操作系统之上&#xff…

本地通过隧道连接服务器的mysql

前言 服务器上部署了 mysql,本地希望能访问该 mysql,但是又不希望 mysql 直接暴露在公网上 那么可以通过隧道连接 ssh 端口的方式进行连接 从外网看,服务器只开放了一个 ssh 端口,并没有开放 3306 监听端口 设置本地免密登录 …

MyBatis-Plus模块扩展分页查询

文章目录 1.说明2.common-mybatis-plus-starter1.目录结构2.MybatisPLusAutoConfiguration.java 添加MyBatis Plus分页插件3.PlusPageResult.java MyBatis Plus 分页对象的增强版4.SunPlusPageHelper.java 分页工具类 3.EasyCode模板修改4.common-mybatis-plus-starter-demo 分…

TestHubo基础教程-创建项目

TestHubo是一款国产开源一站式测试工具,涵盖功能测试、接口测试、性能测试,以及 Web 和 App 测试,可以满足不同类型项目的测试需求。本文将介绍如何快速创建第一个项目,以快速入门上手。 1、创建项目 在 TestHubo 中,…

Pythong 解决Pycharm 运行太慢

Pythong 解决Pycharm 运行太慢 官方给Pycharm自身占用的最大内存设低估了限制,我的Pycharm刚开始默认是256mb。 首先找到自己的Pycharm安装目录 根据合适自己的改 保存,重启Pycharm

【Cocos TypeScript 零基础 15.1】

目录 见缝插针UI脚本针脚本球脚本心得_旋转心得_更改父节点心得_缓动动画成品展示图 见缝插针 本人只是看了老师的大纲,中途不明白不会的时候再去看的视频 所以代码可能与老师代码有出入 SIKI_学院_点击跳转 UI脚本 import { _decorator, Camera, color, Component, directo…

docker部署单机版doris,完整无坑

文章目录 一、部署1、修改内核参数2、下载Docker 开发环境镜像3、下载安装包4、启动镜像5、配置fe6、配置be7、远程连接 二、运维命令参考资料 一、部署 1、修改内核参数 在启动doris的be时,需要将 Linux 操作系统的内核参数设置为2000000,这里是Doris…

docker 进阶命令(基于Ubuntu)

数据卷 Volume: 目录映射, 目录挂载 匿名绑定: 匿名绑定的 volume 在容器删除的时候, 数据卷也会被删除, 匿名绑定是不能做到持久化的, 地址一般是 /var/lib/docker/volumes/xxxxx/_data 绑定卷时修改宿主机的目录或文件, 容器内的数据也会同步修改, 反之亦然 # 查看所有 vo…

指针复习 ( 下 )

这是指针复习的第二篇,主要介绍指针与数组、函数的应用。 一、指针与数组 1.1 数组名的理解 在说明指针与数组的关系之前,我们必须要先了解「数组名」的意义 我们在指针复习 ( 下 ) 中,指针整数的部份有写过下面这段代码 int arr[10] {1,…

ElementUI 的组件 Switch(开关)如何让文字显示在按钮上

效果图&#xff1a; 一、引入switch组件 给组件自定义一个类&#xff1a;tableScopeSwitch&#xff0c;设置开关的值和对应展示的文字&#xff08;开为 1&#xff0c;并展示启用&#xff1b;关为 0&#xff0c;并展示禁用&#xff09;。 <div class"tableScopeSwitch…

Linux---系统函数

一、打开文件open #include <fcntl.h> int open(const char *pathname&#xff0c;int flags); int open(const char *pathname&#xff0c;int flags&#xff0c;mode_t mode); 功能: 打开或创建文件 参数: pathname //打开的文件名 flags //操作的标志…

智能编程助手功能革新与价值重塑之:GitHub Copilot

引言&#xff1a; GitHub Copilot 的最新更新为开发者带来了显著变化&#xff0c;其中 Agent Mode 功能尤为引人注目。该模式能够自动识别并修复代码错误、自动生成终端命令&#xff0c;并具备多级任务推理能力&#xff0c;这使得开发者在开发复杂功能时&#xff0c;可大幅减少…

Elasticsearch+Logstash+Kibana可视化集群部署

文章目录 1.组件介绍简述2.集群规划3.Es组件部署4.Logstash组件部署5.Kibana组件部署6.Kibana的基础使用 1.组件介绍简述 Elasticsearch&#xff1a;开源实时分布式搜索和分析引擎&#xff0c;支持大规模数据存储和高吞吐量&#xff0c;提供丰富的搜索功能和可扩展性。 Logsta…

SpringBoot多数据源实践:基于场景的构建、实现和事务一体化研究

1. 多数据源应用场景剖析 1.1 业务驱动的多数据源需求 数据量与业务复杂度引发的分库分表&#xff1a;在现代企业级应用中&#xff0c;随着业务的不断拓展和用户量的持续增长&#xff0c;数据量呈爆炸式增长。例如&#xff0c;在大型电商平台中&#xff0c;用户数据、订单数据…