JS 的行为设计模式:策略、观察者与命令模式

JS 的行为设计模式:策略、观察者与命令模式

在软件开发中,设计模式是解决特定问题的通用解决方案。行为设计模式专注于对象之间的通信和职责分配。本文将介绍三种常用的行为设计模式:策略模式、观察者模式和命令模式,以及它们的主要组成部分、工作原理、优缺点和应用场景。

策略模式

策略模式允许在运行时选择算法或行为,从而使得算法的变化独立于使用算法的客户。它定义了一系列算法,并将每个算法封装起来,使它们可以互相替换。

主要组成部分

  1. 策略接口(Strategy):定义所有支持的算法或策略的统一接口。
  2. 具体策略(Concrete Strategy):实现策略接口,封装具体的算法或行为。
  3. 上下文(Context):持有策略对象的引用,并通过该策略对象调用具体的算法。

优点

  • 灵活性:可以在运行时选择不同的策略。
  • 可扩展性:可以轻松添加新的策略。
  • 单一职责:每个策略类只负责实现一种算法。

缺点

  • 策略数量增加可能导致类的数量增加。
  • 客户端需要了解不同策略的具体实现。

应用场景

  • 需要使用多种算法的场景。
  • 避免使用大量的条件语句。
  • 动态选择算法。

示例

javascript 复制代码
class Strategy {
    execute(a, b) {
        throw new Error("This method should be overridden!");
    }
}

class AddStrategy extends Strategy {
    execute(a, b) {
        return a + b;
    }
}

class SubtractStrategy extends Strategy {
    execute(a, b) {
        return a - b;
    }
}

class Context {
    constructor(strategy) {
        this.strategy = strategy;
    }

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

    executeStrategy(a, b) {
        return this.strategy.execute(a, b);
    }
}

const context = new Context(new AddStrategy());
console.log(context.executeStrategy(5, 3)); // 输出: 8

context.setStrategy(new SubtractStrategy());
console.log(context.executeStrategy(5, 3)); // 输出: 2

观察者模式

观察者模式定义了对象之间的一对多依赖关系,使得当一个对象的状态发生变化时,所有依赖于它的对象都能够自动收到通知并更新。

主要组成部分

  1. 主题(Subject):维护观察者的列表,并在状态变化时通知所有注册的观察者。
  2. 观察者(Observer):定义更新接口,以便在主题状态变化时接收通知。
  3. 具体主题(Concrete Subject):实现主题接口,维护状态并在状态变化时通知观察者。
  4. 具体观察者(Concrete Observer):实现观察者接口,定义对主题状态变化的具体响应。

优点

  • 松耦合:主题和观察者之间的关系是松散的。
  • 动态关系:可以在运行时添加或移除观察者。
  • 自动更新:观察者能够自动接收通知。

缺点

  • 通知开销:如果观察者数量较多,状态变化时的通知开销可能较大。
  • 顺序问题:观察者的通知顺序可能影响程序的行为。

应用场景

  • 事件处理系统。
  • 数据绑定。
  • 消息推送系统。
  • 股票市场监控。
  • 社交媒体通知。
  • 游戏开发。
  • 日志系统。
  • 天气监测系统。

示例

javascript 复制代码
class Subject {
    constructor() {
        this.observers = [];
    }

    registerObserver(observer) {
        this.observers.push(observer);
    }

    removeObserver(observer) {
        this.observers = this.observers.filter(obs => obs !== observer);
    }

    notifyObservers() {
        this.observers.forEach(observer => observer.update(this));
    }
}

class Observer {
    update(subject) {
        throw new Error("This method should be overridden!");
    }
}

class Display implements Observer {
    update(subject) {
        console.log(`Temperature updated: ${subject.getTemperature()}°C`);
    }
}

class WeatherStation extends Subject {
    constructor() {
        super();
        this.temperature = 0;
    }

    setTemperature(temp) {
        this.temperature = temp;
        this.notifyObservers(); // 通知所有观察者
    }

