【设计模式】行为型

行为型设计模式

相关文章

设计模式 行为型


一、观察者模式

观察者模式(Observer Pattern)是一种行为型设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,当主题对象状态发生变化时,所有依赖于它的观察者对象都会得到通知并自动更新。观察者模式也被称为发布-订阅(Publish-Subscribe)模式。

主要角色

  • 主题(Subject):也称为被观察者或可观察对象,它是被观察的对象。主题对象中包含了观察者对象的集合,并提供添加、删除和通知观察者的方法。

  • 观察者(Observer):也称为订阅者或监听者,它是观察主题对象状态变化的对象。观察者必须注册到主题中,以便主题在状态发生变化时通知它们。

适用场景

  • 当一个系统需要动态地在多个对象中选择一个时,可以使用观察者模式。
  • 当一个对象需要将自己的改变通知其他对象,而不知道其他对象是谁时,可以使用观察者模式。

举例

假设有一个简单的气象站系统,气象站会不断地收集天气信息,并将这些信息发送给所有的订阅者(观察者),比如手机应用、网站等。这个系统中,气象站就是主题,而手机应用、网站等就是观察者。当气象信息更新时,所有的订阅者都会得到通知并更新自己的显示内容。

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; // 优惠5%
}
}

// 具体策略:微信支付
class WechatPayStrategy extends PaymentStrategy {
calculateAmount(amount) {
return amount * 0.98; // 优惠2%
}
}

// 上下文
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)); // 输出:95

paymentContext.setStrategy(new WechatPayStrategy());
console.log(paymentContext.calculate(100)); // 输出:98

三、命令模式

命令模式(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(); // 输出:TV is turned on

remoteControl.setCommand(turnOffCommand);
remoteControl.pressButton(); // 输出:TV is turned off

四、状态模式

状态模式(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(); // 输出:Typing text...
editor.select(); // 输出:Selecting text...
editor.copy(); // 输出:Copying text...

// 切换到选择状态
editor.setState(selectionState);

// 用户操作
editor.type(); // 输出:Cannot type while selecting
editor.select(); // 输出:Already selecting text
editor.copy(); // 输出:Copying selected text...

// 切换到插入状态
editor.setState(insertionState);

// 用户操作
editor.type(); // 输出:Inserting text...
editor.select(); // 输出:Cannot select while inserting
editor.copy(); // 输出:Cannot copy while inserting

喜欢这篇文章?打赏一下支持一下作者吧!
【设计模式】行为型
https://www.cccccl.com/20220808/设计模式/设计模式 行为型/
作者
Jeffrey
发布于
2022年8月8日
许可协议