软件设计模式-行为型
ssk-wh Lv4

行为型设计模式关注对象之间的通信方式,以及如何在对象之间有效地传递信息。

以下是几种常见的行为型设计模式:

责任链模式(Chain of Responsibility Pattern):将请求的发送者和接收者解耦,从而使得多个对象都有机会处理这个请求。
命令模式(Command Pattern):将一个请求封装成一个对象,从而使不同的请求可以对客户进行参数化,并支持请求排队、记录日志、撤销操作等功能。
解释器模式(Interpreter Pattern):给定一个语言,定义它的文法表示,并定义一个解释器,用来解释语言中的句子。
迭代器模式(Iterator Pattern):提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。
中介者模式(Mediator Pattern):用一个中介对象来封装一系列的对象交互,从而使各个对象之间的耦合松散,并且可以独立地改变它们之间的交互。
备忘录模式(Memento Pattern):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便之后恢复对象的状态。
观察者模式(Observer Pattern):定义对象间一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都将得到通知并自动更新。
状态模式(State Pattern):允许一个对象在其内部状态改变时改变它的行为。
策略模式(Strategy Pattern):定义算法族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用它的客户端(Context)而独立变化。
模板方法模式(Template Method Pattern):定义一个操作中的算法骨架,将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
访问者模式(Visitor pattern):在访问者模式中,算法被分离出来并在一个称为“访问者”的对象中实现,它可以遍历复杂的数据结构并对其进行操作。

责任链模式

责任链模式(Chain of Responsibility Pattern)是一种行为型模式,它可以使多个对象都有机会处理请求,从而避免了请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。

在责任链模式中,每个处理者都有一个后继处理者,如果当前处理者不能处理该请求,则将请求转发给下一个处理者,直到找到能够处理请求的处理者为止。因此,责任链模式实现了请求的发送者和接收者之间的解耦,让请求可以在不影响客户端的情况下被多个对象处理。

在责任链模式中,通常会定义一个抽象的处理者类,其中包含处理请求的方法和后继处理者的引用。具体的处理者类继承自抽象处理者类,实现自己的处理逻辑,同时将未处理的请求转发给后继处理者。

优缺点

责任链模式的优点是将请求的发送者和接收者解耦,提高系统的灵活性和可扩展性。缺点是由于请求需要在链中传递,因此可能会影响系统的性能。此外,如果链太长或者构造不合理,可能会导致请求无法被处理。

示例

以下是一个基于责任链模式的简单示例,实现了一个电商系统的商品优惠活动审批流程。在这个示例中,有三种不同类型的审批人,分别是初审人员、中审人员和高审人员,每个审批人员都可以审批商品优惠活动,但是不同类型的审批人员有不同的审批权限和审批级别。

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
#include <iostream>
#include <vector>

using namespace std;

// 抽象审批人员类
class Approver {
public:
Approver() = default;
virtual ~Approver() = default;
virtual void approve(int amount) = 0;
void setNext(Approver* next) { next_ = next; }

protected:
Approver* next_ = nullptr;
};

// 初审人员
class FirstApprover : public Approver {
public:
FirstApprover() = default;
~FirstApprover() override = default;
void approve(int amount) override {
if (amount < 100) {
cout << "初审通过!" << endl;
} else if (next_) {
next_->approve(amount);
}
}
};

// 中审人员
class SecondApprover : public Approver {
public:
SecondApprover() = default;
~SecondApprover() override = default;
void approve(int amount) override {
if (amount < 500) {
cout << "中审通过!" << endl;
} else if (next_) {
next_->approve(amount);
}
}
};

// 高审人员
class ThirdApprover : public Approver {
public:
ThirdApprover() = default;
~ThirdApprover() override = default;
void approve(int amount) override {
if (amount < 1000) {
cout << "高审通过!" << endl;
} else {
cout << "审批拒绝!" << endl;
}
}
};

int main() {
FirstApprover first_approver;
SecondApprover second_approver;
ThirdApprover third_approver;

// 设置责任链
first_approver.setNext(&second_approver);
second_approver.setNext(&third_approver);

// 申请活动
first_approver.approve(80); // 初审通过
first_approver.approve(200); // 中审通过
first_approver.approve(800); // 高审通过
first_approver.approve(1500); // 审批拒绝

return 0;
}

