相关文章
设计模式 行为型
一、观察者模式
观察者模式(Observer Pattern)是一种行为型设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,当主题对象状态发生变化时,所有依赖于它的观察者对象都会得到通知并自动更新。观察者模式也被称为发布-订阅(Publish-Subscribe)模式。
主要角色
适用场景
- 当一个系统需要动态地在多个对象中选择一个时,可以使用观察者模式。
- 当一个对象需要将自己的改变通知其他对象,而不知道其他对象是谁时,可以使用观察者模式。
举例
假设有一个简单的气象站系统,气象站会不断地收集天气信息,并将这些信息发送给所有的订阅者(观察者),比如手机应用、网站等。这个系统中,气象站就是主题,而手机应用、网站等就是观察者。当气象信息更新时,所有的订阅者都会得到通知并更新自己的显示内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| class WeatherStation { constructor() { this.observers = []; this.temperature = null; this.humidity = null; this.pressure = null; }
addObserver(observer) { this.observers.push(observer); }
removeObserver(observer) { this.observers = this.observers.filter(obs => obs !== observer); }
setWeatherData(temperature, humidity, pressure) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; this.notifyObservers(); }
notifyObservers() { this.observers.forEach(observer => observer.update(this.temperature, this.humidity, this.pressure)); } }
class PhoneApp { update(temperature, humidity, pressure) { console.log('Phone App: Temperature = ' + temperature + ', Humidity = ' + humidity + ', Pressure = ' + pressure); } }
class Website { update(temperature, humidity, pressure) { console.log('Website: Temperature = ' + temperature + ', Humidity = ' + humidity + ', Pressure = ' + pressure); } }
const weatherStation = new WeatherStation();
const phoneApp = new PhoneApp(); const website = new Website();
weatherStation.addObserver(phoneApp); weatherStation.addObserver(website);
weatherStation.setWeatherData(25, 60, 1013);
|
二、策略模式
策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列算法,并将每个算法封装成一个对象,使得这些算法可以互相替换。策略模式可以让算法的变化独立于使用它们的客户端,从而使客户端能够更灵活地选择算法,并且可以在运行时动态地切换算法。
主要角色
- 策略接口(Strategy):定义了所有支持的算法的公共接口。通常是一个接口或抽象类,其中声明了算法的抽象方法。
- 具体策略(Concrete Strategy):实现了策略接口,提供了具体的算法实现。
- 上下文(Context):维护一个对策略对象的引用,并且在运行时可以动态地改变策略。通常包含了一个设置策略的方法,以及一个调用策略方法的方法。
适用场景
- 当一个系统需要动态地在多个算法中选择一个时,可以使用策略模式。
- 当一个对象有多种行为,并且需要在不同情况下根据需求选择不同行为时,可以使用策略模式。
举例
假设有一个简单的支付系统,根据不同的支付方式计算支付金额,其中每种支付方式都有自己的计算规则。可以使用策略模式来实现这个系统:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| class PaymentStrategy { calculateAmount(amount) {} }
class AlipayStrategy extends PaymentStrategy { calculateAmount(amount) { return amount * 0.95; } }
class WechatPayStrategy extends PaymentStrategy { calculateAmount(amount) { return amount * 0.98; } }
class PaymentContext { constructor(strategy) { this.strategy = strategy; }
setStrategy(strategy) { this.strategy = strategy; }
calculate(amount) { return this.strategy.calculateAmount(amount); } }
const paymentContext = new PaymentContext(new AlipayStrategy()); console.log(paymentContext.calculate(100));
paymentContext.setStrategy(new WechatPayStrategy()); console.log(paymentContext.calculate(100));
|
三、命令模式
命令模式(Command Pattern)是一种行为型设计模式,它将请求封装成对象,从而允许使用不同的请求、队列或者日志来参数化其他对象,并且支持可撤销的操作。命令模式中的关键对象包括命令(Command)、调用者(Invoker)、接收者(Receiver)和客户端(Client)。
主要角色
- 命令(Command):定义了执行操作的接口,包含一个执行方法 execute(),负责调用接收者的相应操作。
- 具体命令(Concrete Command):实现了命令接口,封装了对接收者的调用,将调用操作和接收者解耦。
- 接收者(Receiver):执行实际操作的对象。命令对象通过调用接收者的方法来执行命令所代表的操作。
- 调用者(Invoker):负责调用命令对象执行请求的对象。它包含一个命令对象,并可以通过命令对象执行请求。
- 客户端(Client):创建命令对象并设置其接收者,然后将命令对象传递给调用者来执行。
适用场景
- 当需要对请求进行参数化、排队、记录请求日志等操作时,可以使用命令模式。
- 当需要支持撤销、重做操作时,可以使用命令模式。
- 当需要将请求发送者和接收者解耦时,可以使用命令模式。
举例
假设有一个简单的遥控器应用,其中有一组按钮,每个按钮都对应一个特定的操作,比如打开电视、关闭电视、调高音量、调低音量等。可以使用命令模式来实现这个遥控器应用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| class Command { execute() {} }
class TurnOnTVCommand extends Command { constructor(tv) { super(); this.tv = tv; } execute() { this.tv.turnOn(); } }
class TurnOffTVCommand extends Command { constructor(tv) { super(); this.tv = tv; } execute() { this.tv.turnOff(); } }
class TV { turnOn() { console.log('TV is turned on'); } turnOff() { console.log('TV is turned off'); } }
class RemoteControl { constructor() { this.command = null; } setCommand(command) { this.command = command; } pressButton() { this.command.execute(); } }
const tv = new TV(); const turnOnCommand = new TurnOnTVCommand(tv); const turnOffCommand = new TurnOffTVCommand(tv);
const remoteControl = new RemoteControl();
remoteControl.setCommand(turnOnCommand); remoteControl.pressButton();
remoteControl.setCommand(turnOffCommand); remoteControl.pressButton();
|
四、状态模式
状态模式(State Pattern)是一种行为型设计模式,它允许一个对象在其内部状态改变时改变它的行为。状态模式将对象的行为封装在不同的状态类中,并将对象在不同状态下的行为抽象为一个个状态类,使得状态之间的转换更加清晰,同时也将状态逻辑分散到各个状态类中,使得系统更易于扩展和维护。
主要角色
- 环境(Context):定义了客户感兴趣的接口,并且维护一个具体状态子类的实例,以定义当前状态。
- 抽象状态(State):定义了一个接口以封装环境对象中所有与状态相关的行为。
- 具体状态(Concrete State):实现了抽象状态定义的接口,并为状态机中的某个特定状态提供了具体的实现。
适用场景
- 当一个对象的行为取决于它的状态,并且在运行时可能会根据状态改变行为时,可以使用状态模式。
- 当一个操作中含有庞大的分支语句,且每个分支都与特定的状态相关时,可以考虑使用状态模式。
举例
假设有一个简单的文档编辑器,其中文档可以处于编辑状态、选择状态和插入状态。在编辑状态下,用户可以编辑文本;在选择状态下,用户可以选择文本;在插入状态下,用户可以插入文本。我们可以使用状态模式来实现这个文档编辑器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
| class EditorContext { constructor() { this.state = null; } setState(state) { this.state = state; } type() { this.state.type(); } select() { this.state.select(); } copy() { this.state.copy(); } }
class EditorState { constructor(editor) { this.editor = editor; } type() {} select() {} copy() {} }
class EditingState extends EditorState { type() { console.log('Typing text...'); } select() { console.log('Selecting text...'); } copy() { console.log('Copying text...'); } }
class SelectionState extends EditorState { type() { console.log('Cannot type while selecting'); } select() { console.log('Already selecting text'); } copy() { console.log('Copying selected text...'); } }
class InsertionState extends EditorState { type() { console.log('Inserting text...'); } select() { console.log('Cannot select while inserting'); } copy() { console.log('Cannot copy while inserting'); } }
const editor = new EditorContext(); const editingState = new EditingState(editor); const selectionState = new SelectionState(editor); const insertionState = new InsertionState(editor);
editor.setState(editingState);
editor.type(); editor.select(); editor.copy();
editor.setState(selectionState);
editor.type(); editor.select(); editor.copy();
editor.setState(insertionState);
editor.type(); editor.select(); editor.copy();
|