【面试题】 ES6 类聊 JavaScript 设计模式之行为型模式(二)

news2025/7/20 23:59:10

本文是《ES6 类聊 JavaScript 设计模式》的第四篇,介绍第三种类型的设计模式行为设计模式,其特别关注对象之间的通信。

在软件工程中, 行为型模式为设计模式的一种类型,用来识别对象之间的常用交流模式并加以实现。如此,可在进行这些交流活动时增强弹性。—— 维基百科

  • 观察者模式:Observer
  • 访问者模式:Visitor
  • 策略模式:Strategy
  • 状态模式:State
  • 模板方法模式:Template Method

  • 《ES6 类聊 JavaScript 设计模式之创建型模式》

18. 观察者模式:Observer

观察者模式是一种关键的行为设计模式,它定义了对象之间的一对多依赖关系,以便当一个对象(发布者)更改其状态时,所有其他依赖对象(订阅者)都会收到通知并自动更新。也称为 PubSub(发布者/订阅者)或事件调度程序/侦听器模式。发布者有时称为主体,订阅者有时称为观察者。

观察者模式是一种软件设计模式,其中一个名为主体的对象维护其依赖项列表,称为观察者,并自动通知他们任何状态更改,通常通过调用他们的方法之一。 —— 维基百科

实例

将创建了一个简单的 Subject 类,它具有从订阅者集合中添加和删除 Observer 类对象的方法。将 Subject 类对象中的任何更改传播到订阅的观察者的方法 fire

class Subject {
    constructor() {
        this._observers = [];
    }

    subscribe(observer) {
        this._observers.push(observer);
    }

    unsubscribe(observer) {
        this._observers = this._observers.filter((obs) => observer !== obs);
    }

    fire(change) {
        this._observers.forEach((observer) => {
            observer.update(change);
        });
    }
}

class Observer {
    constructor(state) {
        this.state = state;
        this.initialState = state;
    }

    update(change) {
        const state = this.state;
        const handlers = {
            inc: (num) => ++num,
            dec: (num) => --num,
        };
        const changeMethod = handlers[change.toLowerCase()];
        this.state = changeMethod ? changeMethod(state) : this.initialState;
    }
}

// 使用
const sub = new Subject();

const obs1 = new Observer(1);
const obs2 = new Observer(19);

sub.subscribe(obs1);
sub.subscribe(obs2);

sub.fire("INC");

console.log(obs1.state); // 2
console.log(obs2.state); // 20
复制代码

19. 访问者模式:Visitor

访问者模式向对象添加操作而无需修改它们。

访问者模式是一种将算法与其操作的对象结构分离的方法。这种分离的实际结果是能够在不修改结构的情况下向现有对象结构添加新操作。—— 维基百科

实例

将举一个数学表达式 NumberExpression 的例子,列出给定的算术表达式。

class NumberExpression {
    constructor(value) {
        this.value = value;
    }

    print(buffer) {
        buffer.push(this.value.toString());
    }
}

class AdditionExpression {
    constructor(left, right) {
        this.left = left;
        this.right = right;
    }

    print(buffer) {
        buffer.push("(");
        this.left.print(buffer);
        buffer.push("+");
        this.right.print(buffer);
        buffer.push(")");
    }
}
复制代码

使用方式如下:

const e = new AdditionExpression(
    new NumberExpression(6),
    new AdditionExpression(new NumberExpression(2), new NumberExpression(8))
);

const buffer = [];
e.print(buffer);
console.log(buffer.join(""));
复制代码

输出结果如下:

(6+(2+8))
复制代码

20. 策略模式:Strategy

策略模式允许在某些情况下选择其中一种算法。允许为特定任务封装替代算法。它定义了一系列算法并以这样一种方式封装它们,即它们在运行时可互换,而无需客户干预或知识。

策略模式是一种行为软件设计模式,可以在运行时选择算法。代码不是直接实现单个算法,而是接收运行时指令,以确定要使用一系列算法中的哪一个。 —— 维基百科

实例

将举一个例子,有一个文本处理器,将根据策略(HTML 或 Markdown)输出列表数据格式。

const OutputFormat = Object.freeze({
    markdown: 0,
    html: 1,
});

class ListStrategy {
    start(buffer) {}
    end(buffer) {}
    addListItem(buffer, item) {}
}