在这个示例中,抽象审批人员类定义了一个纯虚函数approve(),表示审批操作。具体的审批人员类(初审人员、中审人员、高审人员)都继承自抽象审批人员类,实现了自己的审批逻辑,并通过setNext()函数设置了自己的下一个审批人员,形成了一个责任链。最后,在main()函数中,创建了三个审批人员对象,然后通过setNext()函数设置它们之间的关系,模拟了一次申请审批的流程。

命令模式

命令模式是一种行为设计模式,它将请求或操作封装为一个对象,从而使可以将不同的请求参数化和队列化。命令模式中,发送请求的对象称为发送者,执行请求的对象称为接收者,而将请求对象称为命令。

命令模式常常用于实现撤销操作,重做操作,日志记录等功能。

示例

假设我们有一个简单的文本编辑器,支持撤销和恢复操作。我们可以使用命令模式来实现这个功能。

首先,定义一个命令接口 Command,其中包含两个方法 execute() 和 undo(),分别用于执行命令和撤销命令。

1
2
3
4
5
6
7
class Command {
public:
virtual ~Command() = default;
virtual void execute() = 0;
virtual void undo() = 0;
};

然后,定义具体的命令类。这里我们假设有两种命令:插入文本和删除文本。

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
class InsertCommand : public Command {
public:
InsertCommand(string& text, int pos, char ch)
: text_(text), pos_(pos), ch_(ch), deleted_(false) {}
void execute() override {
text_.insert(pos_, 1, ch_);
deleted_ = false;
}
void undo() override {
if (!deleted_) {
text_.erase(pos_, 1);
deleted_ = true;
}
}

private:
string& text_;
int pos_;
char ch_;
bool deleted_;
};

class DeleteCommand : public Command {
public:
DeleteCommand(string& text, int pos)
: text_(text), pos_(pos), deleted_char_(text_[pos]), deleted_(false) {}
void execute() override {
if (!deleted_) {
text_.erase(pos_, 1);
deleted_ = true;
}
}
void undo() override {
text_.insert(pos_, 1, deleted_char_);
deleted_ = false;
}

private:
string& text_;
int pos_;
char deleted_char_;
bool deleted_;
};

在文本编辑器中,我们需要有一个命令管理器,用于保存当前的命令和历史命令。每当用户执行一个命令时,该命令将被添加到命令管理器中,当需要撤销命令时,我们可以调用命令管理器中的 undo() 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class CommandManager {
public:
void execute(Command* cmd) {
cmd->execute();
commands_.push_back(cmd);
}
void undo() {
if (!commands_.empty()) {
auto cmd = commands_.back();
cmd->undo();
commands_.pop_back();
}
}

private:
vector<Command*> commands_;
};

最后,在主函数中,我们可以使用命令模式来实现文本编辑器的撤销和恢复操作。

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
int main() {
string text = "hello world";
CommandManager cmd_mgr;

// 执行一些命令
cmd_mgr.execute(new InsertCommand(text, 6, ','));
cmd_mgr.execute(new InsertCommand(text, 7, ' '));
cmd_mgr.execute(new InsertCommand(text, 8, 'C'));
cmd_mgr.execute(new InsertCommand(text, 9, '+'));

cout << "After insert: " << text << endl;

// 撤销最后一个命令
cmd_mgr.undo();
cout << "After undo: " << text << endl;

// 再次撤销
cmd_mgr.undo();
cout << "After undo: " << text << endl;

// 执行一些命令
cmd_mgr.execute(new DeleteCommand (text, 6));
cmd_mgr.execute(new DeleteCommand (text, 7));
cmd_mgr.execute(new DeleteCommand (text, 8));
cmd_mgr.execute(new DeleteCommand (text, 9));

cout << "After delete: " << text << endl;

// 撤销最后一个命令
cmd_mgr.undo();
cout << "After undo: " << text << endl;

// 再次撤销
cmd_mgr.undo();
cout << "After undo: " << text << endl;

return 0;
}

解释器模式

