ES6 类聊 JavaScript 设计模式之行为型模式(二)

简介: 笔记

8.png

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

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

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

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


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

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

实例

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

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();

总结


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


相关文章
|
26天前
|
设计模式 前端开发 JavaScript
JavaScript设计模式及其在实战中的应用,涵盖单例、工厂、观察者、装饰器和策略模式
本文深入探讨了JavaScript设计模式及其在实战中的应用,涵盖单例、工厂、观察者、装饰器和策略模式,结合电商网站案例,展示了设计模式如何提升代码的可维护性、扩展性和可读性,强调了其在前端开发中的重要性。
29 2
|
1月前
|
JavaScript 前端开发 安全
ECMAScript 6(以下简称 ES6)的出现为 JavaScript 带来了许多新的特性和改进,其中 let 和 const 是两个非常重要的关键字。
ES6 引入了 `let` 和 `const` 关键字,为 JavaScript 的变量管理带来了革新。`let` 提供了块级作用域和暂存死区特性,避免变量污染,增强代码可读性和安全性;`const` 用于声明不可重新赋值的常量,但允许对象和数组的内部修改。两者在循环、函数内部及复杂项目中广泛应用,有助于实现不可变数据结构,提升代码质量。
26 5
|
1月前
|
存储 JavaScript 前端开发
JS的ES6知识点
【10月更文挑战第19天】这只是 ES6 的一些主要知识点,ES6 还带来了许多其他的特性和改进,这些特性使得 JavaScript 更加现代化和强大,为开发者提供了更多的便利和灵活性。
25 3
|
2月前
|
JavaScript 前端开发 索引
JavaScript ES6及后续版本:新增的常用特性与亮点解析
JavaScript ES6及后续版本:新增的常用特性与亮点解析
56 4
|
2月前
|
自然语言处理 JavaScript 前端开发
JavaScript高级——ES6基础入门
JavaScript高级——ES6基础入门
31 1
|
1月前
|
前端开发 JavaScript
JavaScript新纪元:ES6+特性深度解析与实战应用
【10月更文挑战第29天】本文深入解析ES6+的核心特性,包括箭头函数、模板字符串、解构赋值、Promise、模块化和类等,结合实战应用,展示如何利用这些新特性编写更加高效和优雅的代码。
45 0
|
2月前
|
前端开发 JavaScript 小程序
JavaScript的ES6中Promise的使用以及个人理解
JavaScript的ES6中Promise的使用以及个人理解
23 1
|
1月前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
|
3月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
|
1月前
|
设计模式 开发者 Python
Python编程中的设计模式:工厂方法模式###
本文深入浅出地探讨了Python编程中的一种重要设计模式——工厂方法模式。通过具体案例和代码示例,我们将了解工厂方法模式的定义、应用场景、实现步骤以及其优势与潜在缺点。无论你是Python新手还是有经验的开发者,都能从本文中获得关于如何在实际项目中有效应用工厂方法模式的启发。 ###
下一篇
DataWorks