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

简介: 笔记

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

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

  • 责任链模式
  • 命令模式
  • 迭代器模式
  • 中介者模式
  • 备忘录模式

13. 责任链模式


责任链模式创建了一个对象链,从一个点开始,它会停止,直到找到某个条件。

为解除请求的发送者和接收者之间耦合,而使多个对象都有机会处理这个请求。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它。—— 维基百科

实例

实例将使用一个生物攻击防御的游戏。游戏中生物将增加它的防御和攻击当它到达一定的点。它会形成一个链条,攻击和防御会增加和减少。

class Creature {
    constructor(name, attack, defense) {
        this.name = name;
        this.attack = attack;
        this.defense = defense;
    }
    toString() {
        return `${this.name} (攻击值:${this.attack} / 防御值:${this.defense})`;
    }
}
class CreatureModifier {
    constructor(creature) {
        this.creature = creature;
        this.next = null;
    }
    add(modifier) {
        if (this.next) {
            this.next.add(modifier);
        } else {
            this.next = modifier;
        }
    }
    handle() {
        if (this.next) {
            this.next.handle();
        }
    }
}
// 增加攻击逻辑
class DoubleAttackModifier extends CreatureModifier {
    constructor(creature) {
        super(creature);
    }
    handle() {
        console.log(`来自 ${this.creature.name} 的双重攻击!`);
        this.creature.attack *= 2;
        super.handle();
    }
}
// 增加防御逻辑
class IncreaseDefenseModifier extends CreatureModifier {
    constructor(creature) {
        super(creature);
    }
    handle() {
        if (this.creature.attack <= 2) {
            console.log(`增加 ${this.creature.name} 的防御!`);
            this.creature.defense++;
        }
        super.handle();
    }
}

接下来看下如何使用责任链模式定义的类。

const bacteria = new Creature("Bacteria", 1, 1); 
console.log(bacteria.toString()); // Bacteria (攻击值:1 / 防御值:1)
const root = new CreatureModifier(bacteria);
root.add(new DoubleAttackModifier(bacteria));
root.add(new IncreaseDefenseModifier(bacteria));
root.handle(); // 来自 Bacteria 的双重攻击! // 增加 Bacteria 的防御!
console.log(bacteria.toString()); // Bacteria (攻击值:2 / 防御值:2)

14. 命令模式


命令模式将创建将动作封装在对象中的对象。

在面向对象的编程中,命令模式是一种行为设计模式,其中对象用于封装执行操作或稍后触发事件所需的所有信息。这些信息包括方法名称、拥有该方法的对象和方法参数的值。  —— 维基百科

实例

代码实例将举一个银行帐户的需求,假如必须存入或提取一定数量的钱,会在其中发出命令。


class BankAccount {
    constructor(balance = 0) {
        this.balance = balance;
    }
    deposit(amount) {
        this.balance += amount;
        console.log(`账户存入:${amount},余额为:${this.balance}`);
    }
    withdraw(amount) {
        if (this.balance - amount >= BankAccount.overdraftLimit) {
            this.balance -= amount;
            console.log(`账户提现:${amount},余额为:${this.balance}`);
        }
    }
    toString() {
        return `余额为 ${this.balance}`;
    }
}
BankAccount.overdraftLimit = -100;  // 假设可以透支 100

接下来创建相应的命令:


const Action = Object.freeze({
    deposit: 1,
    withdraw: 2,
});
class BankAccountCommand {
    constructor(account, action, amount) {
        this.account = account;
        this.action = action;
        this.amount = amount;
    }
    call() {
        const callHandler = {};
        callHandler[Action.deposit] = (amount) => this.account.deposit(amount);
        callHandler[Action.withdraw] = (amount) =>
            this.account.withdraw(amount);
        callHandler[this.action](this.amount);
    }
    undo() {
        const callHandler = {};
        callHandler[Action.deposit] = (amount) => this.account.withdraw(amount);
        callHandler[Action.withdraw] = (amount) => this.account.deposit(amount);
        callHandler[this.action](this.amount);
    }
}
// 
const bankAccount = new BankAccount(100);
const cmd = new BankAccountCommand(bankAccount, Action.deposit, 50);
cmd.call(); // 账户存入:50,余额为:150
console.log(bankAccount.toString()); // 余额为 150
cmd.undo(); // 账户提现:50,余额为:100
console.log(bankAccount.toString()); // 余额为 100