解释器模式(Interpreter Pattern)是一种行为型设计模式,它定义了一种语言文法,以及一种解释器来解释该语言中的语句,使用户能够定义自己的语言,并能够解释执行。该模式常常用于编译器的实现中,也常常用于实现基于规则的系统,如正则表达式处理等。

在解释器模式中,通常有两个角色:抽象表达式(Abstract Expression)和具体表达式(Concrete Expression)。抽象表达式定义了解释器的接口,具体表达式则根据具体的语法规则实现该接口。此外,解释器还可以定义一些上下文相关的信息,这些信息对于理解并解释语言中的语句非常重要。

示例

假设有一个字符串类 String,需要实现一个解释器来解析字符串中的数字并求和。例如,字符串 “1+2+3” 应该解析为数字 1、2、3 并求和得到 6。

首先定义一个抽象解释器类 Interpreter,其中有一个纯虚函数 interpret() 用于解释字符串。然后定义一个具体解释器类 NumberInterpreter,用于解释数字;以及一个具体解释器类 AddInterpreter,用于解释加号。

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
#include <iostream>
#include <string>
#include <vector>

using namespace std;

// 抽象解释器类
class Interpreter {
public:
virtual ~Interpreter() {}
virtual int interpret() = 0;
};

// 数字解释器
class NumberInterpreter : public Interpreter {
public:
NumberInterpreter(int number) : number_(number) {}
int interpret() override {
return number_;
}

private:
int number_;
};

// 加号解释器
class AddInterpreter : public Interpreter {
public:
AddInterpreter(Interpreter* left, Interpreter* right) : left_(left), right_(right) {}
~AddInterpreter() {
delete left_;
delete right_;
}
int interpret() override {
return left_->interpret() + right_->interpret();
}

private:
Interpreter* left_;
Interpreter* right_;
};

// 解释器环境类
class InterpreterContext {
public:
InterpreterContext(const string& input) : input_(input) {}
~InterpreterContext() {
for (auto interpreter : interpreters_) {
delete interpreter;
}
}
int evaluate() {
// 将字符串转为解释器对象
int len = input_.length();
for (int i = 0; i < len; ++i) {
if (isdigit(input_[i])) { // 如果是数字,则转为 NumberInterpreter 对象
int j = i;
while (j < len && isdigit(input_[j])) ++j;
string num = input_.substr(i, j - i);
Interpreter* interpreter = new NumberInterpreter(stoi(num));
interpreters_.push_back(interpreter);
i = j - 1;
} else if (input_[i] == '+') { // 如果是加号,则转为 AddInterpreter 对象
Interpreter* left = interpreters_.back();
Interpreter* right = new NumberInterpreter(stoi(input_.substr(i + 1)));
interpreters_.pop_back();
Interpreter* interpreter = new AddInterpreter(left, right);
interpreters_.push_back(interpreter);
}
}
return interpreters_.back()->interpret();
}

private:
string input_;
vector<Interpreter*> interpreters_;
};

int main() {
InterpreterContext context("1+2+3");
int result = context.evaluate();
cout << "Result: " << result << endl;
return 0;
}

这个示例中,解释器模式将字符串解释为一个语法树,然后再通过遍历语法树求得最终结果。由于这里只有加法和数字,所以语法树很简单,只有两个节点。但如果要支持更多的运算符和操作数,语法树会变得更加复杂。

迭代器模式

迭代器模式(Iterator Pattern)是一种行为型设计模式,它允许客户端遍历容器(如列表、数组等)中的元素,而不需要暴露该容器的内部实现。它通过提供一种统一的接口来遍历不同类型的容器,使得遍历算法可以与特定容器的实现解耦。

迭代器模式主要包含以下角色:

迭代器(Iterator):定义遍历元素所需要的方法,一般来说会有 hasNext() 方法用于判断是否还有元素,next() 方法用于返回当前元素并将指针移动到下一个元素。
具体迭代器(ConcreteIterator):实现迭代器接口,负责遍历具体的容器。
容器(Container):定义返回一个迭代器的方法,例如 iterator() 方法,用于返回一个迭代器对象。
具体容器(ConcreteContainer):实现容器接口,负责存储数据和返回迭代器对象。