class MarkdownListStrategy extends ListStrategy {
    addListItem(buffer, item) {
        buffer.push(` * ${item}`);
    }
}

class HtmlListStrategy extends ListStrategy {
    start(buffer) {
        buffer.push("<ul>");
    }
    end(buffer) {
        buffer.push("</ul>");
    }
    addListItem(buffer, item) {
        buffer.push(`   <li>${item}</li>`);
    }
}
复制代码

创建 TextProcessor 类

class TextProcessor {
    constructor(outputFormat) {
        this.buffer = [];
        this.setOutputFormat(outputFormat);
    }

    setOutputFormat(format) {
        switch (format) {
            case OutputFormat.markdown:
                this.listStrategy = new MarkdownListStrategy();
                break;
            case OutputFormat.html:
                this.listStrategy = new HtmlListStrategy();
                break;
        }
    }

    appendList(items) {
        this.listStrategy.start(this.buffer);
        for (const item of items) {
            this.listStrategy.addListItem(this.buffer, item);
        }
        this.listStrategy.end(this.buffer);
    }

    clear() {
        this.buffer = [];
    }

    toString() {
        return this.buffer.join("\n");
    }
}
复制代码

下面是使用方式:

console.log("==============Markdown===============")
const tp = new TextProcessor();
const arrayItems = ["第一条", "第二条", "第三条"];
tp.setOutputFormat(OutputFormat.markdown);
tp.appendList(arrayItems);
console.log(tp.toString());

console.log("==============HTML===============")
tp.clear();
tp.setOutputFormat(OutputFormat.html);
tp.appendList(arrayItems);
console.log(tp.toString());
复制代码

输出结果如下:

==============Markdown===============
 * 第一条
 * 第二条
 * 第三条
==============HTML===============
<ul>
   <li>第一条</li>
   <li>第二条</li>
   <li>第三条</li>
</ul>
复制代码

21. 状态模式:State

状态模式允许对象根据其内部状态的变化来改变其行为。状态模式类返回的对象似乎改变了它的类。它为一组有限的对象提供特定于状态的逻辑,其中每个对象类型代表一个特定的状态。

状态模式是一种行为软件设计模式,它允许对象在其内部状态发生变化时改变其行为。这种模式接近于有限状态机的概念。 —— 维基百科

实例

将举一个电灯开关的例子,打开或关闭开关,它的状态就会改变。

class State {
    constructor() {
        if (this.constructor === State) throw new Error("abstract!");
    }

    on(sw) {
        console.log("灯已打开!");
    }

    off(sw) {
        console.log("灯已关闭!");
    }
}

class OffState extends State {
    constructor() {
        super();
        console.log("关灯");
    }

    on(sw) {
        console.log("开灯中…");
        sw.state = new OnState();
    }
}

class OnState extends State {
    constructor() {
        super();
        console.log("开灯");
    }

    off(sw) {
        console.log("关灯中…");
        sw.state = new OffState();
    }
}

class Switch {
    constructor() {
        this.state = new OffState();
    }

    on() {
        this.state.on(this);
    }

    off() {
        this.state.off(this);
    }
}
复制代码

下面就是使用方法:

const switchHelper = new Switch();
switchHelper.on();
switchHelper.off();
复制代码

将在控制台上可以看到日志:

关灯
开灯中…
开灯
关灯中…
关灯
复制代码

22. 模板方法模式:Template Method

模板方法模式是一种基于定义算法骨架或操作实现的行为设计模式,但将一些步骤推迟到子类。它允许子类重新定义算法的某些步骤,而不改变算法的外部结构。

模板方法是超类中的一个方法,通常是一个抽象超类,并根据许多高级步骤定义了操作的框架。 —— 维基百科

实例

将以国际象棋游戏为例。

class Game {
    constructor(numberOfPlayers) {
        this.numberOfPlayers = numberOfPlayers;
        this.currentPlayer = 0;
    }

    run() {
        this.start();
        while (!this.haveWinner) {
            this.takeTurn();
        }
        console.log(`玩家【${this.winningPlayer}】赢了!`);
    }
    start() {}
    get haveWinner() {}
    takeTurn() {}
    get winningPlayer() {}
}
复制代码

接下来创建继承上面 Game 类的国际象棋 Class。上面的 Game 类就是一个游戏的骨架,下面的 Chess 类就是基于这个骨架来实现其方法。