15. 迭代器模式


迭代器模式访问对象的元素而不暴露其底层表示。

在面向对象编程中,迭代器模式是一种设计模式,其中迭代器用于遍历容器并访问容器的元素。 —— 维基百科

实例

将举一个数组的例子,在其中打印一个数组的值,然后使用迭代器将其值对调输出。


class Stuff {
    constructor() {
        this.a = 33;
        this.b = 66;
    }
    [Symbol.iterator]() {
        let i = 0;
        return {
            next: () => {
                return {
                    done: i > 1,
                    value: this[i++ === 0 ? "a" : "b"],
                };
            },
        };
    }
    get backwards() {
        let i = 0;
        return {
            next: () => {
                return {
                    done: i > 1,
                    value: this[i++ === 0 ? "b" : "a"],
                };
            },
            [Symbol.iterator]: function () {
                return this;
            },
        };
    }
}
const values = [100, 200, 300];
for (const i in values) {
    console.log(`索引 ${i} 的值 ${values[i]}`);
}
for (const v in values) {
    console.log(`值为 ${v}`);
}
const stuff = new Stuff();
console.log("=====>正常打印<======");
for (const item of stuff) {
    console.log(item);
}
console.log("=====>对调打印<======");
for (const item of stuff.backwards) {
    console.log(item);
}

运行后在终端显示结果如下:

索引 0 的值 100
索引 1 的值 200
索引 2 的值 300
值为 0
值为 1
值为 2
=====>正常打印<======
33
66
=====>对调打印<======
66
33

16. 中介者模式


中介者模式添加了一个第三方对象来控制两个对象之间的交互。它允许类之间的松散耦合,因为它是唯一详细了解其方法的类。

中介者模式定义了一个对象,该对象封装了一组对象如何交互。这种模式被认为是一种行为模式,因为它可以改变程序的运行行为。在面向对象编程中,程序通常由许多类组成。—— 维基百科

实例

代码是一个人使用聊天室的例子。在这里,聊天室充当了两个人交流的中介。


class Person {
    constructor(name) {
        this.name = name;
        this.chatLog = [];
    }
    receive(sender, message) {
        const s = `${sender}:“${message}”`;
        console.log(`【${this.name}的聊天会话】${s}`);
        this.chatLog.push(s);
    }
    say(message) {
        this.room.broadcast(this.name, message);
    }
    pm(who, message) {
        this.room.message(this.name, who, message);
    }
}
// 创建聊天室
class ChatRoom {
    constructor() {
        this.people = [];
    }
    broadcast(source, message) {
        for (const p of this.people) {
            if (p.name !== source) {
                p.receive(source, message);
            }
        }
    }
    join(p) {
        const joinMsg = `${p.name} 加入聊天室`;
        this.broadcast("room", joinMsg);
        p.room = this;
        this.people.push(p);
    }
    message(source, destination, message) {
        for (const p of this.people) {
            if (p.name === destination) {
                p.receive(source, message);
            }
        }
    }
}
// 使用方式
const room = new ChatRoom();
const Quintion = new Person("Quintion");
const Devpoint = new Person("Devpoint");
room.join(Quintion);
room.join(Devpoint);
Quintion.say("下午好!");
const Tony = new Person("Tony");
room.join(Tony);
Tony.say("大家好!");

运行后在终端可以看到如下结果:

【Quintion的聊天会话】room:“Devpoint 加入聊天室”
【Devpoint的聊天会话】Quintion:“下午好!”
【Quintion的聊天会话】room:“Tony 加入聊天室”
【Devpoint的聊天会话】room:“Tony 加入聊天室”
【Quintion的聊天会话】Tony:“大家好!”
【Devpoint的聊天会话】Tony:“大家好!”

17. 备忘录模式:Memento


备忘录模式将对象恢复到之前的状态。

备忘录模式是一种软件设计模式,它提供了将对象恢复到其先前状态的能力。备忘录模式由三个对象实现:发起者、看守者和备忘录。使用的场景是在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。

实例

将以一个银行账户为例,其中存储之前的状态,并具有撤销功能。