示例

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
#include <iostream>
#include <vector>

// 迭代器接口
class Iterator {
public:
virtual ~Iterator() = default;
virtual bool hasNext() = 0;
virtual int next() = 0;
};

// 具体迭代器实现
class VectorIterator : public Iterator {
public:
VectorIterator(std::vector<int>& v) : v_(v), index_(0) {}
bool hasNext() override {
return index_ < v_.size();
}
int next() override {
return v_[index_++];
}

private:
std::vector<int>& v_;
int index_;
};

// 容器接口
class Container {
public:
virtual ~Container() = default;
virtual Iterator* iterator() = 0;
};

// 具体容器实现
class VectorContainer : public Container {
public:
VectorContainer(std::vector<int>& v) : v_(v) {}
Iterator* iterator() override {
return new VectorIterator(v_);
}

private:
std::vector<int>& v_;
};

// 客户端代码
int main() {
std::vector<int> v{1, 2, 3, 4, 5};
VectorContainer container(v);
Iterator* iterator = container.iterator();
while (iterator->hasNext()) {
std::cout << iterator->next() << " ";
}
std::cout << std::endl;
delete iterator;

return 0;
}

这个示例中,我们首先定义了一个迭代器接口 Iterator,其中 hasNext() 方法用于判断是否还有元素,next() 方法用于返回当前元素并将指针移动到下一个元素。接着,我们实现了一个具体的迭代器 VectorIterator,它基于一个 std::vector 容器实现了 hasNext() 和 next() 方法。

在实际开发中,迭代器模式被广泛应用于STL中,比如vector、list、map等STL容器都提供了迭代器,通过迭代器可以方便地对容器中的元素进行遍历和操作。此外,迭代器模式也常用于数据库操作中,尤其是在对大型数据集进行查询和遍历时,通过迭代器可以减少内存占用和提高查询效率。

中介者模式

中介者模式(Mediator Pattern)是一种行为型设计模式,它可以使对象间的通信通过中介者进行,而不是直接相互引用。这种模式中,中介者对象将负责协调多个对象之间的交互,从而使它们能够相互独立地改变和复用。

在中介者模式中,存在一个中介者对象,它封装了一系列的对象之间的交互逻辑。当一个对象发生变化时,它会通知中介者对象,然后由中介者对象进行相应的处理和通知其它相关对象。

优点

中介者模式的优点是将多个对象间的复杂关系和交互逻辑集中到了中介者对象中,从而使得对象间的通信更加简单,同时也提高了代码的可维护性和可扩展性。同时,中介者模式也可以减少对象间的耦合度,使得系统更加灵活和易于扩展。

缺点

但是,中介者模式也有一些缺点。首先,由于中介者对象需要了解并处理多个对象间的逻辑关系,因此它往往会变得比较复杂,难以维护。此外,中介者模式也可能导致系统中出现大量的中介者对象,从而使得系统变得更加复杂。

示例

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
#include <iostream>
#include <string>
#include <vector>

class Colleague;

class Mediator {
public:
virtual ~Mediator() {}
virtual void SendMessage(Colleague* colleague, std::string message) = 0;
};

class Colleague {
public:
Colleague(Mediator* mediator) : mediator_(mediator) {}
virtual ~Colleague() {}
virtual void SendMessage(std::string message) = 0;
virtual void ReceiveMessage(std::string message) = 0;

protected:
Mediator* mediator_;
};

class ConcreteColleagueA : public Colleague {
public:
ConcreteColleagueA(Mediator* mediator) : Colleague(mediator) {}
void SendMessage(std::string message) override {
mediator_->SendMessage(this, message);
}
void ReceiveMessage(std::string message) override {
std::cout << "ConcreteColleagueA received message: " << message << std::endl;
}
};

class ConcreteColleagueB : public Colleague {
public:
ConcreteColleagueB(Mediator* mediator) : Colleague(mediator) {}
void SendMessage(std::string message) override {
mediator_->SendMessage(this, message);
}
void ReceiveMessage(std::string message) override {
std::cout << "ConcreteColleagueB received message: " << message << std::endl;
}
};