class Chess extends Game {
    constructor() {
        super(2);
        this.maxTurns = 10;
        this.turn = 1;
    }

    start() {
        console.log(` ${this.numberOfPlayers} 玩家开始国际象棋游戏`);
    }

    get haveWinner() {
        return this.turn === this.maxTurns;
    }

    takeTurn() {
        console.log(
            `Turn ${this.turn++} taken by player ${this.currentPlayer}`
        );
        this.currentPlayer = (this.currentPlayer + 1) % this.numberOfPlayers;
    }

    get winningPlayer() {
        return this.currentPlayer;
    }
}
复制代码

使用的方式如下:

const chess = new Chess();
chess.run();
复制代码

总结

设计模式对软件工程至关重要,并且对于解决常见问题非常有帮助。通过这系列文章,加强对设计模式的理解。

 

给大家推荐一个实用面试题库

1、前端面试题库 (面试必备)            推荐:★★★★★

地址:前端面试题库

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

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

相关文章

【Linux】进程控制

目录&#x1f308;前言&#x1f338;1、进程创建&#x1f361;1.1、概念&#x1f362;1.2、fork()之后执行顺序&#x1f367;1.3、fork()返回值&#x1f368;1.4、写时拷贝&#x1f369;1.5、fork创建失败问题&#x1f341;2、进程终止&#x1f372;2.1、概念&#x1f371;2.2、…

【附源码】Python计算机毕业设计数据学院工作量管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

力扣刷题day48|583两个字符串的删除操作、72编辑距离

文章目录583. 两个字符串的删除操作动态规划思路一动态规划思路二动态规划五部曲72. 编辑距离思路动态规划五部曲583. 两个字符串的删除操作 力扣题目链接 给定两个单词 word1 和 word2 &#xff0c;返回使得 word1 和 word2 相同所需的最小步数。 每步 可以删除任意一个字符…

K_A05_001 基于 STM32等单片机驱动8X8点阵模块(MAX7219)显示0-9

目录 一、资源说明 二、基本参数 1、参数 2、引脚说明 三、通信协议说明 工作时序 对应程序: 四、部分代码说明 1、接线说明 1.1、STC89C52RC8X8点阵模块&#xff08;MAX7219&#xff09; 1.2、STM32F103C8T68X8点阵模块&#xff08;MAX7219&#xff09; 2、亮度调节 五、基…

Python中内置数据库!SQLite使用指南!

&#x1f4a1; 作者&#xff1a;韩信子ShowMeAI &#x1f4d8; Python3◉技能提升系列&#xff1a;https://www.showmeai.tech/tutorials/56 &#x1f4d8; 本文地址&#xff1a;https://www.showmeai.tech/article-detail/390 &#x1f4e2; 声明&#xff1a;版权所有&#xf…

Docker数据卷

Docker数据卷、Docker安全Docker数据卷Docker数据卷管理bind mountdocker数据卷两种方式不同和相同卷插件简介convoy卷插件实践Docker数据卷 主要解决存储问题、容器数据滞留 Docker数据卷管理 bind mount “-v”宿主机路径容器内nginx发布目录 新建一个首页 现在容器内目录…

猿创征文|工具百宝箱-编辑器-笔记工具-日常小工具-原型设计工具

这篇文主要介绍了开发者工具之外的一些日常小工具&#xff0c;我用这些小工具主要完成什么工作。分享给大家 官方活动入口&#xff1a;「猿创征文 」第四季 | 2022 年我的开发者工具 猿创征文&#xff5c;工具百宝箱-代码编辑器-版本控制工具-终端神器-项目与事务跟踪工具-SFTP…

pdf文件转txt怎么转?这几个方法你值得收藏

平时我们在网络上搜索资料的时候&#xff0c;会发现很多资料都是以PDF格式显示的&#xff0c;虽然这种文件格式很方便我们查看&#xff0c;但是如果将其保存到手机中&#xff0c;又太占用内存了。其实我们可以将其转换成txt格式&#xff0c;因为我发现它不仅不占空间&#xff0…

助力数据中心双碳发展,存储如何变得越来越绿?

2022年11月10日&#xff0c;我看到曙光发了首款液冷存储&#xff0c;目标锁定数据中心PUE1.1以下。在“双碳”发展趋势下&#xff0c;聚焦液冷存储与液冷服务器的创新&#xff0c;曙光对绿色数据中心的可持续发展将带来行业引领效应。 双碳大趋势下&#xff0c;绿色存储呼之欲出…