    getTemperature() {
        return this.temperature;
    }
}

const weatherStation = new WeatherStation();
const display = new Display();

weatherStation.registerObserver(display);
weatherStation.setTemperature(25); // 输出: Temperature updated: 25°C

命令模式

命令模式将请求封装为对象,允许使用不同的请求、队列请求或日志请求,以及支持可撤销操作。

主要组成部分

  1. 命令接口(Command):定义统一的接口,通常包含一个 execute 方法。
  2. 具体命令(Concrete Command):实现命令接口,封装具体的操作和接收者。
  3. 接收者(Receiver):包含执行请求的具体方法。
  4. 调用者(Invoker):持有命令对象并在适当的时候调用命令的 execute 方法。
  5. 客户端(Client):创建具体命令对象并设置接收者。

优点

  • 解耦:请求的发送者与接收者之间的解耦。
  • 可扩展性:可以轻松添加新的命令。
  • 支持撤销/重做:可以通过保存命令的历史记录来实现撤销和重做功能。

缺点

  • 命令类数量增加:每个具体命令都需要一个类。
  • 复杂性:对于简单的请求,使用命令模式可能显得过于复杂。

应用场景

  • 图形用户界面(GUI)。
  • 撤销/重做功能。
  • 任务调度。
  • 日志记录。
  • 远程控制。
  • 宏命令。
  • 游戏开发。
  • 网络请求。

示例

javascript 复制代码
class Command {
    execute() {
        throw new Error("This method should be overridden!");
    }
}

class LightOnCommand extends Command {
    constructor(light) {
        super();
        this.light = light;
    }

    execute() {
        this.light.turnOn();
    }
}

class LightOffCommand extends Command {
    constructor(light) {
        super();
        this.light = light;
    }

    execute() {
        this.light.turnOff();
    }
}

class Light {
    turnOn() {
        console.log("The light is on.");
    }

    turnOff() {
        console.log("The light is off.");
    }
}

class RemoteControl {
    constructor() {
        this.command = null;
    }

    setCommand(command) {
        this.command = command;
    }

    pressButton() {
        if (this.command) {
            this.command.execute();
        }
    }
}

const light = new Light();
const lightOn = new LightOnCommand(light);
const lightOff = new LightOffCommand(light);
const remote = new RemoteControl();

remote.setCommand(lightOn);
remote.pressButton(); // 输出: The light is on.

remote.setCommand(lightOff);
remote.pressButton(); // 输出: The light is off.

通过这些设计模式,开发者可以构建更加灵活、可扩展和可维护的软件系统。每种模式都有其特定的应用场景和优缺点,选择合适的模式可以有效地解决特定的设计问题。

-- 欢迎点赞、关注、转发、收藏【我码玄黄】,各大平台同名。

相关推荐
石小石Orz2 分钟前
Three.js + AI:AI 算法生成 3D 萤火虫飞舞效果~
javascript·人工智能·算法
小行星1254 分钟前
前端预览pdf文件流
前端·javascript·vue.js
join85 分钟前
解决vue-pdf的签章不显示问题
javascript·vue.js·pdf
小行星12511 分钟前
前端把dom页面转为pdf文件下载和弹窗预览
前端·javascript·vue.js·pdf
土豆湿27 分钟前
拥抱极简主义前端开发:NoCss.js 引领无 CSS 编程潮流
开发语言·javascript·css
J总裁的小芒果36 分钟前
Vue3 el-table 默认选中 传入的数组
前端·javascript·elementui·typescript
Lei_zhen9638 分钟前
记录一次electron-builder报错ENOENT: no such file or directory, rename xxxx的问题
前端·javascript·electron
辣条小哥哥39 分钟前
electron主进程和渲染进程之间的通信
javascript·electron·ecmascript
咖喱鱼蛋40 分钟前
Electron一些概念理解
前端·javascript·electron
yqcoder42 分钟前
Vue3 + Vite + Electron + TS 项目构建
前端·javascript·vue.js