class ConcreteMediator : public Mediator {
public:
void AddColleague(Colleague* colleague) {
colleagues_.push_back(colleague);
}
void SendMessage(Colleague* colleague, std::string message) override {
for (auto c : colleagues_) {
if (c != colleague) {
c->ReceiveMessage(message);
}
}
}

private:
std::vector<Colleague*> colleagues_;
};

int main() {
ConcreteMediator mediator;
ConcreteColleagueA colleagueA(&mediator);
ConcreteColleagueB colleagueB(&mediator);

mediator.AddColleague(&colleagueA);
mediator.AddColleague(&colleagueB);

colleagueA.SendMessage("Hello, I'm Colleague A.");
colleagueB.SendMessage("Hi, I'm Colleague B.");

return 0;
}

这个示例定义了中介者(Mediator)和同事(Colleague)两个基本抽象类。中介者定义了同事之间的通信接口,同事通过中介者进行通信。具体的同事类(ConcreteColleagueA和ConcreteColleagueB)实现了同事抽象类的接口,通过中介者与其他同事进行通信。具体中介者类(ConcreteMediator)实现了中介者抽象类的接口,负责管理同事对象,协调同事之间的通信。

在主函数中,我们创建了一个具体中介者对象和两个具体同事对象,将同事对象添加到中介者对象中,并通过同事对象的发送消息接口进行通信。

备忘录模式

备忘录模式是一种行为型设计模式,它允许在不暴露对象实现细节的情况下捕获并存储对象的内部状态,并在以后将对象恢复到该状态。备忘录模式通常用于需要记录对象状态的情况,例如在撤销操作时,或者在处理事务性操作时需要回滚。

备忘录模式由三个主要角色组成:原始对象(Originator)、备忘录(Memento)和负责人(Caretaker)。原始对象负责创建备忘录,并从备忘录中恢复其状态。备忘录是原始对象状态的快照。负责人负责管理备忘录,并在必要时将其传递给原始对象。

备忘录模式的核心思想是将状态的存储和恢复与原始对象分离开来,以保持其封装性。这样可以将对象状态的实现细节隐藏起来,同时还可以更方便地在对象之间共享状态。备忘录模式的一个重要优点是它提供了一种简单且可扩展的方式来支持撤销操作。

场景

备忘录模式的使用场景通常是在需要记录一个对象的内部状态的变化历史,并且希望能够随时回到之前的某个状态时。比如:

编辑器软件中的“撤销”和“重做”功能,用户对文本进行修改时,软件可以将每次修改前的状态记录下来,当用户需要撤销或者重做某个操作时,可以将状态恢复到对应的历史状态。

游戏中的存档和读档功能,游戏可以将当前游戏状态保存到备忘录对象中,当玩家需要恢复到之前的某个状态时,可以通过读取备忘录对象来实现。

购物网站中的“购物车”功能,用户在选购商品时,可以将每个商品的状态记录下来,当用户需要修改购物车内容时,可以随时回到之前的某个状态。

总之,备忘录模式适用于需要记录对象状态变化历史、并且需要能够随时回溯到历史状态的场景。

示例

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
#include <iostream>
#include <string>
#include <vector>

// 备忘录类,用于存储某个状态
class Memento {
public:
Memento() = default;
Memento(const std::string& state) : state_(state) {}
~Memento() = default;

std::string getState() const { return state_; }

private:
std::string state_;
};

// 原始对象类,需要进行备份的对象
class Originator {
public:
Originator() = default;
Originator(const std::string& state) : state_(state) {}
~Originator() = default;

void setState(const std::string& state) { state_ = state; }
std::string getState() const { return state_; }

// 创建备忘录对象,并保存当前状态
Memento createMemento() { return Memento(state_); }

// 根据备忘录对象恢复状态
void restoreMemento(const Memento& memento) { state_ = memento.getState(); }

private:
std::string state_;
};

// 管理备忘录的类
class Caretaker {
public:
Caretaker() = default;
~Caretaker() {
for (auto m : mementos_) {
delete m;
}
mementos_.clear();
}

// 添加备忘录对象
void addMemento(Memento* memento) { mementos_.push_back(memento); }

// 获取指定位置的备忘录对象
Memento* getMemento(int index) const {
if (index < 0 || index >= mementos_.size()) {
return nullptr;
}
return mementos_[index];
}

private:
std::vector<Memento*> mementos_;
};

