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

ops/2024/9/24 17:26:11/

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)); // 输出: 8context.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.

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

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


http://www.ppmy.cn/ops/115376.html

相关文章

Android中的引用类型:Weak Reference, Soft Reference, Phantom Reference 和 WeakHashMap

在Android开发中,内存管理是一个非常重要的话题。为了更好地管理内存,Java和Android提供了多种引用类型,包括Weak Reference、Soft Reference、Phantom Reference以及WeakHashMap。这些引用类型在不同的场景下可以帮助我们更有效地管理内存&a…

【IDEA配置Maven环境】

在IDEA欢迎界面 选择 IDEA中 Customize > ALLSettings > Build,Execution,Deployment > Build Tools > Maven

汽车软件开发之敏捷开发

一、前言 目前汽车电子产品,特别是汽车几大域控(如:智能座舱、智能驾驶、智能网联、车身控制)市场竞争激烈,消费者对汽车的需求逐渐多元化和个性化,用户对座舱和智驾产品的要求也越来越高。他们不仅要求产…

c++249多态

#include<iostream> using namespace std; class Parent { public:Parent(int a){this->a a;cout << " Parent" << a << endl;} public:virtual void print()//在子类里面可写可不写 {cout << "Parent" <<a<&l…

oracle生成时间戳字符的两种方法

在Oracle中&#xff0c;生成时间戳字符串可以通过两种方式实现&#xff1a;使用SYSTIMESTAMP函数和使用TO_CHAR函数。 方法一&#xff1a;使用SYSTIMESTAMP函数 SELECT SYSTIMESTAMP FROM DUAL; 方法二&#xff1a;使用TO_CHAR函数 SELECT TO_CHAR(SYSTIMESTAMP, YYYY-MM-D…

jspdf踩坑 htmltocanvas

Maximum call stack size exceeded Occurred while linting D:\project\static\simhei-normal.js:2 at String.match (<anonymous>) at Array.forEach (<anonymous>) at Array.forEach (<anonymous>) 解决方案是换到jspdf.js目录里

【Leetcode152】分割回文串(回溯 | 递归)

文章目录 一、题目二、思路三、代码 一、题目 二、思路 具体例子和步骤&#xff1a;假设 s "aab"&#xff0c;步骤如下&#xff1a; 初始状态&#xff1a; s "aab"path []res [] 第一层递归&#xff08;外层循环&#xff09;&#xff1a; path []检…

Flink 与 Kubernetes (K8s)、YARN 和 Mesos集成对比

Flink 与 Kubernetes (K8s)、YARN 和 Mesos 的紧密集成&#xff0c;是 Flink 能够在不同分布式环境中高效运行的关键特性。 Flink 提供了与这些资源管理系统的深度集成&#xff0c;以便在多种集群管理环境下提交、运行和管理 Flink 作业。Flink 与 K8s、YARN 和 Mesos 集成的详…