JS 的行为设计模式:策略、观察者与命令模式
在软件开发中,设计模式是解决特定问题的通用解决方案。行为设计模式专注于对象之间的通信和职责分配。本文将介绍三种常用的行为设计模式:策略模式、观察者模式和命令模式,以及它们的主要组成部分、工作原理、优缺点和应用场景。
策略模式
策略模式允许在运行时选择算法或行为,从而使得算法的变化独立于使用算法的客户。它定义了一系列算法,并将每个算法封装起来,使它们可以互相替换。
主要组成部分
- 策略接口(Strategy):定义所有支持的算法或策略的统一接口。
- 具体策略(Concrete Strategy):实现策略接口,封装具体的算法或行为。
- 上下文(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
观察者模式
观察者模式定义了对象之间的一对多依赖关系,使得当一个对象的状态发生变化时,所有依赖于它的对象都能够自动收到通知并更新。
主要组成部分
- 主题(Subject):维护观察者的列表,并在状态变化时通知所有注册的观察者。
- 观察者(Observer):定义更新接口,以便在主题状态变化时接收通知。
- 具体主题(Concrete Subject):实现主题接口,维护状态并在状态变化时通知观察者。
- 具体观察者(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
命令模式
命令模式将请求封装为对象,允许使用不同的请求、队列请求或日志请求,以及支持可撤销操作。
主要组成部分
- 命令接口(Command):定义统一的接口,通常包含一个 execute 方法。
- 具体命令(Concrete Command):实现命令接口,封装具体的操作和接收者。
- 接收者(Receiver):包含执行请求的具体方法。
- 调用者(Invoker):持有命令对象并在适当的时候调用命令的 execute 方法。
- 客户端(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.
通过这些设计模式,开发者可以构建更加灵活、可扩展和可维护的软件系统。每种模式都有其特定的应用场景和优缺点,选择合适的模式可以有效地解决特定的设计问题。
-- 欢迎点赞、关注、转发、收藏【我码玄黄】,各大平台同名。