int main() {
Originator originator("Initial state"); // 创建原始对象
Caretaker caretaker; // 创建备忘录管理对象

// 添加备忘录对象
caretaker.addMemento(originator.createMemento());

// 改变原始对象状态
originator.setState("New state");

// 添加备忘录对象
caretaker.addMemento(originator.createMemento());

// 恢复原始对象状态
originator.restoreMemento(*caretaker.getMemento(0));

std::cout << originator.getState() << std::endl; // 输出 "Initial state"

return 0;
}

观察者模式

观察者模式是一种行为型设计模式,它定义了对象之间的一对多依赖关系,使得当一个对象状态发生变化时,所有依赖于它的对象都会得到通知并自动更新。在观察者模式中,被观察的对象通常被称为主题(Subject),而观察者被称为订阅者(Observer)。

观察者模式的主要优点是实现了松耦合(Loose Coupling)的设计,因为主题和订阅者之间没有直接的依赖关系。这样一来,主题的变化不会影响到订阅者,也不会影响到其他的主题。观察者模式还可以实现动态的发布和订阅机制,因为主题和订阅者可以在运行时动态地注册和取消。

示例

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
#include <iostream>
#include <vector>

// 抽象观察者
class Observer {
public:
virtual ~Observer() = default;
virtual void update() = 0;
};

// 具体观察者
class ConcreteObserver : public Observer {
public:
ConcreteObserver(const std::string& name) : name_(name) {}
void update() override {
std::cout << name_ << " received the update." << std::endl;
}

private:
std::string name_;
};

// 抽象主题
class Subject {
public:
virtual ~Subject() = default;
virtual void attach(Observer* observer) = 0;
virtual void detach(Observer* observer) = 0;
virtual void notify() = 0;
};

// 具体主题
class ConcreteSubject : public Subject {
public:
void attach(Observer* observer) override {
observers_.push_back(observer);
}

void detach(Observer* observer) override {
observers_.erase(std::remove(observers_.begin(), observers_.end(), observer), observers_.end());
}

void notify() override {
for (auto observer : observers_) {
observer->update();
}
}

private:
std::vector<Observer*> observers_;
};

int main() {
ConcreteObserver observer1("Observer 1");
ConcreteObserver observer2("Observer 2");

ConcreteSubject subject;
subject.attach(&observer1);
subject.attach(&observer2);

subject.notify();

subject.detach(&observer1);

subject.notify();

return 0;
}

上面的示例中,Subject 定义了一个抽象主题的接口,包括注册、注销和通知观察者的方法;ConcreteSubject 是一个具体的主题,它维护了一个观察者列表,并在状态变化时通知观察者;Observer 定义了一个抽象观察者的接口,包括更新方法;ConcreteObserver 是一个具体的观察者,它在收到主题的通知后会执行自己的更新方法。在 main 函数中,我们创建了两个具体观察者

状态模式

状态模式(State Pattern)是一种行为型设计模式,它允许对象在其内部状态改变时改变它的行为。这种模式把对象的行为包装在不同的状态对象里,每个状态对象都可以根据其内部状态改变而改变它的行为。

状态模式包含以下角色:

Context(上下文):是具有状态的对象,维护一个当前状态对象,并在状态改变时通知状态对象进行相应的处理。
State(状态):定义状态的接口,对于每个状态的具体实现,它有两个职责:处理自身相关的行为,同时处理状态转移。
ConcreteState(具体状态):具体实现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
#include <iostream>

class Context;

// 状态抽象类
class State {
public:
virtual ~State() = default;
virtual void handle(Context* context) = 0;
};

// 具体状态类A
class ConcreteStateA : public State {
public:
void handle(Context* context) override;
};

// 具体状态类B
class ConcreteStateB : public State {
public:
void handle(Context* context) override;
};

// 上下文类
class Context {
public:
Context(State* state) : state_(state) {}

void setState(State* state) { state_ = state; }

void request() { state_->handle(this); }

private:
State* state_;
};