计算机网络复习

考试重点 要掌握OSI七层模型&#xff0c; 会根据数据画模拟和数字信号的图&#xff0c;尤其是TCP和UDP所提供的服务&#xff0c; 掌握TCP连接建立和释放的完整过程&#xff0c; 掌握滑动窗口的概念&#xff0c; 还要了解端到端的含义&#xff0c; 了解ARP、ICMP、CIDR等协议的…

干了3年软件测试,2022年我离职了...

今天在网上刷到一个帖子&#xff0c;说软件测试岗&#xff0c;在公司呆了三年&#xff0c;由于疫情原因&#xff0c;公司效益不是很好&#xff0c;加上自己的技术一直停留在功能测试&#xff0c;在公司可有可无&#xff0c;被公司裁后找不到工作… 逛百度贴吧、逛技术论坛&…

[附源码]java毕业设计基于servlet技术实现游戏娱乐平台

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

FFplay文档解读-48-多媒体过滤器二

32.8 ebur128 EBU R128扫描仪滤光片。此过滤器将音频流作为输入并以不变的方式输出。默认情况下&#xff0c;它以10Hz的频率记录消息&#xff0c;具有瞬时响度&#xff08;由M标识&#xff09;&#xff0c;短期响度&#xff08;S&#xff09;&#xff0c;集成响度&#xff08;…

简单工厂,工厂方法,抽象工厂模式

软件设计七大原则 一、简单工厂&#xff08;静态工厂方法&#xff09; 它存在的目的很简单&#xff1a;定义一个创建对象的接口。组成&#xff1a; 工厂类角色&#xff1a;这是本模式的核心&#xff0c;含有一定的商业逻辑和判断逻 辑。在java中它往往由一个具体类实现。 抽…

南非醉茄来源的天然产物之活性大盘点

图 1. 南非醉茄 (Withania Somnifera) 的多种药理活性[2] 化学成分 到目前为止&#xff0c;大约有超过 12 种生物碱和 40 多种甾体内脂类化合物从南非醉茄中被分离出来 。其中&#xff0c;醉茄内脂 (Withanolides) 因其广泛的药理活性 (抗肿瘤、抗菌、抗炎和免疫调节活性等) 受…

RabbitMQ系列【9】过期时间

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 文章目录概念队列过期控制台代码消息过期删除策略概念 TTL全称Time To Live&#xff0c;是指存活时间或过期时间。当消息到达存活时间后&#xff0c;还没有被消费&#xff0c;会被自动清除。 RabbitMQ…

二十六、设置时序电路初始状态的方法(初始值设置)

----------------------------------------------------------------------------------------------------- 该专栏主要介绍用场效应管设计基本电路,由浅到深,从用场效应管设计最基本的非门、与非门、或非门、与门、或门的设计,到用场效应管设计触发,再到用场效应管设计具…

【论文阅读】Combinatorial Benders’ Cuts for the Strip Packing Problem

文章目录一、摘要二、求解条形装箱的Benders分解2.1 Notation2.2 SPP的数学逻辑模型2.3 分解方法三、从问题的解决方案3.1 复杂性分析3.2 y-check的算法3.2.1 预处理过程3.2.1.1 Merge Items 合并项目3.2.1.2 Lift Item Widths 增大项目宽度3.2.1.3 Shrink the Strip 缩小长条容…

实战+代码!Selenium + Phantom JS爬取天天基金数据

功能&#xff1a; 通过程序实现从基金列表页&#xff0c;获取指定页数内所有基金的近一周收益率以及每支基金的详情页链接。再进入每支基金的详情页获取其余的基金信息&#xff0c;将所有获取到的基金详细信息按近6月收益率倒序排列写入一个Excel表格。 思路&#xff1a; 通过…

python-pandas用法大全

目录1 修改 DataFrame 某一列的数据类型2 读取和保存3 特定值的替换4 两个 DataFrame 的连接4.1 join4.2 某列作为拼接的依据5 删除某一列5.1 删除第n列5.2 删除特定名称列6 行、列重排6.1列重排6.2 行重排6.3 根据某一列的值排序6.4 随机打乱所有行7 修改某列的名称7.1 全局修…