// 备忘录
class Memento {
    constructor(balance) {
        this.balance = balance;
    }
    getBalance() {
        return this.balance;
    }
}
class BankAccount {
    constructor(balance = 0) {
        this.balance = balance;
    }
    deposit(amount) {
        this.balance += amount;
        return new Memento(this.balance);
    }
    restore(m) {
        this.balance = m.balance;
    }
    toString() {
        return `账户余额:${this.balance}`;
    }
}
// 这里开始如何使用上面定义的类
const bankAccount = new BankAccount(1000);
const m1 = bankAccount.deposit(200);
console.log(bankAccount.toString()); // 当前账户余额为:1200
const m2 = bankAccount.deposit(200);
console.log(bankAccount.toString()); // 当前账户余额为:1400
// 恢复到m1
bankAccount.restore(m1);
console.log(bankAccount.toString()); // 账户余额:1200
// 恢复到m2
bankAccount.restore(m2);
console.log(bankAccount.toString()); // 账户余额:1400

写在最后


本文总结了行为型设计模式中的一部份,后面还将继续学习总结剩下的5种设计模式。


相关文章
|
25天前
|
缓存 JavaScript 数据安全/隐私保护
js开发:请解释什么是ES6的Proxy,以及它的用途。
`ES6`的`Proxy`对象用于创建一个代理,能拦截并自定义目标对象的访问和操作,应用于数据绑定、访问控制、函数调用的拦截与修改以及异步操作处理。
17 3
|
25天前
|
JavaScript
js开发:请解释什么是ES6的类(class),并说明它与传统构造函数的区别。
ES6的类提供了一种更简洁的面向对象编程方式,对比传统的构造函数,具有更好的可读性和可维护性。类使用`class`定义,`constructor`定义构造方法,`extends`实现继承,并可直接定义静态方法。示例展示了如何创建`Person`类、`Student`子类以及它们的方法调用。
21 2
|
26天前
|
自然语言处理 JavaScript 网络架构
js开发:请解释什么是ES6的箭头函数,以及它与传统函数的区别。
ES6的箭头函数以`=&gt;`定义,简化了函数写法,具有简洁语法和词法作用域的`this`。它无`arguments`对象,不能用作构造函数,不支持`Generator`,且不改变`this`、`super`、`new.target`绑定。适用于简短表达式,常用于异步编程和高阶函数。
17 5
|
1月前
|
前端开发 JavaScript 开发者
探索JavaScript ES6的八种常见使用技巧:开启现代编程之旅
探索JavaScript ES6的八种常见使用技巧:开启现代编程之旅
|
2月前
|
JavaScript 前端开发
Google Earth Engine(GEE)扩展——ee-polyfill JavaScript 方法 (ES6+)
Google Earth Engine(GEE)扩展——ee-polyfill JavaScript 方法 (ES6+)
16 1
|
3月前
|
XML 前端开发 JavaScript
JavaScript代码优化利器:从ES5到ES6(二)
JavaScript代码优化利器:从ES5到ES6
|
2月前
|
消息中间件 Web App开发 JavaScript
Node.js【简介、安装、运行 Node.js 脚本、事件循环、ES6 作业队列、Buffer(缓冲区)、Stream(流)】(一)-全面详解(学习总结---从入门到深化)
Node.js【简介、安装、运行 Node.js 脚本、事件循环、ES6 作业队列、Buffer(缓冲区)、Stream(流)】(一)-全面详解(学习总结---从入门到深化)
70 0
|
25天前
|
JavaScript 前端开发
js开发:请解释什么是ES6的async/await,以及它如何解决回调地狱问题。
ES6的`async/await`是基于Promise的异步编程工具,能以同步风格编写异步代码,提高代码可读性。它缓解了回调地狱问题,通过将异步操作封装为Promise,避免回调嵌套。错误处理更直观,类似同步的try...catch。
|
25天前
|
JavaScript
js开发:请解释什么是ES6的Generator函数,以及它的用途。
ES6的Generator函数是暂停恢复的特殊函数,用yield返回多个值,适用于异步编程和流处理,解决了回调地狱问题。
16 6
|
25天前
|
存储 JavaScript 索引
js开发:请解释什么是ES6的Map和Set,以及它们与普通对象和数组的区别。
ES6引入了Map和Set数据结构。Map的键可为任意类型,有序且支持get、set、has、delete操作;Set存储唯一值,提供add、delete、has方法。两者皆可迭代。示例展示了Map和Set的基本用法,如添加、查询、删除元素。
13 2