void ConcreteStateA::handle(Context* context) {
std::cout << "ConcreteStateA handles the request." << std::endl;
context->setState(new ConcreteStateB);
}

void ConcreteStateB::handle(Context* context) {
std::cout << "ConcreteStateB handles the request." << std::endl;
context->setState(new ConcreteStateA);
}

int main() {
State* stateA = new ConcreteStateA;
State* stateB = new ConcreteStateB;
Context* context = new Context(stateA);

// 状态A处理请求
context->request();

// 状态B处理请求
context->request();

delete context;
delete stateA;
delete stateB;

return 0;
}

这个例子中,有两个具体状态类ConcreteStateA和ConcreteStateB,它们实现了State接口,同时定义了它们各自的处理行为和状态转移。Context类作为具有状态的对象,维护了一个State对象,当请求发生时,会将请求交给当前状态对象进行处理,并根据处理结果决定是否需要转移状态。

策略模式

策略模式(Strategy Pattern)是一种行为型设计模式,它允许在运行时动态地选择算法的行为,将算法的实现与使用者分离开来。

优点

在策略模式中,算法被封装成一系列的策略(Strategy),这些策略可以被外部使用者调用,使用者可以根据具体的场景需要动态地选择不同的策略来完成某种任务,而不需要关心具体的算法实现。

策略模式的优点在于它将不同的算法封装到不同的策略类中,使得算法的具体实现与客户端代码相互独立,从而更加容易维护和扩展。另外,策略模式还可以使得客户端代码更加灵活地选择不同的算法实现,从而可以根据不同的情况和需求选择最优的算法。

场景

举个例子,假设我们有一个排序器类(Sorter),该类有一个 sort() 函数用于排序。由于排序算法有很多种,我们可以考虑使用策略模式来将排序算法的实现与使用者分离开来。我们可以定义一个 SortStrategy 抽象类作为排序策略的接口,然后派生出不同的子类来实现具体的排序算法,如 QuickSortStrategy、MergeSortStrategy 等等。在 Sorter 类中,我们可以定义一个成员变量 sort_strategy_,用于保存当前的排序策略,然后在 sort() 函数中调用 sort_strategy_ 的 sort() 函数来完成排序操作。在使用该排序器类的时候,用户可以根据自己的需要选择不同的排序策略来完成排序操作。

示例

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
#include <iostream>
#include <vector>

using namespace std;

// 排序策略基类
class SortStrategy {
public:
virtual ~SortStrategy() {}
virtual void sort(vector<int>& nums) = 0;
};

// 快速排序策略
class QuickSortStrategy : public SortStrategy {
public:
void sort(vector<int>& nums) override {
// 实现快速排序算法
cout << "Using quick sort strategy..." << endl;
}
};

// 归并排序策略
class MergeSortStrategy : public SortStrategy {
public:
void sort(vector<int>& nums) override {
// 实现归并排序算法
cout << "Using merge sort strategy..." << endl;
}
};

// 排序器类
class Sorter {
public:
void setStrategy(SortStrategy* strategy) {
strategy_ = strategy;
}

void sort(vector<int>& nums) {
strategy_->sort(nums);
}

private:
SortStrategy* strategy_;
};

int main() {
vector<int> nums = {3, 1, 4, 2, 5};

Sorter sorter;
QuickSortStrategy quick_sort;
MergeSortStrategy merge_sort;

sorter.setStrategy(&quick_sort);
sorter.sort(nums); // 使用快速排序策略

sorter.setStrategy(&merge_sort);
sorter.sort(nums); // 使用归并排序策略

return 0;
}

在上面的示例代码中,我们首先定义了一个 SortStrategy 抽象类作为排序策略的接口,然后派生出 QuickSortStrategy 和 MergeSortStrategy 等子类来实现排序的功能。在具体场景中,根据不同的情况可以切换不同的策略。

模板方法模式

模板方法模式也叫模板模式,模板模式(Template Pattern)是一种行为型设计模式,它定义了一个操作中的算法框架,将某些步骤延迟到子类中实现,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

该模式通常包含一个抽象模板类,它定义了算法的基本结构,以及一些抽象方法,由子类实现;还有具体子类,实现这些抽象方法,以完成算法的特定步骤。

该模式的优点在于,它使得算法的具体实现与其基本结构分离,使得算法更易于重用和扩展,同时还遵循了开闭原则和单一职责原则。

示例

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
#include <iostream>
#include <vector>

// 模板类
template<typename T>
class AbstractSort {
public:
virtual void sort(std::vector<T>& data) = 0;
};

// 模板实现类A
template<typename T>
class ConcreteSortA : public AbstractSort<T> {
public:
void sort(std::vector<T>& data) override {
std::cout << "Using sort A." << std::endl;
// TODO: 具体排序算法实现
}
};

// 模板实现类B
template<typename T>
class ConcreteSortB : public AbstractSort<T> {
public:
void sort(std::vector<T>& data) override {
std::cout << "Using sort B." << std::endl;
// TODO: 具体排序算法实现
}
};

int main() {
std::vector<int> data{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};

// 使用模板实现类A进行排序
AbstractSort<int>* sortA = new ConcreteSortA<int>();
sortA->sort(data);

// 使用模板实现类B进行排序
AbstractSort<int>* sortB = new ConcreteSortB<int>();
sortB->sort(data);

delete sortA;
delete sortB;

return 0;
}

上述代码定义了一个模板类 AbstractSort,并规定了一个模板方法 sort,该方法接受一个 std::vector 类型的参数,并规定了排序算法的大致流程,但不涉及具体实现。

之后通过继承 AbstractSort 的方式实现了两个不同的排序算法类,即 ConcreteSortA 和 ConcreteSortB,并分别重写了 sort 方法,实现了不同的排序算法。

在 main 函数中,分别创建了 ConcreteSortA 和 ConcreteSortB 对象,并通过指向 AbstractSort 的指针调用 sort 方法实现了不同的排序算法。

访问者模式

访问者模式是一种行为型设计模式,它可以在不修改被访问元素的类的前提下,通过将访问算法从元素中分离出来,使其能够独立地变化。这种模式将数据结构和数据操作分离开来,使得新的操作易于添加到现有系统中。

在访问者模式中,有两种基本元素:被访问的元素和访问者。被访问的元素通常是一个复杂的数据结构,例如一个对象树或一个列表。访问者是一个可以在各种元素上执行各种操作的对象。被访问的元素需要提供一种接受访问者的方法,以便访问者可以遍历它的子元素,并对它们执行特定的操作。

示例

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
#include <iostream>
#include <vector>

class ElementA;
class ElementB;

class Visitor {
public:
virtual ~Visitor() {}
virtual void visit(ElementA& a) = 0;
virtual void visit(ElementB& b) = 0;
};

class Element {
public:
virtual ~Element() {}
virtual void accept(Visitor& v) = 0;
};

class ElementA : public Element {
public:
void accept(Visitor& v) override {
v.visit(*this);
}
};

class ElementB : public Element {
public:
void accept(Visitor& v) override {
v.visit(*this);
}
};

class ConcreteVisitor : public Visitor {
public:
void visit(ElementA& a) override {
std::cout << "ConcreteVisitor: visit ElementA" << std::endl;
}

void visit(ElementB& b) override {
std::cout << "ConcreteVisitor: visit ElementB" << std::endl;
}
};

int main() {
std::vector<Element*> elements{new ElementA, new ElementB};
ConcreteVisitor visitor;

for (auto element : elements) {
element->accept(visitor);
}

for (auto element : elements) {
delete element;
}

return 0;
}

在这个示例中,有两个被访问的元素类:ElementA和ElementB,它们都实现了Element接口,并提供了accept()方法,以便访问者可以访问它们。

还有一个访问者类ConcreteVisitor,它实现了Visitor接口,并提供了visit()方法来访问元素。

在main()函数中,我们创建了一个包含ElementA和ElementB对象的向量。我们创建了一个ConcreteVisitor对象,并在for循环中依次遍历向量中的元素,对每个元素调用accept()方法,将访问者传递给它们。这样,元素就可以访问访问者,并让访问者对它们进行操作。

输出为:

1
2
ConcreteVisitor: visit ElementA
ConcreteVisitor: visit ElementB
 Comments