Chemmy's Blog

chengming0916@outlook.com

备忘录模式(Memento Pattern)是行为型设计模式的重要分支,其核心设计目标是在不破坏对象封装性的前提下,捕获对象的内部状态并将其持久化到外部容器,以便在需要时快速将对象恢复至指定历史状态。该模式完美解决了“状态保存与恢复”的核心痛点,广泛应用于撤销操作、数据回滚、快照备份等场景。本文将从核心结构拆解、多语言落地实现、优缺点深度剖析、适用场景梳理及实战总结等维度,系统解读备忘录模式的设计思想与工程实践,为开发者提供可直接复用的技术方案。

一、备忘录模式核心结构

备忘录模式的核心价值在于“封装状态、解耦管理”,通过三大核心角色的协同工作,实现状态的安全保存与灵活恢复,各角色职责边界清晰,无冗余依赖,具体定义与交互逻辑如下:

1.1 原发器(Originator)

原发器是需要进行状态管理的核心业务对象,负责两个核心操作:一是创建备忘录,即捕获自身当前的内部状态,并将状态写入备忘录对象;二是恢复状态,即从备忘录中读取历史状态,将自身恢复至该状态。原发器是唯一有权限访问备忘录内部状态的角色,确保状态的封装性。

1.2 备忘录(Memento)

备忘录是状态的“容器”,专门用于存储原发器的内部状态,其设计核心是“封装隔离”。为保证状态安全,备忘录仅暴露供原发器读写状态的接口(或仅允许原发器访问其私有状态),其他角色(如管理者)仅能持有备忘录对象,无法修改或直接访问其内部状态,避免破坏原发器的封装性。

1.3 管理者(Caretaker)

管理者是备忘录的“管家”,负责备忘录对象的生命周期管理,包括存储、获取、清理等操作,但不参与状态的读写。管理者的核心作用是解耦原发器与备忘录的直接依赖,通过统一的接口管理多个备忘录,支持多状态快照的存储与切换(如多步撤销)。

核心交互流程:原发器捕获当前状态 → 创建备忘录并写入状态 → 管理者存储备忘录 → 需恢复状态时,管理者取出指定备忘录 → 原发器从备忘录读取状态并完成恢复。

二、多语言实现备忘录模式

以“文本编辑器撤销功能”为统一实战场景,设计核心逻辑:原发器(编辑器)负责存储当前文本内容,备忘录存储文本快照,管理者管理快照列表,支持多步保存与撤销。以下提供C#、Python、Golang、C++及纯C的可运行实现,贴合各语言原生特性,补充规范注释、边界校验与资源释放逻辑,兼顾实用性与严谨性,清晰呈现模式的适配思路。

2.1 C# 实现(面向对象规范实现)

C#作为强类型面向对象语言,通过类封装备忘录状态,依托访问修饰符(private)保证备忘录的封装性,借助泛型与集合优化管理者的备忘录管理逻辑,代码结构清晰、可维护性高,适用于企业级应用的撤销/回滚场景(如办公软件、业务系统配置管理)。

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
using System;
using System.Collections.Generic;

/// <summary>
/// 备忘录:存储原发器(文本编辑器)的状态,仅允许原发器访问
/// </summary>
public class Memento
{
/// <summary>
/// 存储的文本状态(私有字段,仅通过属性供原发器访问)
/// </summary>
public string TextState { get; private set; }

/// <summary>
/// 构造函数:仅允许原发器创建备忘录(通过访问修饰符控制)
/// </summary>
/// <param name="textState">文本编辑器当前状态</param>
internal Memento(string textState)
{
TextState = textState ?? string.Empty; // 避免空值,提升健壮性
}
}

/// <summary>
/// 原发器:文本编辑器,负责创建/恢复备忘录,管理自身文本状态
/// </summary>
public class Originator
{
/// <summary>
/// 编辑器当前文本状态
/// </summary>
private string _currentText = string.Empty;

/// <summary>
/// 设置编辑器文本状态
/// </summary>
/// <param name="text">新文本内容</param>
public void SetText(string text)
{
_currentText = text ?? string.Empty;
Console.WriteLine($"编辑器更新状态:{_currentText}");
}

/// <summary>
/// 获取当前文本状态
/// </summary>
/// <returns>当前文本内容</returns>
public string GetText() => _currentText;

/// <summary>
/// 创建备忘录:捕获当前文本状态,生成快照
/// </summary>
/// <returns>包含当前状态的备忘录</returns>
public Memento CreateMemento()
{
return new Memento(_currentText);
}

/// <summary>
/// 从备忘录恢复状态:将编辑器恢复至备忘录存储的历史状态
/// </summary>
/// <param name="memento">待恢复的备忘录</param>
public void RestoreMemento(Memento memento)
{
if (memento == null)
{
Console.WriteLine("警告:备忘录不可为null,无法恢复状态");
return;
}
_currentText = memento.TextState;
Console.WriteLine($"编辑器恢复状态:{_currentText}");
}
}

/// <summary>
/// 管理者:管理备忘录列表,支持多快照存储与获取
/// </summary>
public class Caretaker
{
/// <summary>
/// 存储备忘录的队列(FIFO,适配多步撤销场景)
/// </summary>
private readonly Queue<Memento> _mementos = new Queue<Memento>();

/// <summary>
/// 最大快照数量(避免内存溢出)
/// </summary>
private readonly int _maxCount = 10;

/// <summary>
/// 添加备忘录快照
/// </summary>
/// <param name="memento">待存储的备忘录</param>
public void AddMemento(Memento memento)
{
if (memento == null)
{
Console.WriteLine("警告:无法添加空备忘录");
return;
}
// 超过最大快照数量,移除最旧的快照
if (_mementos.Count >= _maxCount)
{
_mementos.Dequeue();
Console.WriteLine("提示:快照数量已达上限,移除最旧快照");
}
_mementos.Enqueue(memento);
Console.WriteLine($"快照添加成功,当前快照数量:{_mementos.Count}");
}

/// <summary>
/// 获取最新的备忘录快照(适配一步撤销)
/// </summary>
/// <returns>最新的备忘录,无快照时返回null</returns>
public Memento GetLatestMemento()
{
if (_mementos.Count == 0)
{
Console.WriteLine("警告:无历史快照可获取");
return null;
}
// 队列尾部为最新快照,此处采用ToArray获取最后一个元素(不改变队列结构)
var mementoArray = _mementos.ToArray();
return mementoArray[^1];
}

/// <summary>
/// 根据索引获取指定备忘录快照(适配多步撤销)
/// </summary>
/// <param name="index">快照索引</param>
/// <returns>指定索引的备忘录,索引无效时返回null</returns>
public Memento GetMemento(int index)
{
if (index < 0 || index >= _mementos.Count)
{
Console.WriteLine("警告:快照索引无效");
return null;
}
return _mementos.ToArray()[index];
}
}

// 客户端:测试文本编辑器的撤销功能
class Program
{
static void Main()
{
try
{
// 1. 初始化组件
Originator editor = new Originator();
Caretaker caretaker = new Caretaker();

// 2. 输入文本并保存快照
editor.SetText("第1步:编写备忘录模式核心概念");
caretaker.AddMemento(editor.CreateMemento());

// 3. 继续输入并保存快照
editor.SetText("第2步:梳理多语言实现思路");
caretaker.AddMemento(editor.CreateMemento());

// 4. 继续输入并保存快照
editor.SetText("第3步:分析优缺点与使用场景");
caretaker.AddMemento(editor.CreateMemento());

// 5. 撤销一步(恢复到第2步状态)
Console.WriteLine("\n=== 执行一步撤销 ===");
editor.RestoreMemento(caretaker.GetLatestMemento());

// 6. 再撤销一步(恢复到第1步状态)
Console.WriteLine("\n=== 执行第二步撤销 ===");
editor.RestoreMemento(caretaker.GetMemento(0));
}
catch (Exception ex)
{
Console.WriteLine($"执行异常:{ex.Message}");
}
}
}

2.2 Python 实现(动态语言简洁实现)

Python遵循“鸭子类型”,无需显式定义接口,通过类封装与属性装饰器(@property)保证备忘录的封装性,语法简洁、无冗余类型声明。依托动态特性,可灵活扩展备忘录的状态字段,无需修改核心逻辑,适用于快速开发、轻量级项目(如小型编辑器、配置工具)。

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
98
class Memento:
"""备忘录:存储文本编辑器的状态,仅允许原发器访问内部状态"""
def __init__(self, text_state):
# 私有字段,通过属性供原发器访问,外部无法直接修改
self._text_state = text_state or ""

@property
def text_state(self):
"""供原发器读取状态的接口"""
return self._text_state

class Originator:
"""原发器:文本编辑器,负责创建/恢复备忘录,管理自身状态"""
def __init__(self):
self._current_text = "" # 编辑器当前文本状态

def set_text(self, text):
"""设置编辑器文本状态,处理空值,提升健壮性"""
self._current_text = text or ""
print(f"编辑器更新状态:{self._current_text}")

def get_text(self):
"""获取当前文本状态"""
return self._current_text

def create_memento(self):
"""创建备忘录,捕获当前文本状态"""
return Memento(self._current_text)

def restore_memento(self, memento):
"""从备忘录恢复状态,校验备忘录合法性"""
if not isinstance(memento, Memento):
print("警告:无效的备忘录对象,无法恢复状态")
return
self._current_text = memento.text_state
print(f"编辑器恢复状态:{self._current_text}")

class Caretaker:
"""管理者:管理备忘录列表,支持多快照存储与多步撤销"""
def __init__(self, max_count=10):
self._mementos = [] # 存储备忘录的列表
self._max_count = max_count # 最大快照数量,避免内存溢出

def add_memento(self, memento):
"""添加备忘录快照,处理空值与数量上限"""
if not isinstance(memento, Memento):
print("警告:无法添加无效的备忘录")
return
# 超过最大数量,移除最旧的快照
if len(self._mementos) >= self._max_count:
self._mementos.pop(0)
print("提示:快照数量已达上限,移除最旧快照")
self._mementos.append(memento)
print(f"快照添加成功,当前快照数量:{len(self._mementos)}")

def get_latest_memento(self):
"""获取最新的备忘录快照(一步撤销)"""
if not self._mementos:
print("警告:无历史快照可获取")
return None
return self._mementos[-1]

def get_memento(self, index):
"""根据索引获取指定备忘录快照(多步撤销)"""
if index < 0 or index >= len(self._mementos):
print("警告:快照索引无效")
return None
return self._mementos[index]

# 客户端测试:模拟文本编辑器的撤销流程
if __name__ == "__main__":
try:
# 1. 初始化组件
editor = Originator()
caretaker = Caretaker(max_count=5)

# 2. 输入文本并保存快照
editor.set_text("第1步:编写备忘录模式核心概念")
caretaker.add_memento(editor.create_memento())

# 3. 继续输入并保存快照
editor.set_text("第2步:梳理多语言实现思路")
caretaker.add_memento(editor.create_memento())

# 4. 继续输入并保存快照
editor.set_text("第3步:分析优缺点与使用场景")
caretaker.add_memento(editor.create_memento())

# 5. 撤销一步(恢复到第2步状态)
print("\n=== 执行一步撤销 ===")
editor.restore_memento(caretaker.get_latest_memento())

# 6. 再撤销一步(恢复到第1步状态)
print("\n=== 执行第二步撤销 ===")
editor.restore_memento(caretaker.get_memento(0))
except Exception as e:
print(f"执行异常:{str(e)}")

2.3 Golang 实现(接口至上轻量实现)

Go语言无类和继承,遵循“组合优于继承”原则,通过结构体封装备忘录与原发器状态,依托“小写字段”实现封装(包内可见、包外不可访问),无需额外访问修饰符。代码极简高效,贴合Go语言设计理念,适配高并发后端场景(如分布式系统配置回滚、服务状态快照)。

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package main

import (
"fmt"
)

// memento 备忘录:小写字段保证包内访问(封装),仅允许原发器访问状态
type memento struct {
textState string // 存储的文本状态
}

// Originator 原发器:文本编辑器,负责创建/恢复备忘录
type Originator struct {
currentText string // 编辑器当前文本状态
}

// SetText 设置编辑器文本状态,处理空值
func (o *Originator) SetText(text string) {
if text == "" {
o.currentText = ""
} else {
o.currentText = text
}
fmt.Printf("编辑器更新状态:%s\n", o.currentText)
}

// GetText 获取当前文本状态
func (o *Originator) GetText() string {
return o.currentText
}

// CreateMemento 创建备忘录,捕获当前状态
func (o *Originator) CreateMemento() *memento {
return &memento{textState: o.currentText}
}

// RestoreMemento 从备忘录恢复状态,校验备忘录合法性
func (o *Originator) RestoreMemento(m *memento) {
if m == nil {
fmt.Println("警告:备忘录不可为nil,无法恢复状态")
return
}
o.currentText = m.textState
fmt.Printf("编辑器恢复状态:%s\n", o.currentText)
}

// Caretaker 管理者:管理备忘录列表,支持多快照存储与撤销
type Caretaker struct {
mementos []*memento // 存储备忘录的切片
maxCount int // 最大快照数量
}

// NewCaretaker 初始化管理者,指定最大快照数量
func NewCaretaker(maxCount int) *Caretaker {
if maxCount <= 0 {
maxCount = 10 // 默认最大快照数量
}
return &Caretaker{
mementos: make([]*memento, 0, maxCount),
maxCount: maxCount,
}
}

// AddMemento 添加备忘录快照,处理数量上限
func (c *Caretaker) AddMemento(m *memento) {
if m == nil {
fmt.Println("警告:无法添加空备忘录")
return
}
// 超过最大数量,移除最旧的快照
if len(c.mementos) >= c.maxCount {
c.mementos = c.mementos[1:]
fmt.Println("提示:快照数量已达上限,移除最旧快照")
}
c.mementos = append(c.mementos, m)
fmt.Printf("快照添加成功,当前快照数量:%d\n", len(c.mementos))
}

// GetLatestMemento 获取最新的备忘录快照(一步撤销)
func (c *Caretaker) GetLatestMemento() *memento {
if len(c.mementos) == 0 {
fmt.Println("警告:无历史快照可获取")
return nil
}
return c.mementos[len(c.mementos)-1]
}

// GetMemento 根据索引获取指定备忘录快照(多步撤销)
func (c *Caretaker) GetMemento(index int) *memento {
if index < 0 || index >= len(c.mementos) {
fmt.Println("警告:快照索引无效")
return nil
}
return c.mementos[index]
}

// 客户端测试:模拟文本编辑器撤销流程
func main() {
// 1. 初始化组件
editor := &Originator{}
caretaker := NewCaretaker(5)

// 2. 输入文本并保存快照
editor.SetText("第1步:编写备忘录模式核心概念")
caretaker.AddMemento(editor.CreateMemento())

// 3. 继续输入并保存快照
editor.SetText("第2步:梳理多语言实现思路")
caretaker.AddMemento(editor.CreateMemento())

// 4. 继续输入并保存快照
editor.SetText("第3步:分析优缺点与使用场景")
caretaker.AddMemento(editor.CreateMemento())

// 5. 撤销一步(恢复到第2步状态)
fmt.Println("\n=== 执行一步撤销 ===")
editor.RestoreMemento(caretaker.GetLatestMemento())

// 6. 再撤销一步(恢复到第1步状态)
fmt.Println("\n=== 执行第二步撤销 ===")
editor.RestoreMemento(caretaker.GetMemento(0))
}

2.4 C++ 实现(抽象类+友元经典实现)

C++通过类与友元机制实现备忘录的封装性,将原发器声明为备忘录的友元,确保只有原发器能访问备忘录的私有状态;同时通过虚析构函数与手动内存管理,避免内存泄漏。该实现兼顾灵活性与高性能,适用于底层开发、高频调用场景(如编译器撤销功能、嵌入式设备配置回滚)。

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#include <iostream>
#include <vector>
#include <string>
using namespace std;

// 前向声明:让管理者识别备忘录
class Memento;

/// <summary>
/// 原发器:文本编辑器,负责创建/恢复备忘录
/// 声明为Memento的友元,有权访问其私有状态
/// </summary>
class Originator {
private:
string currentText; // 编辑器当前文本状态
public:
Originator() : currentText("") {}

/// <summary>
/// 设置编辑器文本状态
/// </summary>
void setText(const string& text) {
currentText = text.empty() ? "" : text;
cout << "编辑器更新状态:" << currentText << endl;
}

/// <summary>
/// 获取当前文本状态
/// </summary>
string getText() const {
return currentText;
}

/// <summary>
/// 创建备忘录,捕获当前状态
/// </summary>
Memento* createMemento();

/// <summary>
/// 从备忘录恢复状态
/// </summary>
void restoreMemento(Memento* m);
};

/// <summary>
/// 备忘录:存储文本状态,仅允许原发器访问私有成员
/// </summary>
class Memento {
private:
string textState; // 存储的文本状态
// 友元声明:仅Originator可访问私有成员
friend class Originator;

/// <summary>
/// 私有构造函数:仅允许原发器创建备忘录
/// </summary>
Memento(const string& state) : textState(state) {}

/// <summary>
/// 私有接口:供原发器读取状态
/// </summary>
string getTextState() const {
return textState;
}
};

// 实现原发器的备忘录方法
Memento* Originator::createMemento() {
return new Memento(currentText);
}

void Originator::restoreMemento(Memento* m) {
if (m == nullptr) {
cout << "警告:备忘录不可为nullptr,无法恢复状态" << endl;
return;
}
currentText = m->getTextState();
cout << "编辑器恢复状态:" << currentText << endl;
}

/// <summary>
/// 管理者:管理备忘录列表,负责内存释放
/// </summary>
class Caretaker {
private:
vector<Memento*> mementos; // 存储备忘录指针的容器
int maxCount; // 最大快照数量
public:
Caretaker(int maxCount = 10) : maxCount(maxCount) {}

/// <summary>
/// 析构函数:释放所有备忘录内存,避免泄漏
/// </summary>
~Caretaker() {
for (auto m : mementos) {
delete m;
m = nullptr;
}
mementos.clear();
}

/// <summary>
/// 添加备忘录快照
/// </summary>
void addMemento(Memento* m) {
if (m == nullptr) {
cout << "警告:无法添加空备忘录" << endl;
return;
}
// 超过最大数量,移除最旧的快照并释放内存
if (mementos.size() >= maxCount) {
delete mementos[0];
mementos[0] = nullptr;
mementos.erase(mementos.begin());
cout << "提示:快照数量已达上限,移除最旧快照" << endl;
}
mementos.push_back(m);
cout << "快照添加成功,当前快照数量:" << mementos.size() << endl;
}

/// <summary>
/// 获取最新的备忘录快照
/// </summary>
Memento* getLatestMemento() {
if (mementos.empty()) {
cout << "警告:无历史快照可获取" << endl;
return nullptr;
}
return mementos.back();
}

/// <summary>
/// 根据索引获取指定备忘录快照
/// </summary>
Memento* getMemento(int index) {
if (index < 0 || index >= (int)mementos.size()) {
cout << "警告:快照索引无效" << endl;
return nullptr;
}
return mementos[index];
}
};

// 客户端测试:模拟文本编辑器撤销流程
int main() {
try {
// 1. 初始化组件
Originator* editor = new Originator();
Caretaker* caretaker = new Caretaker(5);

// 2. 输入文本并保存快照
editor->setText("第1步:编写备忘录模式核心概念");
caretaker->addMemento(editor->createMemento());

// 3. 继续输入并保存快照
editor->setText("第2步:梳理多语言实现思路");
caretaker->addMemento(editor->createMemento());

// 4. 继续输入并保存快照
editor->setText("第3步:分析优缺点与使用场景");
caretaker->addMemento(editor->createMemento());

// 5. 撤销一步(恢复到第2步状态)
cout << "\n=== 执行一步撤销 ===" << endl;
editor->restoreMemento(caretaker->getLatestMemento());

// 6. 再撤销一步(恢复到第1步状态)
cout << "\n=== 执行第二步撤销 ===" << endl;
editor->restoreMemento(caretaker->getMemento(0));

// 释放资源
delete editor;
delete caretaker;
editor = nullptr;
caretaker = nullptr;
} catch (const exception& e) {
cout << "执行异常:" << e.what() << endl;
}
return 0;
}

2.5 纯C语言实现(结构体+函数指针模拟实现)

纯C无面向对象特性,通过“结构体封装数据+函数指针封装行为”模拟备忘录模式的核心逻辑,依托命名约定(前缀下划线)模拟私有字段,通过函数接口控制状态的读写权限,底层可控性强、无额外依赖。适用于嵌入式、底层开发等资源受限场景,完整还原模式“封装状态、解耦管理”的核心思想。

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 前向声明:解决结构体与函数指针的交叉引用
typedef struct Memento Memento;
typedef struct Originator Originator;
typedef struct Caretaker Caretaker;

/// <summary>
/// 备忘录结构体:模拟私有字段(下划线前缀),仅通过接口供原发器访问
/// </summary>
typedef struct Memento {
char* _text_state; // 存储的文本状态(模拟私有字段)
} Memento;

/// <summary>
/// 原发器结构体:文本编辑器,封装状态与方法指针
/// </summary>
typedef struct Originator {
char* current_text; // 当前文本状态
// 方法指针:模拟类的成员方法
Memento* (*create_memento)(Originator*); // 创建备忘录
void (*restore_memento)(Originator*, Memento*); // 恢复状态
} Originator;

/// <summary>
/// 管理者结构体:管理备忘录列表
/// </summary>
typedef struct Caretaker {
Memento** mementos; // 存储备忘录指针的数组
int count; // 当前快照数量
int capacity; // 数组容量
int max_count; // 最大快照数量
} Caretaker;

// -------------------------- 备忘录工具函数 --------------------------
/// <summary>
/// 创建备忘录,初始化状态
/// </summary>
Memento* memento_create(const char* text_state) {
Memento* m = (Memento*)malloc(sizeof(Memento));
if (m == NULL) return NULL;
// 处理空值,避免野指针
int len = text_state ? strlen(text_state) : 0;
m->_text_state = (char*)malloc(len + 1);
if (m->_text_state == NULL) {
free(m);
return NULL;
}
strcpy(m->_text_state, text_state ? text_state : "");
return m;
}

/// <summary>
/// 销毁备忘录,释放内存
/// </summary>
void memento_destroy(Memento* m) {
if (m == NULL) return;
free(m->_text_state);
free(m);
}

/// <summary>
/// 供原发器读取备忘录状态的接口(模拟私有字段访问)
/// </summary>
const char* memento_get_text_state(Memento* m) {
if (m == NULL) return "";
return m->_text_state;
}

// -------------------------- 原发器方法实现 --------------------------
/// <summary>
/// 原发器:创建备忘录,捕获当前状态
/// </summary>
Memento* originator_create_memento(Originator* o) {
if (o == NULL) return NULL;
return memento_create(o->current_text);
}

/// <summary>
/// 原发器:从备忘录恢复状态
/// </summary>
void originator_restore_memento(Originator* o, Memento* m) {
if (o == NULL || m == NULL) {
printf("警告:原发器或备忘录为空,无法恢复状态\n");
return;
}
// 释放原有状态内存,避免泄漏
if (o->current_text != NULL) {
free(o->current_text);
}
// 复制备忘录中的状态
const char* state = memento_get_text_state(m);
int len = strlen(state);
o->current_text = (char*)malloc(len + 1);
strcpy(o->current_text, state);
printf("编辑器恢复状态:%s\n", o->current_text);
}

/// <summary>
/// 原发器:设置文本状态
/// </summary>
void originator_set_text(Originator* o, const char* text) {
if (o == NULL) return;
// 释放原有状态内存
if (o->current_text != NULL) {
free(o->current_text);
}
// 处理空值
int len = text ? strlen(text) : 0;
o->current_text = (char*)malloc(len + 1);
strcpy(o->current_text, text ? text : "");
printf("编辑器更新状态:%s\n", o->current_text);
}

/// <summary>
/// 初始化原发器
/// </summary>
Originator* originator_init() {
Originator* o = (Originator*)malloc(sizeof(Originator));
if (o == NULL) return NULL;
o->current_text = NULL;
o->create_memento = originator_create_memento;
o->restore_memento = originator_restore_memento;
// 初始化状态为空字符串
originator_set_text(o, "");
return o;
}

// -------------------------- 管理者方法实现 --------------------------
/// <summary>
/// 初始化管理者
/// </summary>
Caretaker* caretaker_init(int max_count) {
Caretaker* c = (Caretaker*)malloc(sizeof(Caretaker));
if (c == NULL) return NULL;
// 默认最大快照数量为10
c->max_count = max_count <= 0 ? 10 : max_count;
c->capacity = c->max_count;
c->count = 0;
c->mementos = (Memento**)malloc(sizeof(Memento*) * c->capacity);
if (c->mementos == NULL) {
free(c);
return NULL;
}
return c;
}

/// <summary>
/// 管理者:添加备忘录快照
/// </summary>
void caretaker_add_memento(Caretaker* c, Memento* m) {
if (c == NULL || m == NULL) {
printf("警告:管理者或备忘录为空,无法添加快照\n");
return;
}
// 超过最大数量,移除最旧快照并释放内存
if (c->count >= c->max_count) {
memento_destroy(c->mementos[0]);
// 移动数组,移除第一个元素
for (int i = 0; i < c->count - 1; i++) {
c->mementos[i] = c->mementos[i + 1];
}
c->count--;
printf("提示:快照数量已达上限,移除最旧快照\n");
}
c->mementos[c->count++] = m;
printf("快照添加成功,当前快照数量:%d\n", c->count);
}

/// <summary>
/// 管理者:获取最新的备忘录快照
/// </summary>
Memento* caretaker_get_latest_memento(Caretaker* c) {
if (c == NULL || c->count == 0) {
printf("警告:无历史快照可获取\n");
return NULL;
}
return c->mementos[c->count - 1];
}

/// <summary>
/// 管理者:根据索引获取指定备忘录快照
/// </summary>
Memento* caretaker_get_memento(Caretaker* c, int index) {
if (c == NULL || index < 0 || index >= c->count) {
printf("警告:快照索引无效\n");
return NULL;
}
return c->mementos[index];
}

/// <summary>
/// 释放所有资源
/// </summary>
void cleanup(Originator* o, Caretaker* c) {
// 释放原发器资源
if (o != NULL) {
if (o->current_text != NULL) {
free(o->current_text);
}
free(o);
}
// 释放管理者与备忘录资源
if (c != NULL) {
for (int i = 0; i < c->count; i++) {
memento_destroy(c->mementos[i]);
}
free(c->mementos);
free(c);
}
}

// 客户端测试:模拟文本编辑器撤销流程
int main() {
// 1. 初始化组件
Originator* editor = originator_init();
Caretaker* caretaker = caretaker_init(5);
if (editor == NULL || caretaker == NULL) {
printf("初始化失败,退出程序\n");
cleanup(editor, caretaker);
return -1;
}

// 2. 输入文本并保存快照
originator_set_text(editor, "第1步:编写备忘录模式核心概念");
caretaker_add_memento(caretaker, editor->create_memento(editor));

// 3. 继续输入并保存快照
originator_set_text(editor, "第2步:梳理多语言实现思路");
caretaker_add_memento(caretaker, editor->create_memento(editor));

// 4. 继续输入并保存快照
originator_set_text(editor, "第3步:分析优缺点与使用场景");
caretaker_add_memento(caretaker, editor->create_memento(editor));

// 5. 撤销一步(恢复到第2步状态)
printf("\n=== 执行一步撤销 ===\n");
editor->restore_memento(editor, caretaker_get_latest_memento(caretaker));

// 6. 再撤销一步(恢复到第1步状态)
printf("\n=== 执行第二步撤销 ===\n");
editor->restore_memento(editor, caretaker_get_memento(caretaker, 0));

// 7. 释放所有资源
cleanup(editor, caretaker);
return 0;
}

三、备忘录模式的优缺点

备忘录模式的核心优势的是“封装状态、灵活回滚”,但同时存在资源消耗与维护成本的权衡。实际开发中需结合业务场景,平衡设计优雅与工程落地成本,避免过度设计或滥用。

3.1 核心优点

  • 封装性极佳:备忘录仅允许原发器访问内部状态,外部角色(如管理者)无法直接修改或访问,完美遵循“封装原则”,保证状态数据的安全性。

  • 状态回滚灵活:支持多步快照的保存与恢复,可轻松实现撤销、回滚、快照备份等功能,无需修改原发器核心逻辑,适配多场景状态管理需求。

  • 职责划分清晰:原发器负责状态管理,备忘录负责状态存储,管理者负责备忘录生命周期管理,三者各司其职、解耦关联,符合“单一职责原则”,提升代码可维护性。

  • 扩展性良好:新增状态字段或扩展快照功能时,仅需修改原发器与备忘录,无需改动管理者,对现有业务逻辑侵入性低。

3.2 主要缺点

  • 资源消耗较大:若原发器状态字段多、快照保存频繁,会产生大量备忘录对象,占用内存资源;尤其在高频保存场景(如实时编辑器),可能引发性能瓶颈。

  • 维护成本较高:若原发器的状态结构发生变更(如新增/删除字段),备忘录需同步修改,管理者的存储逻辑也可能需要调整,增加维护成本。

  • 并发安全风险:多线程环境下,多个线程同时读写备忘录或原发器状态,可能引发线程安全问题,需额外添加锁机制,增加代码复杂度。

  • 序列化成本高:若需持久化备忘录(如存储到文件、数据库),需实现序列化/反序列化逻辑,尤其对于复杂状态,序列化成本较高。

四、备忘录模式的使用场景

备忘录模式的适用场景具有明确的核心前提:需要保存对象历史状态,且需支持状态回滚,同时要求不破坏对象封装性。以下结合具体业务场景与实战案例,帮助开发者精准判断适用性,避免滥用。

4.1 核心适用场景

  • 撤销/回滚操作场景:最典型的场景是文本编辑器、表格软件的撤销功能,每输入一步保存一个快照,支持多步撤销;此外,数据库事务回滚、代码版本回滚也属于此类场景。

  • 状态快照备份场景:如游戏存档(保存角色等级、装备、进度等状态)、虚拟机快照(保存虚拟机运行状态)、系统配置快照(保存系统当前配置),支持后续恢复至指定状态。

  • 业务流程回溯场景:如审批流程中,保存每个节点的状态,支持回溯到任意节点重新处理;订单流程中,保存订单的状态变更记录,便于异常时回滚至正常状态。

  • 测试场景:自动化测试中,保存测试用例的初始状态,执行测试后恢复至初始状态,避免测试用例之间的相互影响。

4.2 不适用场景

  • 对象状态简单、无需回滚的场景,使用备忘录模式会增加代码复杂度,直接通过变量存储状态更高效。

  • 状态频繁变更且状态量极大的场景(如实时日志、高频数据采集),大量备忘录会占用过多内存,导致性能下降。

  • 对象封装性要求极低,允许外部直接访问状态的场景,无需通过备忘录间接管理状态。

五、总结

备忘录模式通过“原发器-备忘录-管理者”的三元结构,优雅地解决了“状态保存与恢复”的核心痛点,其核心设计思想是在不破坏对象封装性的前提下,实现状态的安全存储与灵活回滚。该模式的本质是“状态的隔离与管理”,让原发器专注于核心业务逻辑,将状态的存储与恢复交给备忘录和管理者,提升代码的可维护性与扩展性。

不同语言对备忘录模式的实现风格虽有显著差异,但核心逻辑高度一致:面向对象语言(C#、Python、Golang、C++)依托类、接口、友元等特性,轻松实现状态封装与访问控制,代码结构清晰;纯C语言则通过结构体+函数指针模拟面向对象特性,虽实现繁琐,但能在资源受限场景中还原模式核心价值,兼顾底层可控性。

使用备忘录模式时,需重点权衡两个核心点:一是资源消耗,对于高频保存、状态量大的场景,可优化为增量快照(仅保存状态变化部分)或定期清理过期快照,避免内存溢出;二是维护成本,若原发器状态频繁变更,需谨慎使用,避免因状态结构调整导致的大量代码修改。

总之,备忘录模式是“状态管理与回滚”场景下的最优解决方案之一,掌握其核心结构与多语言实现,能帮助开发者在实际项目中优雅处理撤销、快照、回滚等需求,平衡代码设计与工程落地成本,提升系统的健壮性与可扩展性。

访问者模式(Visitor Pattern)是行为型设计模式的核心范式之一,核心思想是分离数据结构与数据操作,在不修改现有数据结构的前提下,灵活扩展新的操作逻辑。该模式通过引入“访问者”角色,将对数据的操作封装为独立实体,让数据结构专注于数据存储,操作逻辑专注于业务处理,尤其适用于数据结构稳定但操作频繁变化的场景。本文将从核心结构、多语言落地实现、优缺点剖析、适用场景及实战总结等维度,系统拆解访问者模式的设计思路与工程实践,为开发者提供可直接复用的技术方案。

一、访问者模式核心结构

访问者模式的核心是通过五大角色的协同,实现“数据与操作”的解耦,各角色职责边界清晰、分工明确,共同构成完整的设计闭环。理解各角色的作用与交互逻辑,是掌握该模式的关键,具体定义如下:

1.1 抽象元素(Element)

抽象元素是数据结构的抽象契约,定义了一个核心方法——接受访问者的Accept方法。该方法的核心作用是将自身作为参数,传递给访问者,触发访问者对当前元素的具体操作,是元素与访问者建立关联的桥梁。

1.2 具体元素(ConcreteElement)

具体元素是抽象元素的具象化实现,负责存储具体数据,并实现Accept方法。其核心逻辑是:在Accept方法中调用访问者对应的访问方法,将自身实例传递给访问者,完成操作的委托执行,确保访问者能获取元素的内部数据并执行处理。

1.3 抽象访问者(Visitor)

抽象访问者是操作逻辑的抽象契约,为每一种具体元素类型声明一个对应的访问方法(如VisitConcreteElementA)。访问方法的参数为对应类型的具体元素,确保访问者能精准操作不同类型的元素,同时规范所有具体访问者的实现标准。

1.4 具体访问者(ConcreteVisitor)

具体访问者是抽象访问者的具象化实现,负责实现抽象访问者声明的所有访问方法,定义对不同具体元素的差异化操作逻辑。一个具体访问者对应一套完整的操作逻辑,新增操作只需新增具体访问者,无需修改现有代码。

1.5 对象结构(ObjectStructure)

对象结构是元素的容器,负责管理所有具体元素的集合,提供元素的添加、删除和遍历方法。其核心作用是统一接收访问者,遍历所有元素并触发元素的Accept方法,让访问者能批量处理集合中的所有元素,简化客户端调用逻辑。

核心交互逻辑:客户端创建对象结构→添加具体元素→创建具体访问者→对象结构接收访问者→遍历元素,触发元素Accept方法→元素调用访问者对应方法→访问者执行具体操作。

二、多语言实现访问者模式

以“数据处理工具”为统一实战场景,设计两类具体元素(ConcreteElementA存储基础数值,ConcreteElementB存储运算数值),两类具体访问者(基础数据查看、数据运算处理),通过对象结构管理元素集合。以下提供C#、Python、Golang、C++及纯C的可运行实现,贴合各语言原生特性,补充规范注释与边界校验,兼顾实用性与严谨性,清晰呈现模式适配思路。

2.1 C# 实现(面向对象规范实现)

C#作为强类型面向对象语言,通过接口定义抽象元素与抽象访问者,依托多态特性实现操作的动态分发,代码结构清晰、可维护性高,适用于企业级业务系统中“数据结构固定、操作易扩展”的场景(如报表生成、数据统计)。

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
using System;
using System.Collections.Generic;

/// <summary>
/// 抽象访问者:定义对所有具体元素的访问契约
/// </summary>
public interface IVisitor
{
/// <summary>访问具体元素A</summary>
/// <param name="elementA">具体元素A实例</param>
void VisitConcreteElementA(ConcreteElementA elementA);

/// <summary>访问具体元素B</summary>
/// <param name="elementB">具体元素B实例</param>
void VisitConcreteElementB(ConcreteElementB elementB);
}

/// <summary>
/// 具体访问者1:基础数据查看操作
/// 核心逻辑:仅展示元素的原始数据,无额外运算
/// </summary>
public class ConcreteVisitor1 : IVisitor
{
public void VisitConcreteElementA(ConcreteElementA elementA)
{
Console.WriteLine($"【{GetType().Name}】处理 {elementA.GetType().Name},原始数据:{elementA.Data}");
}

public void VisitConcreteElementB(ConcreteElementB elementB)
{
Console.WriteLine($"【{GetType().Name}】处理 {elementB.GetType().Name},原始数据:{elementB.Value}");
}
}

/// <summary>
/// 具体访问者2:数据运算处理操作
/// 核心逻辑:对元素数据执行差异化运算(翻倍、平方)
/// </summary>
public class ConcreteVisitor2 : IVisitor
{
public void VisitConcreteElementA(ConcreteElementA elementA)
{
int result = elementA.Data * 2;
Console.WriteLine($"【{GetType().Name}】处理 {elementA.GetType().Name},数据翻倍结果:{result}");
}

public void VisitConcreteElementB(ConcreteElementB elementB)
{
int result = elementB.Value * elementB.Value;
Console.WriteLine($"【{GetType().Name}】处理 {elementB.GetType().Name},数据平方结果:{result}");
}
}

/// <summary>
/// 抽象元素:定义接受访问者的核心方法
/// </summary>
public interface IElement
{
/// <summary>接受访问者访问,触发对应操作</summary>
/// <param name="visitor">访问者实例</param>
void Accept(IVisitor visitor);
}

/// <summary>
/// 具体元素A:存储基础数值数据
/// </summary>
public class ConcreteElementA : IElement
{
/// <summary>元素核心数据</summary>
public int Data { get; set; } = 10;

/// <summary>接受访问者,委托执行对应操作</summary>
public void Accept(IVisitor visitor)
{
visitor.VisitConcreteElementA(this);
}
}

/// <summary>
/// 具体元素B:存储运算数值数据
/// </summary>
public class ConcreteElementB : IElement
{
/// <summary>元素核心数据</summary>
public int Value { get; set; } = 5;

/// <summary>接受访问者,委托执行对应操作</summary>
public void Accept(IVisitor visitor)
{
visitor.VisitConcreteElementB(this);
}
}

/// <summary>
/// 对象结构:管理元素集合,提供批量访问入口
/// </summary>
public class ObjectStructure
{
// 存储元素的集合
private readonly List<IElement> _elements = new List<IElement>();

/// <summary>添加元素到集合</summary>
public void AddElement(IElement element)
{
if (element == null)
{
Console.WriteLine("警告:添加的元素不可为null");
return;
}
_elements.Add(element);
}

/// <summary>接受访问者,遍历所有元素执行操作</summary>
public void Accept(IVisitor visitor)
{
if (visitor == null)
{
Console.WriteLine("警告:访问者不可为null");
return;
}
foreach (var element in _elements)
{
element.Accept(visitor);
}
}
}

// 客户端:测试访问者模式的核心交互逻辑
class Program
{
static void Main(string[] args)
{
try
{
// 1. 创建对象结构(元素容器)
ObjectStructure structure = new ObjectStructure();
// 2. 添加具体元素
structure.AddElement(new ConcreteElementA());
structure.AddElement(new ConcreteElementB());

// 3. 创建具体访问者1(基础数据查看)
IVisitor visitor1 = new ConcreteVisitor1();
Console.WriteLine("=== 执行基础数据查看操作 ===");
structure.Accept(visitor1);

// 4. 创建具体访问者2(数据运算处理)
IVisitor visitor2 = new ConcreteVisitor2();
Console.WriteLine("\n=== 执行数据运算处理操作 ===");
structure.Accept(visitor2);
}
catch (Exception ex)
{
Console.WriteLine($"执行异常:{ex.Message}");
}
}
}

2.2 Python 实现(动态语言简洁实现)

Python遵循“鸭子类型”,无需显式定义接口,通过抽象基类(ABC)约定元素与访问者的行为,语法简洁、无冗余类型声明。依托动态特性,可灵活扩展元素与访问者,无需严格的类型校验,适用于快速开发、轻量级项目(如数据处理脚本)。

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
from abc import ABC, abstractmethod

class Visitor(ABC):
"""抽象访问者:通过抽象方法约定访问行为"""
@abstractmethod
def visit_concrete_element_a(self, element):
"""访问具体元素A"""
pass

@abstractmethod
def visit_concrete_element_b(self, element):
"""访问具体元素B"""
pass

class ConcreteVisitor1(Visitor):
"""具体访问者1:基础数据查看操作"""
def visit_concrete_element_a(self, element):
print(f"【{self.__class__.__name__}】处理 {element.__class__.__name__},原始数据:{element.data}")

def visit_concrete_element_b(self, element):
print(f"【{self.__class__.__name__}】处理 {element.__class__.__name__},原始数据:{element.value}")

class ConcreteVisitor2(Visitor):
"""具体访问者2:数据运算处理操作"""
def visit_concrete_element_a(self, element):
result = element.data * 2
print(f"【{self.__class__.__name__}】处理 {element.__class__.__name__},数据翻倍结果:{result}")

def visit_concrete_element_b(self, element):
result = element.value ** 2
print(f"【{self.__class__.__name__}】处理 {element.__class__.__name__},数据平方结果:{result}")

class Element(ABC):
"""抽象元素:约定接受访问者的方法"""
@abstractmethod
def accept(self, visitor):
"""接受访问者访问"""
pass

class ConcreteElementA(Element):
"""具体元素A:存储基础数值数据"""
def __init__(self):
self.data = 10 # 元素核心数据

def accept(self, visitor):
# 委托访问者执行对应操作,传递自身实例
visitor.visit_concrete_element_a(self)

class ConcreteElementB(Element):
"""具体元素B:存储运算数值数据"""
def __init__(self):
self.value = 5 # 元素核心数据

def accept(self, visitor):
# 委托访问者执行对应操作,传递自身实例
visitor.visit_concrete_element_b(self)

class ObjectStructure:
"""对象结构:管理元素集合,提供批量访问入口"""
def __init__(self):
self.elements = [] # 存储元素的集合

def add_element(self, element):
"""添加元素到集合,校验元素合法性"""
if not isinstance(element, Element):
raise TypeError("添加的元素必须是Element子类实例")
self.elements.append(element)

def accept(self, visitor):
"""接受访问者,遍历所有元素执行操作"""
if not isinstance(visitor, Visitor):
raise TypeError("访问者必须是Visitor子类实例")
for element in self.elements:
element.accept(visitor)

# 客户端测试
if __name__ == "__main__":
try:
# 1. 创建对象结构并添加元素
structure = ObjectStructure()
structure.add_element(ConcreteElementA())
structure.add_element(ConcreteElementB())

# 2. 执行基础数据查看操作
visitor1 = ConcreteVisitor1()
print("=== 执行基础数据查看操作 ===")
structure.accept(visitor1)

# 3. 执行数据运算处理操作
visitor2 = ConcreteVisitor2()
print("\n=== 执行数据运算处理操作 ===")
structure.accept(visitor2)
except Exception as e:
print(f"执行异常:{str(e)}")

2.3 Golang 实现(接口至上轻量实现)

Go语言无类和继承,遵循“组合优于继承”原则,通过接口定义抽象元素与抽象访问者的契约,结构体实现接口方法完成具体逻辑。依托接口隐式实现特性,代码极简高效,贴合Go语言设计理念,适配高并发后端场景(如数据批量处理、中间件扩展)。

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
98
99
100
101
102
103
104
105
106
package main

import "fmt"

// Visitor 抽象访问者接口:定义访问所有具体元素的方法
type Visitor interface {
VisitConcreteElementA(element *ConcreteElementA) // 访问具体元素A
VisitConcreteElementB(element *ConcreteElementB) // 访问具体元素B
}

// ConcreteVisitor1 具体访问者1:基础数据查看操作
type ConcreteVisitor1 struct{}

// VisitConcreteElementA 处理具体元素A,查看原始数据
func (v *ConcreteVisitor1) VisitConcreteElementA(element *ConcreteElementA) {
fmt.Printf("【ConcreteVisitor1】处理 ConcreteElementA,原始数据:%d\n", element.Data)
}

// VisitConcreteElementB 处理具体元素B,查看原始数据
func (v *ConcreteVisitor1) VisitConcreteElementB(element *ConcreteElementB) {
fmt.Printf("【ConcreteVisitor1】处理 ConcreteElementB,原始数据:%d\n", element.Value)
}

// ConcreteVisitor2 具体访问者2:数据运算处理操作
type ConcreteVisitor2 struct{}

// VisitConcreteElementA 处理具体元素A,数据翻倍
func (v *ConcreteVisitor2) VisitConcreteElementA(element *ConcreteElementA) {
result := element.Data * 2
fmt.Printf("【ConcreteVisitor2】处理 ConcreteElementA,数据翻倍结果:%d\n", result)
}

// VisitConcreteElementB 处理具体元素B,数据平方
func (v *ConcreteVisitor2) VisitConcreteElementB(element *ConcreteElementB) {
result := element.Value * element.Value
fmt.Printf("【ConcreteVisitor2】处理 ConcreteElementB,数据平方结果:%d\n", result)
}

// Element 抽象元素接口:定义接受访问者的方法
type Element interface {
Accept(visitor Visitor) // 接受访问者访问
}

// ConcreteElementA 具体元素A:存储基础数值数据
type ConcreteElementA struct {
Data int // 元素核心数据
}

// Accept 接受访问者,委托执行对应操作
func (e *ConcreteElementA) Accept(visitor Visitor) {
visitor.VisitConcreteElementA(e)
}

// ConcreteElementB 具体元素B:存储运算数值数据
type ConcreteElementB struct {
Value int // 元素核心数据
}

// Accept 接受访问者,委托执行对应操作
func (e *ConcreteElementB) Accept(visitor Visitor) {
visitor.VisitConcreteElementB(e)
}

// ObjectStructure 对象结构:管理元素集合,提供批量访问入口
type ObjectStructure struct {
elements []Element // 存储元素的切片
}

// AddElement 添加元素到集合,校验元素合法性
func (os *ObjectStructure) AddElement(element Element) {
if element == nil {
fmt.Println("警告:添加的元素不可为nil")
return
}
os.elements = append(os.elements, element)
}

// Accept 接受访问者,遍历所有元素执行操作
func (os *ObjectStructure) Accept(visitor Visitor) {
if visitor == nil {
fmt.Println("警告:访问者不可为nil")
return
}
for _, element := range os.elements {
element.Accept(visitor)
}
}

// 客户端测试
func main() {
// 1. 创建对象结构并添加元素
structure := &ObjectStructure{}
structure.AddElement(&ConcreteElementA{Data: 10})
structure.AddElement(&ConcreteElementB{Value: 5})

// 2. 执行基础数据查看操作
visitor1 := &ConcreteVisitor1{}
fmt.Println("=== 执行基础数据查看操作 ===")
structure.Accept(visitor1)

// 3. 执行数据运算处理操作
visitor2 := &ConcreteVisitor2{}
fmt.Println("\n=== 执行数据运算处理操作 ===")
structure.Accept(visitor2)
}

2.4 C++ 实现(抽象类+多态经典实现)

C++通过抽象类(纯虚函数)定义抽象元素与抽象访问者,子类继承并实现具体逻辑,依托多态特性实现操作的动态分发,是访问者模式的经典实现方式。需手动管理内存(虚析构函数避免内存泄漏),兼顾灵活性与高性能,适用于底层开发、高频调用场景(如编译器语法树处理、嵌入式数据解析)。

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#include <iostream>
#include <vector>
using namespace std;

// 前向声明具体元素,解决抽象访问者与具体元素的交叉引用问题
class ConcreteElementA;
class ConcreteElementB;

/// <summary>
/// 抽象访问者:纯虚函数定义访问契约
/// </summary>
class Visitor {
public:
virtual ~Visitor() = default; // 虚析构函数,避免多态内存泄漏
// 访问具体元素A的纯虚方法
virtual void VisitConcreteElementA(ConcreteElementA* element) = 0;
// 访问具体元素B的纯虚方法
virtual void VisitConcreteElementB(ConcreteElementB* element) = 0;
};

/// <summary>
/// 具体访问者1:基础数据查看操作
/// </summary>
class ConcreteVisitor1 : public Visitor {
public:
void VisitConcreteElementA(ConcreteElementA* element) override;
void VisitConcreteElementB(ConcreteElementB* element) override;
};

/// <summary>
/// 具体访问者2:数据运算处理操作
/// </summary>
class ConcreteVisitor2 : public Visitor {
public:
void VisitConcreteElementA(ConcreteElementA* element) override;
void VisitConcreteElementB(ConcreteElementB* element) override;
};

/// <summary>
/// 抽象元素:纯虚函数定义接受访问者的方法
/// </summary>
class Element {
public:
virtual ~Element() = default; // 虚析构函数,避免多态内存泄漏
virtual void Accept(Visitor* visitor) = 0; // 接受访问者的纯虚方法
};

/// <summary>
/// 具体元素A:存储基础数值数据
/// </summary>
class ConcreteElementA : public Element {
public:
int data = 10; // 元素核心数据
// 接受访问者,委托执行对应操作
void Accept(Visitor* visitor) override {
visitor->VisitConcreteElementA(this);
}
};

/// <summary>
/// 具体元素B:存储运算数值数据
/// </summary>
class ConcreteElementB : public Element {
public:
int value = 5; // 元素核心数据
// 接受访问者,委托执行对应操作
void Accept(Visitor* visitor) override {
visitor->VisitConcreteElementB(this);
}
};

// 实现具体访问者1的访问方法
void ConcreteVisitor1::VisitConcreteElementA(ConcreteElementA* element) {
cout << "【ConcreteVisitor1】处理 ConcreteElementA,原始数据:" << element->data << endl;
}

void ConcreteVisitor1::VisitConcreteElementB(ConcreteElementB* element) {
cout << "【ConcreteVisitor1】处理 ConcreteElementB,原始数据:" << element->value << endl;
}

// 实现具体访问者2的访问方法
void ConcreteVisitor2::VisitConcreteElementA(ConcreteElementA* element) {
int result = element->data * 2;
cout << "【ConcreteVisitor2】处理 ConcreteElementA,数据翻倍结果:" << result << endl;
}

void ConcreteVisitor2::VisitConcreteElementB(ConcreteElementB* element) {
int result = element->value * element->value;
cout << "【ConcreteVisitor2】处理 ConcreteElementB,数据平方结果:" << result << endl;
}

/// <summary>
/// 对象结构:管理元素集合,提供批量访问入口,负责内存释放
/// </summary>
class ObjectStructure {
private:
vector<Element*> elements; // 存储元素指针的容器
public:
~ObjectStructure() {
// 释放所有元素内存,避免泄漏
for (auto elem : elements) {
delete elem;
elem = nullptr;
}
}

// 添加元素到集合
void AddElement(Element* element) {
if (element == nullptr) {
cout << "警告:添加的元素不可为nullptr" << endl;
return;
}
elements.push_back(element);
}

// 接受访问者,遍历所有元素执行操作
void Accept(Visitor* visitor) {
if (visitor == nullptr) {
cout << "警告:访问者不可为nullptr" << endl;
return;
}
for (auto elem : elements) {
elem->Accept(visitor);
}
}
};

// 客户端测试
int main() {
try {
// 1. 创建对象结构并添加元素
ObjectStructure* structure = new ObjectStructure();
structure->AddElement(new ConcreteElementA());
structure->AddElement(new ConcreteElementB());

// 2. 执行基础数据查看操作
Visitor* visitor1 = new ConcreteVisitor1();
cout << "=== 执行基础数据查看操作 ===" << endl;
structure->Accept(visitor1);

// 3. 执行数据运算处理操作
Visitor* visitor2 = new ConcreteVisitor2();
cout << "\n=== 执行数据运算处理操作 ===" << endl;
structure->Accept(visitor2);

// 释放访问者内存
delete visitor1;
delete visitor2;
visitor1 = nullptr;
visitor2 = nullptr;

// 释放对象结构内存(内部会释放元素内存)
delete structure;
structure = nullptr;
} catch (const exception& e) {
cout << "执行异常:" << e.what() << endl;
}
return 0;
}

2.5 纯C语言实现(结构体+函数指针模拟实现)

纯C无面向对象特性,通过“结构体封装数据+函数指针封装行为”模拟访问者模式的核心逻辑。结构体封装元素与访问者的属性,函数指针模拟抽象方法,底层可控性强、无额外依赖,适用于嵌入式、底层开发等资源受限场景,完整还原模式“数据与操作分离”的核心思想。

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 前向声明:解决结构体与函数指针的交叉引用问题
typedef struct ConcreteElementA ConcreteElementA;
typedef struct ConcreteElementB ConcreteElementB;
typedef struct Visitor Visitor;
typedef struct Element Element;

// 定义访问者函数指针类型(模拟抽象方法)
// 访问具体元素A的函数指针
typedef void (*VisitConcreteElementAFunc)(Visitor* visitor, ConcreteElementA* element);
// 访问具体元素B的函数指针
typedef void (*VisitConcreteElementBFunc)(Visitor* visitor, ConcreteElementB* element);

/// <summary>
/// 抽象访问者结构体:模拟访问者接口,封装函数指针
/// </summary>
typedef struct Visitor {
VisitConcreteElementAFunc visit_concrete_element_a; // 访问元素A的方法
VisitConcreteElementBFunc visit_concrete_element_b; // 访问元素B的方法
char name[20]; // 访问者名称(用于日志输出)
} Visitor;

/// <summary>
/// 具体元素A:存储基础数值数据,封装accept方法
/// </summary>
typedef struct ConcreteElementA {
int data; // 元素核心数据
// accept方法:接受访问者访问
void (*accept)(ConcreteElementA* element, Visitor* visitor);
} ConcreteElementA;

/// <summary>
/// 具体元素B:存储运算数值数据,封装accept方法
/// </summary>
typedef struct ConcreteElementB {
int value; // 元素核心数据
// accept方法:接受访问者访问
void (*accept)(ConcreteElementB* element, Visitor* visitor);
} ConcreteElementB;

/// <summary>
/// 通用元素结构体:用于对象结构存储,统一管理不同类型元素
/// </summary>
typedef struct Element {
enum { ELEMENT_A, ELEMENT_B } type; // 元素类型标识
void* data; // 指向具体元素的指针
// 通用accept方法:适配不同类型元素
void (*accept)(void* element, Visitor* visitor);
} Element;

// -------------------------- 具体访问者1的实现 --------------------------
/// <summary>
/// 具体访问者1(基础数据查看):访问元素A
/// </summary>
void concrete_visitor1_visit_a(Visitor* visitor, ConcreteElementA* element) {
if (visitor == NULL || element == NULL) return;
printf("【%s】处理 ConcreteElementA,原始数据:%d\n", visitor->name, element->data);
}

/// <summary>
/// 具体访问者1(基础数据查看):访问元素B
/// </summary>
void concrete_visitor1_visit_b(Visitor* visitor, ConcreteElementB* element) {
if (visitor == NULL || element == NULL) return;
printf("【%s】处理 ConcreteElementB,原始数据:%d\n", visitor->name, element->value);
}

// -------------------------- 具体访问者2的实现 --------------------------
/// <summary>
/// 具体访问者2(数据运算处理):访问元素A,数据翻倍
/// </summary>
void concrete_visitor2_visit_a(Visitor* visitor, ConcreteElementA* element) {
if (visitor == NULL || element == NULL) return;
int result = element->data * 2;
printf("【%s】处理 ConcreteElementA,数据翻倍结果:%d\n", visitor->name, result);
}

/// <summary>
/// 具体访问者2(数据运算处理):访问元素B,数据平方
/// </summary>
void concrete_visitor2_visit_b(Visitor* visitor, ConcreteElementB* element) {
if (visitor == NULL || element == NULL) return;
int result = element->value * element->value;
printf("【%s】处理 ConcreteElementB,数据平方结果:%d\n", visitor->name, result);
}

// -------------------------- 具体元素的accept方法实现 --------------------------
/// <summary>
/// 元素A的accept方法:委托访问者执行对应操作
/// </summary>
void concrete_element_a_accept(ConcreteElementA* element, Visitor* visitor) {
if (element == NULL || visitor == NULL) return;
visitor->visit_concrete_element_a(visitor, element);
}

/// <summary>
/// 元素B的accept方法:委托访问者执行对应操作
/// </summary>
void concrete_element_b_accept(ConcreteElementB* element, Visitor* visitor) {
if (element == NULL || visitor == NULL) return;
visitor->visit_concrete_element_b(visitor, element);
}

/// <summary>
/// 通用元素的accept方法:根据元素类型,调用对应具体元素的accept方法
/// </summary>
void element_accept(void* elem, Visitor* visitor) {
if (elem == NULL || visitor == NULL) return;
Element* element = (Element*)elem;
if (element->type == ELEMENT_A) {
ConcreteElementA* a = (ConcreteElementA*)element->data;
a->accept(a, visitor);
} else if (element->type == ELEMENT_B) {
ConcreteElementB* b = (ConcreteElementB*)element->data;
b->accept(b, visitor);
}
}

// -------------------------- 对象结构实现 --------------------------
/// <summary>
/// 对象结构:管理元素集合,提供批量访问入口,负责资源释放
/// </summary>
typedef struct ObjectStructure {
Element** elements; // 存储通用元素指针的数组
int count; // 当前元素数量
int capacity; // 数组容量
} ObjectStructure;

/// <summary>
/// 初始化对象结构
/// </summary>
ObjectStructure* object_structure_init(int capacity) {
if (capacity <= 0) return NULL;
ObjectStructure* os = (ObjectStructure*)malloc(sizeof(ObjectStructure));
if (os == NULL) return NULL;
os->elements = (Element**)malloc(sizeof(Element*) * capacity);
if (os->elements == NULL) {
free(os);
return NULL;
}
os->count = 0;
os->capacity = capacity;
return os;
}

/// <summary>
/// 向对象结构添加元素
/// </summary>
void object_structure_add(ObjectStructure* os, Element* element) {
if (os == NULL || element == NULL || os->count >= os->capacity) return;
os->elements[os->count++] = element;
}

/// <summary>
/// 接受访问者,遍历所有元素执行操作
/// </summary>
void object_structure_accept(ObjectStructure* os, Visitor* visitor) {
if (os == NULL || visitor == NULL) return;
for (int i = 0; i < os->count; i++) {
os->elements[i]->accept(os->elements[i], visitor);
}
}

/// <summary>
/// 释放对象结构及内部资源
/// </summary>
void object_structure_free(ObjectStructure* os) {
if (os == NULL) return;
// 释放每个元素的具体数据和通用元素结构体
for (int i = 0; i < os->count; i++) {
free(os->elements[i]->data);
free(os->elements[i]);
}
free(os->elements);
free(os);
}

// 客户端测试
int main() {
// 1. 初始化具体访问者1(基础数据查看)
Visitor visitor1 = {
.visit_concrete_element_a = concrete_visitor1_visit_a,
.visit_concrete_element_b = concrete_visitor1_visit_b,
.name = "ConcreteVisitor1"
};

// 2. 初始化具体访问者2(数据运算处理)
Visitor visitor2 = {
.visit_concrete_element_a = concrete_visitor2_visit_a,
.visit_concrete_element_b = concrete_visitor2_visit_b,
.name = "ConcreteVisitor2"
};

// 3. 初始化具体元素A
ConcreteElementA* elemA = (ConcreteElementA*)malloc(sizeof(ConcreteElementA));
if (elemA == NULL) return -1;
elemA->data = 10;
elemA->accept = concrete_element_a_accept;
// 包装为通用元素
Element* elementA = (Element*)malloc(sizeof(Element));
if (elementA == NULL) {
free(elemA);
return -1;
}
elementA->type = ELEMENT_A;
elementA->data = elemA;
elementA->accept = element_accept;

// 4. 初始化具体元素B
ConcreteElementB* elemB = (ConcreteElementB*)malloc(sizeof(ConcreteElementB));
if (elemB == NULL) {
free(elementA);
free(elemA);
return -1;
}
elemB->value = 5;
elemB->accept = concrete_element_b_accept;
// 包装为通用元素
Element* elementB = (Element*)malloc(sizeof(Element));
if (elementB == NULL) {
free(elemB);
free(elementA);
free(elemA);
return -1;
}
elementB->type = ELEMENT_B;
elementB->data = elemB;
elementB->accept = element_accept;

// 5. 初始化对象结构并添加元素
ObjectStructure* structure = object_structure_init(2);
if (structure == NULL) {
free(elementB);
free(elemB);
free(elementA);
free(elemA);
return -1;
}
object_structure_add(structure, elementA);
object_structure_add(structure, elementB);

// 6. 执行访问操作
printf("=== 执行基础数据查看操作 ===\n");
object_structure_accept(structure, &visitor1);

printf("\n=== 执行数据运算处理操作 ===\n");
object_structure_accept(structure, &visitor2);

// 7. 释放所有资源
object_structure_free(structure);
return 0;
}

三、访问者模式的优缺点

访问者模式的核心价值是实现“数据与操作的解耦”,其优势集中在操作的灵活扩展,而局限则体现在元素扩展的困难性。实际开发中需结合业务场景权衡使用,平衡设计优雅与工程落地成本,避免过度设计。

3.1 核心优点

  • 操作扩展灵活,符合开闭原则:新增操作只需新增具体访问者,无需修改现有元素代码,大幅降低扩展成本。例如,新增“数据格式化”操作,仅需实现一个新的具体访问者,无需改动元素和对象结构。

  • 操作逻辑集中,便于维护复用:同一类操作逻辑集中在一个具体访问者中,避免操作逻辑分散在多个元素中,提升代码可读性和可维护性,同时便于同类操作的复用。

  • 分离数据与操作,降低耦合:数据结构(元素)专注于数据存储,操作逻辑(访问者)独立管理,打破数据与操作的强关联,让代码结构更清晰,符合“单一职责原则”。

  • 支持多态遍历,简化复杂处理:通过访问者可实现对不同类型元素的差异化处理,无需在客户端编写大量条件判断,简化复杂集合的遍历与处理逻辑。

3.2 主要缺点

  • 元素扩展困难,违反开闭原则:新增具体元素时,需修改所有抽象访问者和具体访问者的代码,添加对应的访问方法,大幅增加扩展成本,不适用于元素频繁变化的场景。

  • 破坏元素封装性:访问者需要访问元素的内部数据(如私有属性)才能执行操作,可能暴露元素的实现细节,破坏面向对象的封装性。

  • 代码复杂度提升,理解成本高:模式引入五大角色,且元素与访问者之间存在双向依赖,增加代码的复杂度和理解难度,不利于新手维护。

  • 面向过程语言适配性差:纯C等面向过程语言需通过复杂的结构体和函数指针模拟面向对象特性,实现代码繁琐、可读性低,增加开发和调试成本。

四、访问者模式的使用场景

访问者模式的适用场景具有明确的前提:数据结构稳定,操作频繁变化。以下结合具体业务场景与实战案例,帮助开发者精准判断适用性,避免滥用。

4.1 核心适用场景

  • 数据结构稳定,操作易变的场景:最典型的场景是编译器的语法树(AST),语法树结构固定,但需支持类型检查、代码生成、语法优化、错误检测等多种操作,新增操作只需新增访问者。

  • 跨元素的统一操作场景:对不同类型的对象执行统一的批量操作,如对文本、图片、视频等不同类型文件,执行备份、压缩、加密等操作,每种操作对应一个访问者。

  • 复杂集合的遍历与差异化处理场景:如电商系统中,对订单、商品、用户等不同实体,执行数据统计、报表生成、权限校验等操作,通过访问者实现差异化处理。

  • 框架/库的扩展场景:如ORM框架中,对不同数据库(MySQL、PostgreSQL、Oracle)执行增删改查操作,每种数据库的操作逻辑封装为一个访问者,便于扩展新数据库。

4.2 不适用场景

  • 元素类型频繁变化的场景(如频繁新增、删除元素),会导致访问者代码频繁修改,违反开闭原则。

  • 元素封装性要求极高的场景,访问者需要访问元素内部数据,会破坏封装。

  • 简单操作场景(仅需1-2种操作),使用访问者模式会增加代码复杂度,直接调用方法更高效。

五、总结

访问者模式的核心价值在于分离数据结构与数据操作,其设计初衷是解决“数据结构稳定但操作易变”的问题,通过引入访问者角色,实现操作的灵活扩展,同时让数据结构专注于自身职责。不同语言对该模式的实现方式差异显著,但核心思想一致:

面向对象语言(C#、Python、Golang、C++)可通过接口/抽象类直接适配,依托多态特性实现操作的动态分发,代码结构清晰、可维护性高;纯C语言则需通过结构体+函数指针模拟面向对象特性,虽实现繁琐,但能在资源受限场景中还原模式核心逻辑。

使用访问者模式时,需明确核心前提:数据结构是否稳定、操作是否频繁变化。若元素类型频繁变化,应避免使用;若操作逻辑频繁扩展,访问者模式能显著降低代码耦合,提升扩展能力。在实际开发中,需摒弃“为了使用设计模式而使用”的思维,结合业务场景合理选择,在设计优雅与代码可读性、可维护性之间找到平衡,让设计模式真正服务于工程实践。

责任链模式

责任链模式(Chain of Responsibility Pattern)是行为型设计模式的经典范式,核心是解耦请求发送者与接收者,通过将多个处理对象串联为有序链路,使请求沿链路逐层传递,直至被适配的处理对象处理。该模式赋予每个处理对象自主决策能力,可选择处理请求或转发至下一级,无需硬编码请求与处理者的关联,显著提升代码灵活性、可扩展性与可维护性,是应对复杂请求处理场景的优选方案。本文从核心结构、多语言落地、优缺点剖析、适用场景及实战总结等维度,系统梳理责任链模式的设计思路与工程实践,为开发者提供可落地的技术参考。

一、责任链模式核心结构

责任链模式的核心的是拆分请求处理职责,实现请求有序流转与按需处理。通过明确三类核心角色的职责边界,确保请求规范传递、高效处理,各角色协同构成完整处理链路,具体定义如下:

1.1 抽象处理者(Handler)

抽象处理者是请求处理的统一契约,面向对象语言中通常以抽象类实现,无面向对象特性的语言(如纯C)则通过“结构体+函数指针”模拟。核心职责有二:一是声明处理请求的抽象方法,界定具体处理者的实现标准,保障接口一致性;二是维护下一个处理者的引用,为请求链式传递提供支撑,是链路串联的核心纽带。

1.2 具体处理者(ConcreteHandler)

具体处理者是抽象处理者的具象化实现,是请求处理的核心执行单元。它严格遵循抽象契约,实现具体处理逻辑:首先校验自身是否具备处理当前请求的权限(如金额范围、请求类型等);若具备则执行处理并终止传递,若不具备则转发至下一级处理者;若到达链路末端仍无法处理,则执行兜底逻辑(拒绝请求、记录日志等),避免请求丢失。

1.3 客户端(Client)

客户端核心职责是构建责任链(串联具体处理者),并将请求发送至链路起点。客户端无需关注请求处理流程、处理者数量及传递路径,仅与链路入口交互即可发起请求,真正实现“请求发送”与“请求处理”的完全解耦,契合“迪米特法则”与“单一职责原则”。

核心流转逻辑:客户端创建并串联处理者→向链路起点发送请求→当前处理者校验权限→具备权限则处理并终止;无权限则转发→重复流程,直至请求被处理或链尾兜底。

核心逻辑示意图(以企业费用审批为例):

1
2
3
4
5
客户端 → 小组长(≤1000元)→ 部门经理(≤5000元)→ 总监(≤10000元)→ 链尾(拒绝)
│ │ │ │
├─ 处理(≤1000) ├─ 处理(1001-5000)├─ 处理(5001-10000)├─ 兜底(>10000)
└─ 转发(>1000) └─ 转发(>5000) └─ 转发(>10000) └─ 终止传递

二、多语言实现责任链模式

以“企业费用审批”为统一实战场景(贴合真实业务,便于落地),设计三级审批链路:小组长处理≤1000元,部门经理处理≤5000元,总监处理≤10000元,超出则拒绝。以下提供C#、Python、Golang、C++及纯C的可运行实现,均贴合各语言原生特性,补充规范注释与边界校验,兼顾实用性与严谨性,清晰呈现模式适配思路。

2.1 C# 实现(面向对象规范实现)

C#作为强类型面向对象语言,通过抽象类定义抽象处理者,封装下一级处理者引用与设置方法,子类继承并实现审批逻辑,依托多态实现链式传递。代码结构清晰、可维护性高,适用于企业级业务系统,尤其适配复杂审批、拦截器链等场景。

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
using System;

/// <summary>
/// 抽象处理者:定义审批者统一契约,规范审批行为
/// </summary>
public abstract class Approver
{
/// <summary>下一个审批者,用于请求转发</summary>
protected Approver _nextApprover;

/// <summary>设置下一个审批者,构建责任链</summary>
/// <param name="nextApprover">符合Approver契约的审批者对象</param>
public void SetNext(Approver nextApprover)
{
_nextApprover = nextApprover;
}

/// <summary>抽象审批方法:子类必须实现具体逻辑</summary>
/// <param name="amount">申请费用(非负整数)</param>
public abstract void Approve(int amount);
}

/// <summary>具体处理者1:小组长(≤1000元)</summary>
public class GroupLeader : Approver
{
public override void Approve(int amount)
{
if (amount < 0)
{
Console.WriteLine("错误:申请金额不能为负数,请核对后重新提交!");
return;
}

if (amount <= 1000)
{
Console.WriteLine($"【小组长审批】{amount}元(≤1000元),审批通过。");
}
else if (_nextApprover != null)
{
Console.WriteLine($"【小组长审批】{amount}元超出权限(≤1000元),转发至部门经理。");
_nextApprover.Approve(amount);
}
else
{
Console.WriteLine($"【小组长审批】{amount}元超出权限,无后续审批者,拒绝。");
}
}
}

/// <summary>具体处理者2:部门经理(≤5000元)</summary>
public class DepartmentManager : Approver
{
public override void Approve(int amount)
{
if (amount < 0)
{
Console.WriteLine("错误:申请金额不能为负数,请核对后重新提交!");
return;
}

if (amount <= 5000)
{
Console.WriteLine($"【部门经理审批】{amount}元(≤5000元),审批通过。");
}
else if (_nextApprover != null)
{
Console.WriteLine($"【部门经理审批】{amount}元超出权限(≤5000元),转发至总监。");
_nextApprover.Approve(amount);
}
else
{
Console.WriteLine($"【部门经理审批】{amount}元超出权限,无后续审批者,拒绝。");
}
}
}

/// <summary>具体处理者3:总监(≤10000元)</summary>
public class Director : Approver
{
public override void Approve(int amount)
{
if (amount < 0)
{
Console.WriteLine("错误:申请金额不能为负数,请核对后重新提交!");
return;
}

if (amount <= 10000)
{
Console.WriteLine($"【总监审批】{amount}元(≤10000元),审批通过。");
}
else if (_nextApprover != null)
{
Console.WriteLine($"【总监审批】{amount}元超出权限(≤10000元),转发至下一级。");
_nextApprover.Approve(amount);
}
else
{
Console.WriteLine($"【总监审批】{amount}元超出最高权限,拒绝。");
}
}
}

// 客户端:构建链路并测试
class Program
{
static void Main(string[] args)
{
try
{
// 创建审批者并串联链路
Approver groupLeader = new GroupLeader();
Approver deptManager = new DepartmentManager();
Approver director = new Director();
groupLeader.SetNext(deptManager);
deptManager.SetNext(director);

// 测试不同场景
Console.WriteLine("=== 测试1:800元小额申请 ===");
groupLeader.Approve(800);

Console.WriteLine("\n=== 测试2:3000元中额申请 ===");
groupLeader.Approve(3000);

Console.WriteLine("\n=== 测试3:8000元大额申请 ===");
groupLeader.Approve(8000);

Console.WriteLine("\n=== 测试4:15000元超额申请 ===");
groupLeader.Approve(15000);

Console.WriteLine("\n=== 测试5:负数无效申请 ===");
groupLeader.Approve(-500);
}
catch (Exception ex)
{
Console.WriteLine($"审批异常:{ex.Message}");
}
}
}

2.2 Python 实现(动态语言简洁实现)

Python遵循“鸭子类型”,无需显式定义接口,通过抽象基类(ABC)约定处理者行为,语法简洁、无冗余类型声明。依托动态特性,可灵活调整链路结构,适用于快速开发、轻量级项目,同时通过异常处理与边界校验保障健壮性。

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
from abc import ABC, abstractmethod

class Approver(ABC):
"""抽象处理者:定义审批统一接口"""
def __init__(self):
self.next_approver = None # 下一级审批者

def set_next(self, approver) -> None:
"""设置下一级审批者,校验接口一致性"""
if not isinstance(approver, Approver):
raise TypeError("下一级审批者必须是Approver子类实例")
self.next_approver = approver

@abstractmethod
def approve(self, amount: int) -> None:
"""抽象审批方法,子类必须实现"""
pass

class GroupLeader(Approver):
"""具体处理者1:小组长(≤1000元)"""
def approve(self, amount: int) -> None:
if amount < 0:
print("错误:申请金额不能为负数,请核对后重新提交!")
return

if amount <= 1000:
print(f"【小组长审批】{amount}元(≤1000元),审批通过。")
elif self.next_approver:
print(f"【小组长审批】{amount}元超出权限,转发至部门经理。")
self.next_approver.approve(amount)
else:
print(f"【小组长审批】{amount}元超出权限,无后续审批者,拒绝。")

class DepartmentManager(Approver):
"""具体处理者2:部门经理(≤5000元)"""
def approve(self, amount: int) -> None:
if amount < 0:
print("错误:申请金额不能为负数,请核对后重新提交!")
return

if amount <= 5000:
print(f"【部门经理审批】{amount}元(≤5000元),审批通过。")
elif self.next_approver:
print(f"【部门经理审批】{amount}元超出权限,转发至总监。")
self.next_approver.approve(amount)
else:
print(f"【部门经理审批】{amount}元超出权限,无后续审批者,拒绝。")

class Director(Approver):
"""具体处理者3:总监(≤10000元)"""
def approve(self, amount: int) -> None:
if amount < 0:
print("错误:申请金额不能为负数,请核对后重新提交!")
return

if amount <= 10000:
print(f"【总监审批】{amount}元(≤10000元),审批通过。")
elif self.next_approver:
print(f"【总监审批】{amount}元超出权限,转发至下一级。")
self.next_approver.approve(amount)
else:
print(f"【总监审批】{amount}元超出最高权限,拒绝。")

# 客户端测试
if __name__ == "__main__":
try:
# 构建链路
group_leader = GroupLeader()
dept_manager = DepartmentManager()
director = Director()
group_leader.set_next(dept_manager)
dept_manager.set_next(director)

# 测试场景
print("=== 测试1:800元小额申请 ===")
group_leader.approve(800)

print("\n=== 测试2:3000元中额申请 ===")
group_leader.approve(3000)

print("\n=== 测试3:8000元大额申请 ===")
group_leader.approve(8000)

print("\n=== 测试4:15000元超额申请 ===")
group_leader.approve(15000)

print("\n=== 测试5:负数无效申请 ===")
group_leader.approve(-500)
except Exception as e:
print(f"审批异常:{str(e)}")

2.3 Golang 实现(接口至上轻量实现)

Go语言无类和继承,遵循“组合优于继承”原则,通过接口定义抽象处理者契约,结构体组合基础处理者(封装下一级引用)实现具体逻辑。依托接口隐式实现特性,代码极简高效,贴合Go语言设计理念,适配高并发后端场景(如API网关拦截链、审批服务)。

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package main

import "fmt"

// Approver 抽象处理者接口:定义审批方法签名
type Approver interface {
Next(approver Approver) // 设置下一级审批者
Approve(amount int) // 处理审批请求
}

// BaseApprover 基础处理者:封装下一级引用,复用SetNext方法
type BaseApprover struct {
xt Approver
}

// SetNext 实现链路串联逻辑,复用避免冗余
func (b *BaseApprover) SetNext(approver Approver) {
approver == nil {
intln("警告:下一级审批者不可为nil,避免链路断裂")
return
xt = approver
}

// GroupLeader 具体处理者1:小组长(≤1000元)
type GroupLeader struct {
aseApprover // 组合基础处理者,复用SetNext
}

// Approve 实现小组长审批逻辑
func (g *GroupLeader) Approve(amount int) {
amount < 0 {
ntln("错误:申请金额不能为负数,请核对后重新提交!")
return


amount <= 1000 {
mt.Printf("【小组长审批】%d元(≤1000元),审批通过。\n", amount)
} else if g.next != nil {
t.Printf("【小组长审批】%d元超出权限,转发至部门经理。\n", amount)
.next.Approve(amount)
} else {
t.Printf("【小组长审批】%d元超出权限,无后续审批者,拒绝。\n", amount)
}
}

// DepartmentManager 具体处理者2:部门经理(≤5000元)
type DepartmentManager struct {
Baver
}

// Approve 实现部门经理审批逻辑
func (d *DepartmentManager) Approve(amount int) {
amount < 0 {
mt.Println("错误:申请金额不能为负数,请核对后重新提交!")
turn

if amount <= 5000 {
Printf("【部门经理审批】%d元(≤5000元),审批通过。\n", amount)
else if d.next != nil {
.Printf("【部门经理审批】%d元超出权限,转发至总监。\n", amount)
ext.Approve(amount)
} else {
rintf("【部门经理审批】%d元超出权限,无后续审批者,拒绝。\n", amount)
}

// Director 具体处理者3:总监(≤10000元)
type Director struct {
seApprover
}

// Approve 实现总监审批逻辑
func (dr *Director) Approve(amount int) {
if amount fmt.Pr错误:申请金额不能为负数,请核对后重新提交!")
return
}

unt <= 10000 {
mt.Printf("【总监审批】%d元(≤10000元),审批通过。\n", amount)
lse if dr.next != nil {
Printf("【总监审批】%d元超出权限,转发至下一级。\n", amount)
ext.Approve(amount)
else {
fmt.Printf("【总监审批】%d元超出最高权限,拒绝。\n", amount)
}
}

// 客户端测试
func main() {
创建审批者并构建链路
grer := &GroupLeader{}
ptManager := &DepartmentManager{}
directDirector{}
oupLeader.SetNext(deptManager)
eptManager.SetNext(director)

// 测试场景
t.Println("=== 测试1800元小额申请 ===")
roupLeader.Approve(800)

ntln("\n=== 测试23000元中额申请 ===")
oupLeader.Approve(3000)

mt.Println("\n=== 测试38000元大额申请 ===")
roupLeader.Approve(8000)

.Println("\n=== 测试415000元超额申请 ===")
oupLeader.Approve(15000)

.Println("\n=== 测试5:负数无效申请 ===")
oupLeader.Approve(-500)
}
gr fmt gr fmt g f grfmt.Pri g fm d gror := & deoupLead // } dr.n fmt. } e f if amo intln("< 0 {
Ba }
fmt.P d.n fmt } fmt. }
re f ifseAppro fm g fm f if } fmt.Pri if B}
b.ne fmt.Pr if ne Set

2.4 C++ 实现(抽象类+多态经典实现)

C++通过抽象类(纯虚函数)定义抽象处理者接口,子类继承并实现具体逻辑,依托多态实现链式传递,是责任链模式的经典实现。需手动管理内存(虚析构函数避免泄漏),兼顾灵活性与高性能,适用于底层开发、高频调用场景(如嵌入式请求处理链)。

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#include <iostream>
using namespace std;

// 抽象处理者:纯虚函数定义审批接口
class Approver {
protected:
Approver* nextApprover; // 下一级审批者指针
public:
Approver() : nextApprover(nullptr) {}
virtual ~Approver() {} // 虚析构函数,避免多态内存泄漏

/// <summary>设置下一级审批者</summary>
void setNext(Approver* approver) {
nextApprover = approver;
}

/// <summary>抽象审批方法,子类必须实现</summary>
virtual void approve(int amount) = 0;
};

// 具体处理者1:小组长(≤1000元)
class GroupLeader : public Approver {
public:
void approve(int amount) override {
if (amount < 0) {
cout << "错误:申请金额不能为负数,请核对后重新提交!" << endl;
return;
}

if (amount <= 1000) {
cout << "【小组长审批】" << amount << "元(≤1000元),审批通过。" << endl;
} else if (nextApprover != nullptr) {
cout << "【小组长审批】" << amount << "元超出权限,转发至部门经理。" << endl;
nextApprover->approve(amount);
} else {
cout << "【小组长审批】" << amount << "元超出权限,无后续审批者,拒绝。" << endl;
}
}
};

// 具体处理者2:部门经理(≤5000元)
class DepartmentManager : public Approver {
public:
void approve(int amount) override {
if (amount < 0) {
cout << "错误:申请金额不能为负数,请核对后重新提交!" << endl;
return;
}

if (amount <= 5000) {
cout << "【部门经理审批】" << amount << "元(≤5000元),审批通过。" << endl;
} else if (nextApprover != nullptr) {
cout << "【部门经理审批】" << amount << "元超出权限,转发至总监。" << endl;
nextApprover->approve(amount);
} else {
cout << "【部门经理审批】" << amount << "元超出权限,无后续审批者,拒绝。" << endl;
}
}
};

// 具体处理者3:总监(≤10000元)
class Director : public Approver {
public:
void approve(int amount) override {
if (amount < 0) {
cout << "错误:申请金额不能为负数,请核对后重新提交!" << endl;
return;
}

if (amount <= 10000) {
cout << "【总监审批】" << amount << "元(≤10000元),审批通过。" << endl;
} else if (nextApprover != nullptr) {
cout << "【总监审批】" << amount << "元超出权限,转发至下一级。" << endl;
nextApprover->approve(amount);
} else {
cout << "【总监审批】" << amount << "元超出最高权限,拒绝。" << endl;
}
}
};

// 客户端测试
int main() {
try {
// 创建审批者并构建链路
Approver* groupLeader = new GroupLeader();
Approver* deptManager = new DepartmentManager();
Approver* director = new Director();
groupLeader->setNext(deptManager);
deptManager->setNext(director);

// 测试场景
cout << "=== 测试1:800元小额申请 ===" << endl;
groupLeader->approve(800);

cout << "\n=== 测试2:3000元中额申请 ===" << endl;
groupLeader->approve(3000);

cout << "\n=== 测试3:8000元大额申请 ===" << endl;
groupLeader->approve(8000);

cout << "\n=== 测试4:15000元超额申请 ===" << endl;
groupLeader->approve(15000);

cout << "\n=== 测试5:负数无效申请 ===" << endl;
groupLeader->approve(-500);

// 释放内存,避免泄漏
delete groupLeader;
delete deptManager;
delete director;
groupLeader = nullptr;
deptManager = nullptr;
director = nullptr;
} catch (const exception& e) {
cout << "审批异常:" << e.what() << endl;
}
return 0;
}

2.5 纯C语言实现(结构体+函数指针模拟实现)

纯C无面向对象特性,通过“结构体封装数据+函数指针封装行为”模拟责任链核心逻辑。结构体封装处理者属性与方法指针,函数指针模拟抽象接口,底层可控性强,无额外依赖,适用于嵌入式、底层开发等资源受限场景,完整还原模式核心思想。

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#include <stdio.h>
#include <stdlib.h>

// 前向声明:解决结构体与函数指针交叉引用
typedef struct Approver Approver;

// 处理者结构体:模拟“属性+方法”
struct Approver {
void (*set_next)(Approver* self, Approver* next); // 设置下一级
void (*approve)(Approver* self, int amount); // 审批逻辑
Approver* next; // 下一级指针
};

/// <summary>基础set_next方法,复用避免冗余</summary>
void base_set_next(Approver* self, Approver* next) {
if (self == NULL) {
fprintf(stderr, "警告:当前处理者不可为NULL\n");
return;
}
self->next = next;
}

// 具体处理者1:小组长(≤1000元)
typedef struct {
Approver base; // 组合基础处理者,模拟“继承”
} GroupLeader;

/// <summary>小组长审批逻辑</summary>
void group_leader_approve(Approver* self, int amount) {
if (self == NULL) {
fprintf(stderr, "错误:处理者不可为NULL\n");
return;
}

if (amount < 0) {
printf("错误:申请金额不能为负数,请核对后重新提交!\n");
return;
}

if (amount <= 1000) {
printf("【小组长审批】%d元(≤1000元),审批通过。\n", amount);
} else if (self->next != NULL) {
printf("【小组长审批】%d元超出权限,转发至部门经理。\n", amount);
self->next->approve(self->next, amount);
} else {
printf("【小组长审批】%d元超出权限,无后续审批者,拒绝。\n", amount);
}
}

/// <summary>初始化小组长,分配内存并绑定方法</summary>
GroupLeader* create_group_leader() {
GroupLeader* gl = (GroupLeader*)malloc(sizeof(GroupLeader));
if (gl == NULL) {
fprintf(stderr, "错误:内存分配失败\n");
return NULL;
}
gl->base.set_next = base_set_next;
gl->base.approve = group_leader_approve;
gl->base.next = NULL;
return gl;
}

// 具体处理者2:部门经理(≤5000元)
typedef struct {
Approver base;
} DepartmentManager;

/// <summary>部门经理审批逻辑</summary>
void dept_manager_approve(Approver* self, int amount) {
if (self == NULL) {
fprintf(stderr, "错误:处理者不可为NULL\n");
return;
}

if (amount < 0) {
printf("错误:申请金额不能为负数,请核对后重新提交!\n");
return;
}

if (amount <= 5000) {
printf("【部门经理审批】%d元(≤5000元),审批通过。\n", amount);
} else if (self->next != NULL) {
printf("【部门经理审批】%d元超出权限,转发至总监。\n", amount);
self->next->approve(self->next, amount);
} else {
printf("【部门经理审批】%d元超出权限,无后续审批者,拒绝。\n", amount);
}
}

/// <summary>初始化部门经理</summary>
DepartmentManager* create_dept_manager() {
DepartmentManager* dm = (DepartmentManager*)malloc(sizeof(DepartmentManager));
if (dm == NULL) {
fprintf(stderr, "错误:内存分配失败\n");
return NULL;
}
dm->base.set_next = base_set_next;
dm->base.approve = dept_manager_approve;
dm->base.next = NULL;
return dm;
}

// 具体处理者3:总监(≤10000元)
typedef struct {
Approver base;
} Director;

/// <summary>总监审批逻辑</summary>
void director_approve(Approver* self, int amount) {
if (self == NULL) {
fprintf(stderr, "错误:处理者不可为NULL\n");
return;
}

if (amount < 0) {
printf("错误:申请金额不能为负数,请核对后重新提交!\n");
return;
}

if (amount <= 10000) {
printf("【总监审批】%d元(≤10000元),审批通过。\n", amount);
} else if (self->next != NULL) {
printf("【总监审批】%d元超出权限,转发至下一级。\n", amount);
self->next->approve(self->next, amount);
} else {
printf("【总监审批】%d元超出最高权限,拒绝。\n", amount);
}
}

/// <summary>初始化总监</summary>
Director* create_director() {
Director* dr = (Director*)malloc(sizeof(Director));
if (dr == NULL) {
fprintf(stderr, "错误:内存分配失败\n");
return NULL;
}
dr->base.set_next = base_set_next;
dr->base.approve = director_approve;
dr->base.next = NULL;
return dr;
}

// 客户端测试
int main() {
// 创建审批者
GroupLeader* groupLeader = create_group_leader();
DepartmentManager* deptManager = create_dept_manager();
Director* director = create_director();

if (groupLeader == NULL || deptManager == NULL || director == NULL) {
fprintf(stderr, "错误:审批者创建失败,程序退出\n");
return -1;
}

// 构建链路
groupLeader->base.set_next((Approver*)groupLeader, (Approver*)deptManager);
deptManager->base.set_next((Approver*)deptManager, (Approver*)director);

// 测试场景
printf("=== 测试1:800元小额申请 ===\n");
groupLeader->base.approve((Approver*)groupLeader, 800);

printf("\n=== 测试2:3000元中额申请 ===\n");
groupLeader->base.approve((Approver*)groupLeader, 3000);

printf("\n=== 测试3:8000元大额申请 ===\n");
groupLeader->base.approve((Approver*)groupLeader, 8000);

printf("\n=== 测试4:15000元超额申请 ===\n");
groupLeader->base.approve((Approver*)groupLeader, 15000);

printf("\n=== 测试5:负数无效申请 ===\n");
groupLeader->base.approve((Approver*)groupLeader, -500);

// 释放内存
free(groupLeader);
free(deptManager);
free(director);
groupLeader = NULL;
deptManager = NULL;
director = NULL;

return 0;
}

三、责任链模式的优缺点

责任链模式的核心价值的是解耦请求发送与处理,实现灵活扩展,但也存在性能与调试等方面的局限。实际开发中需结合链路长度、处理复杂度和性能要求权衡使用,平衡设计优雅与工程落地。

3.1 核心优点

  • 解耦请求传递,降低系统耦合:客户端仅需发送请求至链路起点,无需知晓处理者、顺序及逻辑,实现“发送”与“处理”完全解耦,契合“迪米特法则”,提升可维护性。

  • 灵活扩展,符合开闭原则:增删处理者仅需调整链路结构,无需修改原有代码。如新增审批级别时,仅需新增处理者并调整串联关系,降低扩展成本。

  • 动态调整链路,适配多场景:运行时可修改处理者顺序、增删节点,适配差异化需求。如普通员工与管理层审批链路不同,通过动态调整即可实现。

  • 职责单一,逻辑清晰:每个处理者仅负责自身权限内的请求,代码逻辑简洁,便于调试、测试与维护,降低迭代成本。

  • 简化客户端逻辑:避免客户端大量嵌套条件判断,仅需触发请求,减少冗余代码,提升可读性。

3.2 主要缺点

  • 请求兜底风险:若未设置链尾兜底处理者,请求可能遍历链路后无人处理,需额外设计兜底逻辑(如拒绝、日志),增加开发成本。

  • 性能损耗:链路过长会增加请求传递开销,高频场景下影响响应速度,需通过合并处理者、缩短链路优化。

  • 调试难度高:请求处理路径分散在多个处理者中,排查问题需逐一遍历链路,增加调试成本,可通过日志、链路追踪工具优化。

  • 简单场景过度设计:仅1-2个处理者、无扩展需求的场景,使用责任链会增加复杂度,直接调用处理者更高效。

  • 依赖管理复杂:处理者间存在依赖时,需额外引入上下文传递处理结果,增加实现复杂度。

四、责任链模式的使用场景

责任链模式适用于“请求需多个对象依次处理,且处理者可动态调整”的场景。以下结合具体场景与实战案例,帮助开发者精准判断适用性,避免滥用。

4.1 核心适用场景

  • 多级审批流程:企业费用、请假、订单、合同审批等,请求按级别传递,每个级别处理对应权限请求,是最典型的应用场景。

  • 过滤器/拦截器链:Web框架拦截器(Spring Interceptor、Gin中间件)、日志过滤器、数据校验过滤器,请求经多个环节依次处理,可动态增删。

  • 异常处理链:业务异常、系统异常、网络异常等分级处理,异常沿链路传递,直至找到匹配处理器,提升系统健壮性。

  • 事件分发机制:GUI组件事件传递、事件总线多级处理,请求沿链路按需处理。

  • API网关请求处理:限流、鉴权、日志、转发等环节按链路执行,可动态调整处理环节,适配不同服务需求。

4.2 典型实战案例

  • 企业费用审批系统:请求按“小组长→部门经理→总监→财务”传递,新增审批级别或调整权限时,仅修改链路结构,适配组织架构调整。

  • Web框架拦截器链:HTTP请求依次经过“跨域拦截→鉴权拦截→参数校验→日志记录→请求转发”,各环节独立,可灵活增删,提升系统扩展性。

五、总结

责任链模式通过“链式传递”将请求处理逻辑分散到独立处理者中,完美契合“开闭原则”与“单一职责原则”。无论是面向对象语言(C#、Python、Golang、C++)还是面向过程语言(纯C),均可通过各自特性实现核心逻辑——差异仅在实现形式(类/接口/结构体+函数指针),核心思想一致。

实际开发中,需根据场景合理取舍:复杂请求处理流程中,责任链可显著提升代码可维护性与扩展性;简单场景则需避免过度设计。合理运用责任链模式,能让代码结构更清晰、扩展更灵活,是构建高内聚、低耦合系统的重要工具。

策略模式(Strategy Pattern)是一种经典的行为型设计模式,其核心思想是“封装变化、解耦算法”——将一组可替换的算法(策略)封装为独立的组件,使算法能够独立于使用它的客户端灵活切换,彻底摒弃代码中冗余的条件判断语句,大幅提升代码的可扩展性、可维护性与复用性。它不仅是“开闭原则”的典型实践,更是应对复杂业务场景中“算法动态切换”需求的最优解决方案之一。本文将从核心结构、多语言落地实现、优缺点剖析、适用场景及实战总结等维度,全面拆解策略模式的设计思路与工程实践。

一、策略模式核心结构

策略模式的设计核心是“解耦算法的定义与使用”,通过明确的角色分工,实现策略的灵活替换与独立扩展。其核心角色分为三类,各角色职责清晰、协同工作,构成完整的策略驱动体系,具体如下:

1.1 环境类(Context)

作为策略的“调度中心”与“客户端交互入口”,环境类维护一个抽象策略对象的引用,对外提供统一的调用接口,负责将客户端的请求委派给当前的具体策略执行。它不直接实现任何算法逻辑,也不关心具体策略的内部实现,仅负责策略的切换与调用,是连接客户端与策略的桥梁。

1.2 抽象策略类(Strategy)

定义所有具体策略的公共行为契约,通常以接口(面向对象语言)或函数指针(无面向对象特性语言)的形式存在。它声明了所有具体策略必须实现的核心方法(如算法执行方法),规范了策略的统一调用标准,确保环境类能通过统一接口调用不同的策略,实现“多策略统一适配”。

1.3 具体策略类(ConcreteStrategy)

抽象策略的具体实现,是算法逻辑的核心载体。它严格遵循抽象策略定义的接口,封装了某一种特定的算法逻辑,是策略模式的核心扩展点。新增算法时,只需新增对应的具体策略类,无需修改原有代码,完美契合“开闭原则”。

核心流转逻辑:客户端初始化环境类 → 环境类设置具体策略 → 客户端通过环境类调用策略方法 → 环境类委派请求给当前策略 → 具体策略执行算法逻辑 → 如需切换策略,客户端通过环境类更换具体策略,无需修改其他代码。

二、多语言实现策略模式

为适配不同技术栈开发者的落地需求,本文以“二元运算策略”为统一案例(通过加法、乘法两种可切换策略,直观呈现策略模式的核心逻辑),分别提供C#、Python、Golang、C++及纯C语言的可运行实现。各实现均贴合语言原生特性,补充规范注释、异常处理与边界校验,兼顾实用性与严谨性,清晰展现策略模式在不同语言中的适配思路与最佳实践。

2.1 C# 实现(面向对象规范实现)

C# 作为强类型面向对象语言,通过接口严格定义抽象策略契约,依托类的继承与多态实现具体策略逻辑,环境类通过属性封装策略引用,确保策略管理的安全性与规范性,适配企业级业务系统开发,尤其适合需要严格接口约束的场景。

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
98
using System;

/// <summary>
/// 抽象策略接口:定义二元运算策略的统一契约
/// </summary>
public interface IStrategy
{
/// <summary>
/// 执行二元运算
/// </summary>
/// <param name="a">运算数A</param>
/// <param name="b">运算数B</param>
/// <returns>运算结果</returns>
int Calculate(int a, int b);
}

/// <summary>
/// 具体策略1:加法策略
/// </summary>
public class AddStrategy : IStrategy
{
public int Calculate(int a, int b)
{
return a + b;
}
}

/// <summary>
/// 具体策略2:乘法策略
/// </summary>
public class MultiplyStrategy : IStrategy
{
public int Calculate(int a, int b)
{
return a * b;
}
}

/// <summary>
/// 环境类:管理策略引用,委派策略执行
/// </summary>
public class Context
{
// 私有字段存储当前策略,避免外部直接修改
private IStrategy _currentStrategy;

/// <summary>
/// 动态切换策略
/// </summary>
/// <param name="strategy">新的策略对象</param>
/// <exception cref="ArgumentNullException">策略对象不可为null</exception>
public void SetStrategy(IStrategy strategy)
{
_currentStrategy = strategy ?? throw new ArgumentNullException(nameof(strategy), "策略对象不可为null");
}

/// <summary>
/// 执行当前策略的运算逻辑
/// </summary>
/// <param name="a">运算数A</param>
/// <param name="b">运算数B</param>
/// <returns>运算结果</returns>
/// <exception cref="InvalidOperationException">未设置策略时抛出异常</exception>
public int ExecuteStrategy(int a, int b)
{
if (_currentStrategy == null)
{
throw new InvalidOperationException("请先设置具体策略");
}
return _currentStrategy.Calculate(a, b);
}
}

// 客户端调用示例
class Program
{
static void Main(string[] args)
{
try
{
// 初始化环境类
Context context = new Context();

// 切换为加法策略并执行
context.SetStrategy(new AddStrategy());
Console.WriteLine($"5 + 3 = {context.ExecuteStrategy(5, 3)}"); // 输出:5 + 3 = 8

// 动态切换为乘法策略并执行
context.SetStrategy(new MultiplyStrategy());
Console.WriteLine($"5 × 3 = {context.ExecuteStrategy(5, 3)}"); // 输出:5 × 3 = 15
}
catch (Exception ex)
{
Console.WriteLine($"执行异常:{ex.Message}");
}
}
}

2.2 Python 实现(动态语言简洁实现)

Python 遵循“鸭子类型”,无需显式定义接口,可通过抽象基类(ABC)约定策略行为,语法简洁、无繁琐类型声明。依托动态类型特性,环境类可灵活切换策略对象,适配快速开发、脚本开发及轻量级项目,同时通过异常处理保证代码健壮性,完美保留策略模式的核心逻辑。

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
from abc import ABC, abstractmethod

class Strategy(ABC):
"""抽象策略基类:约定二元运算的统一接口"""
@abstractmethod
def calculate(self, a: int, b: int) -> int:
"""
执行二元运算
:param a: 运算数A
:param b: 运算数B
:return: 运算结果
"""
pass

class AddStrategy(Strategy):
"""具体策略1:加法策略"""
def calculate(self, a: int, b: int) -> int:
return a + b

class MultiplyStrategy(Strategy):
"""具体策略2:乘法策略"""
def calculate(self, a: int, b: int) -> int:
return a * b

class Context:
"""环境类:管理策略,委派策略执行"""
def __init__(self):
self._strategy = None # 初始无策略

def set_strategy(self, strategy: Strategy) -> None:
"""
动态切换策略
:param strategy: 具体策略对象
:raises TypeError: 策略对象必须是Strategy的子类实例
"""
if not isinstance(strategy, Strategy):
raise TypeError("策略对象必须是Strategy的子类实例")
self._strategy = strategy

def execute_strategy(self, a: int, b: int) -> int:
"""
执行当前策略的运算逻辑
:param a: 运算数A
:param b: 运算数B
:return: 运算结果
:raises ValueError: 未设置策略时抛出异常
"""
if self._strategy is None:
raise ValueError("请先通过set_strategy方法设置具体策略")
return self._strategy.calculate(a, b)

# 客户端调用示例
if __name__ == "__main__":
try:
context = Context()

# 切换为加法策略
context.set_strategy(AddStrategy())
print(f"5 + 3 = {context.execute_strategy(5, 3)}") # 输出:5 + 3 = 8

# 动态切换为乘法策略
context.set_strategy(MultiplyStrategy())
print(f"5 × 3 = {context.execute_strategy(5, 3)}") # 输出:5 × 3 = 15
except Exception as e:
print(f"执行异常:{str(e)}")

2.3 Golang 实现(接口至上轻量实现)

Go 语言无类和继承,核心遵循“组合优于继承”原则,通过接口定义抽象策略契约,结构体实现具体策略逻辑,代码极简高效。依托接口的隐式实现特性,无需显式声明继承关系,贴合Go语言“简洁、务实、高性能”的设计理念,适配高并发后端开发场景,尤其适合对性能有一定要求的策略切换场景。

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
package main

import "fmt"

// Strategy 抽象策略接口:定义二元运算的统一方法签名
type Strategy interface {
alculate(a, b int) int
}

// AddStrategy 具体策略1:加法策略
type AddStrategy struct{}

// Calculate 实现加法运算逻辑
func (s *AddStrategy) Calculate(a, b int) int {
eturn a + b
}

// MultiplyStrategy 具体策略2:乘法策略
type MultiplyStrategy struct{}

// Calculate 实现乘法运算逻辑
func (s *MultiplyStrategy) Calculate(a, b int) int {
eturn a * b
}

// Context 环境类:管理策略引用,委派策略执行
type Context struct {
egy Strategy // 当前策略引用
}

// SetStrategy 动态切换策略
func (c *Context) SetStrategy(strategy Strategy) {
trategy == nil {
t.Println("警告:策略对象不可为nil")
rn
}
y = strategy
}

// ExecuteStrategy 执行当前策略的运算逻辑
func (c *Context) ExecuteStrategy(a, b int) (int, error) {
c.strategy == nil {
, fmt.Errorf("请先设置具体策略")

return c.strateglate(a, b), nil
}

// 客户端调用示例
func main() {
始化环境类
context := &Context{}

切换为加法策略并执行
ontext.SetStrategy(&AddStrategy{})
esult, err := context.ExecuteStrategy(5, 3)
f err != nil {
.Printf("执行异常:%v\n", err)
else {
f 3 = %d\n", result) // 输出:5 + 3 = 8
动态切换为乘法策略并执行
context.Segy(&MultiplyStrategy{})
, err = context.ExecuteStrategy(5, 3)
rr != nil {
mt.Printf("执行异常:%v\n", err)
} elfmt.Printf("5 × 3 = %d\n", result) // 输出:5 × 3 = 15
}
}
se {
f if e resulttStrate // }

mt.Printf("5 + } fmt i r c // // 初y.Calcu } return 0 if c.strateg retu fm if s strat r r C

2.4 C++ 实现(抽象类+多态经典实现)

C++ 作为经典面向对象语言,通过抽象类(纯虚函数)定义抽象策略接口,子类继承并实现具体策略逻辑,依托多态特性实现策略的灵活切换。需手动管理内存(通过虚析构函数避免内存泄漏),兼顾灵活性与高性能,适配底层开发、高频调用场景,是底层系统、高性能应用的优选实现方式。

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
#include <iostream>
using namespace std;

// 抽象策略类:纯虚函数定义二元运算接口
class Strategy {
public:
virtual ~Strategy() = default; // 虚析构函数,避免多态场景内存泄漏
/// <summary>
/// 执行二元运算
/// </summary>
/// <param name="a">运算数A</param>
/// <param name="b">运算数B</param>
/// <returns>运算结果</returns>
virtual int Calculate(int a, int b) = 0;
};

// 具体策略1:加法策略
class AddStrategy : public Strategy {
public:
int Calculate(int a, int b) override {
return a + b;
}
};

// 具体策略2:乘法策略
class MultiplyStrategy : public Strategy {
public:
int Calculate(int a, int b) override {
return a * b;
}
};

// 环境类:管理策略指针,委派策略执行
class Context {
private:
Strategy* _currentStrategy; // 当前策略指针
public:
// 构造函数:初始化策略指针为nullptr
Context() : _currentStrategy(nullptr) {}

// 析构函数:释放策略对象内存
~Context() {
if (_currentStrategy != nullptr) {
delete _currentStrategy;
_currentStrategy = nullptr;
}
}

/// <summary>
/// 动态切换策略
/// </summary>
/// <param name="strategy">新的策略对象指针</param>
void SetStrategy(Strategy* strategy) {
// 释放旧策略内存
if (_currentStrategy != nullptr) {
delete _currentStrategy;
}
_currentStrategy = strategy;
}

/// <summary>
/// 执行当前策略的运算逻辑
/// </summary>
/// <param name="a">运算数A</param>
/// <param name="b">运算数B</param>
/// <returns>运算结果</returns>
/// <exception cref="runtime_error">未设置策略时抛出异常</exception>
int ExecuteStrategy(int a, int b) {
if (_currentStrategy == nullptr) {
throw runtime_error("请先设置具体策略");
}
return _currentStrategy->Calculate(a, b);
}
};

// 客户端调用示例
int main() {
try {
Context context;

// 切换为加法策略并执行
context.SetStrategy(new AddStrategy());
cout << "5 + 3 = " << context.ExecuteStrategy(5, 3) << endl; // 输出:5 + 3 = 8

// 动态切换为乘法策略并执行
context.SetStrategy(new MultiplyStrategy());
cout << "5 × 3 = " << context.ExecuteStrategy(5, 3) << endl; // 输出:5 × 3 = 15
}
catch (const exception& e) {
cout << "执行异常:" << e.what() << endl;
}
return 0;
}

2.5 纯C语言实现(结构体+函数指针模拟实现)

纯C语言无面向对象特性,无类和多态,通过“结构体封装数据+函数指针封装行为”,手动模拟策略模式的核心逻辑。用结构体封装环境类的属性(策略函数指针),用函数指针模拟抽象策略的接口方法,需手动管理内存,代码虽略显冗余,但底层可控性强,适配嵌入式、底层开发等资源受限场景,完美还原“策略封装、动态切换”的核心思想。

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
#include <stdio.h>
#include <stdlib.h>

// 定义策略函数指针类型(模拟抽象策略接口)
// 参数:两个运算数a、b,返回值:运算结果
typedef int (*StrategyFunc)(int a, int b);

// 环境结构体:封装当前策略函数指针
typedef struct {
StrategyFunc strategy; // 当前策略的函数指针
} Context;

/// <summary>
/// 初始化环境类
/// </summary>
/// <param name="context">环境结构体指针</param>
void Context_Init(Context* context) {
if (context == NULL) {
fprintf(stderr, "警告:环境对象不可为NULL\n");
return;
}
context->strategy = NULL; // 初始无策略
}

/// <summary>
/// 动态切换策略
/// </summary>
/// <param name="context">环境结构体指针</param>
/// <param name="strategy">策略函数指针</param>
void Context_SetStrategy(Context* context, StrategyFunc strategy) {
if (context == NULL) {
fprintf(stderr, "警告:环境对象不可为NULL\n");
return;
}
context->strategy = strategy;
}

/// <summary>
/// 执行当前策略的运算逻辑
/// </summary>
/// <param name="context">环境结构体指针</param>
/// <param name="a">运算数A</param>
/// <param name="b">运算数B</param>
/// <returns>运算结果(异常时返回-1)</returns>
int Context_ExecuteStrategy(Context* context, int a, int b) {
if (context == NULL) {
fprintf(stderr, "错误:环境对象不可为NULL\n");
return -1;
}
if (context->strategy == NULL) {
fprintf(stderr, "错误:请先设置具体策略\n");
return -1;
}
// 调用当前策略的函数指针,执行运算
return context->strategy(a, b);
}

// 具体策略1:加法策略函数
int AddStrategy(int a, int b) {
return a + b;
}

// 具体策略2:乘法策略函数
int MultiplyStrategy(int a, int b) {
return a * b;
}

// 客户端调用示例
int main() {
Context context;
// 初始化环境
Context_Init(&context);

// 切换为加法策略并执行
Context_SetStrategy(&context, AddStrategy);
int result = Context_ExecuteStrategy(&context, 5, 3);
if (result != -1) {
printf("5 + 3 = %d\n", result); // 输出:5 + 3 = 8
}

// 动态切换为乘法策略并执行
Context_SetStrategy(&context, MultiplyStrategy);
result = Context_ExecuteStrategy(&context, 5, 3);
if (result != -1) {
printf("5 × 3 = %d\n", result); // 输出:5 × 3 = 15
}

return 0;
}

三、策略模式的优缺点

策略模式的核心价值是“解耦算法与客户端,实现算法的灵活切换与独立扩展”,其优缺点均围绕这一核心展开。在实际开发中,需结合业务场景的策略数量、切换频率和性能要求,权衡使用,避免过度设计或滥用,确保既发挥其核心优势,又规避潜在问题。

3.1 核心优点

  • 解耦算法与使用方,提升可维护性:策略的实现与调用完全分离,客户端无需关注算法的内部细节,只需通过环境类调用策略。修改某一策略的逻辑时,无需改动客户端和其他策略代码,降低维护成本,也减少了代码耦合带来的风险。

  • 灵活扩展,符合开闭原则:新增策略时,只需实现抽象策略接口(或定义策略函数),无需修改原有代码,即可通过环境类切换使用新策略。例如支付系统新增“银联支付”策略,仅需新增UnionPayStrategy类,无需改动支付核心逻辑。

  • 消除冗余条件判断,提升代码可读性:彻底替代大量嵌套的if/elseswitch语句,将分散的算法逻辑封装为独立策略,让代码结构更清晰,逻辑更易理解和调试。例如排序算法的选择,若用条件判断需区分冒泡、快排、归并等分支,而策略模式可将每种排序封装为独立策略,代码更简洁。

  • 策略复用性强,减少代码冗余:不同的客户端(或场景)可共享同一个策略对象,相同的算法逻辑无需重复编写。例如电商系统的“满减优惠”策略,可在PC端、移动端、小程序等多个场景中复用。

  • 便于单元测试:每个策略独立封装,可单独编写单元测试用例,无需依赖客户端和其他策略,提升测试效率和代码健壮性。

3.2 主要缺点

  • 类/结构体数量增加,存在“类爆炸”风险:每新增一个策略,就需要新增一个具体策略类(或策略函数),当策略数量较多时(如10个以上),会导致类/函数数量激增,增加代码量和维护成本。

  • 客户端需了解所有策略,增加使用成本:客户端必须清楚所有策略的差异和适用场景,才能选择合适的策略,这增加了客户端的使用复杂度。例如支付系统,客户端需知道支付宝、微信、银联等策略的区别,才能根据用户选择切换策略。

  • 轻微性能损耗:部分语言(如C++/C#)中,策略的切换可能涉及对象的创建与销毁,多态调用也会带来轻微的性能开销(可通过策略单例、对象池化等方式优化)。

  • 策略间无依赖管理:若策略之间存在依赖关系(如某策略需基于另一策略的结果执行),策略模式本身无法很好地管理这种依赖,需额外引入其他设计模式(如装饰器模式)辅助实现。

四、策略模式的使用场景

策略模式的核心适用场景是“算法需要动态切换、且算法数量较多、需独立扩展”的业务场景。以下结合具体场景及典型实战案例,帮助开发者快速判断是否适用,实现精准落地,避免滥用或错用。

4.1 核心适用场景

  • 算法需动态切换:客户端需根据不同条件,在运行时动态选择不同的算法。典型案例:支付系统(支付宝、微信、银行卡、银联支付)、排序算法(冒泡、快排、归并、插入排序)、日志存储策略(本地文件、数据库、云存储、消息队列)、电商优惠计算(满减、折扣、优惠券、积分抵扣)。

  • 避免大量条件判断:代码中出现大量与算法选择相关的条件分支,且分支逻辑频繁修改、扩展。例如某风控系统,根据用户等级、交易金额、风险评分选择不同的风控策略,若用条件判断会导致代码臃肿,适合用策略模式重构。

  • 算法需独立扩展与维护:算法的实现可能频繁变更,且需要不影响原有调用逻辑。例如电商平台的推荐算法,需根据用户行为、商品热度等因素动态调整推荐策略,新增或修改推荐算法时,无需改动推荐核心逻辑。

  • 多算法复用场景:多个客户端(或场景)需要共享不同的算法,且算法逻辑相对独立。例如办公软件的文件导出策略(PDF、Excel、Word、TXT),不同模块可共享同一套导出策略。

4.2 典型实战案例

  • 支付系统的支付策略:电商平台的支付模块,支持支付宝、微信、银行卡、银联等多种支付方式,每种支付方式的流程(下单、支付、回调)不同,通过策略模式将每种支付方式封装为独立策略,客户端可根据用户选择动态切换支付策略,新增支付方式时无需修改原有支付逻辑。

  • 排序算法的动态选择:数据处理系统中,根据数据量大小、数据类型(有序/无序)选择不同的排序算法(小数据量用冒泡排序,大数据量用快排,有序数据用插入排序),通过策略模式封装每种排序算法,环境类根据数据条件动态切换策略,提升排序效率。

  • 电商优惠计算系统:电商平台的订单结算模块,支持满减、折扣、优惠券、积分抵扣等多种优惠方式,每种优惠的计算逻辑不同,通过策略模式将每种优惠封装为独立策略,根据订单金额、用户等级等条件动态选择优惠策略,新增优惠方式时无需修改结算核心逻辑。

  • 日志存储策略:系统日志需根据不同环境(开发、测试、生产)选择不同的存储方式(开发环境存储本地文件,测试环境存储数据库,生产环境存储云存储),通过策略模式封装每种存储策略,环境类根据配置动态切换存储方式,提升日志存储的灵活性。

4.3 避坑场景

  • 策略数量极少且无扩展计划:若仅存在2-3个策略,且后续无新增策略的需求,用简单的条件判断即可实现,无需引入策略模式,避免过度设计。例如仅支持“加法、减法”两种运算的工具类,无需封装为策略模式。

  • 算法逻辑简单且无变化:若算法逻辑简单,且长期无需修改,无需封装为独立策略,直接在客户端实现即可,减少代码冗余。例如简单的数值转换、字符串拼接等逻辑,无需使用策略模式。

  • 客户端无需动态切换策略:若策略一旦确定,运行时无需切换,且仅在单一场景使用,无需引入策略模式。例如某系统固定使用快排算法,无需支持其他排序方式,直接实现快排逻辑即可。

  • 极致性能敏感的底层场景:如嵌入式实时系统、高频交易系统,策略模式的多态/函数指针调用会带来轻微性能开销,可能影响系统响应速度,需谨慎使用,可考虑用条件判断替代。

五、策略模式的扩展与实战技巧

在实际工程实践中,单纯的策略模式可能无法满足复杂场景的需求,需结合其他设计模式或优化技巧,解决策略模式的固有缺陷(如类爆炸、客户端需了解所有策略),提升系统的灵活性和可维护性。

5.1 策略工厂模式(解决客户端选择策略的复杂度)

为解决“客户端需了解所有策略”的问题,可结合工厂模式,创建策略工厂类(StrategyFactory),由工厂类根据条件(如配置、参数)自动创建并返回具体策略对象,客户端无需直接实例化策略,只需通过工厂类获取策略,降低客户端的使用复杂度。例如支付系统中,客户端只需传入“支付方式编码”,工厂类即可返回对应的支付策略对象。

5.2 策略单例化(优化性能,减少对象创建开销)

具体策略类通常无状态数据(算法逻辑固定),可将其设计为单例,减少对象创建与销毁的开销。例如C#中通过静态只读实例、Python中通过__new__方法、Go中通过全局变量、C++中通过静态成员函数,实现策略对象的单例化,避免频繁创建策略对象,提升系统性能。

5.3 与其他设计模式结合使用

  • 与工厂模式结合:如上述“策略工厂”,由工厂负责策略的创建与管理,客户端通过工厂获取策略,降低客户端与具体策略的耦合,同时简化策略选择逻辑。

  • 与装饰器模式结合:当策略需要新增额外功能(如日志记录、权限校验)时,可通过装饰器模式为策略动态添加功能,无需修改原有策略代码,实现功能的灵活扩展。例如为支付策略添加“支付日志记录”装饰器,无需修改支付策略本身。

  • 与模板方法模式结合:当多个策略存在共同的逻辑(如前置校验、后置处理)时,可通过模板方法模式将共同逻辑封装到抽象策略基类中,具体策略只需实现差异化的核心逻辑,减少代码冗余。

  • 与观察者模式结合:当策略执行完成后,需要通知其他组件(如更新UI、发送消息),可结合观察者模式,让策略作为被观察者,执行完成后通知所有观察者,实现策略与其他组件的解耦。

5.4 多语言实现的注意事项

  • **C#**:通过接口定义抽象策略,用抽象类封装通用逻辑(如日志记录、参数校验),减少重复代码;利用依赖注入框架(如Autofac、Unity)管理策略对象,简化策略的创建与切换;新增策略时,确保接口实现的一致性,可通过代码检查工具校验。

  • Python:无需严格遵循“接口-实现”结构,可通过鸭子类型简化设计;利用装饰器封装通用逻辑(如策略执行日志),提升代码复用性;注意策略对象的内存管理,避免循环引用;可通过字典映射策略标识与策略对象,简化策略选择逻辑。

  • Golang:依托接口的隐式实现特性,简化状态类定义;通过结构体组合复用通用策略逻辑;利用sync.Once实现策略单例,减少对象创建开销;可通过map存储策略标识与策略对象,实现策略的快速查找与切换。

  • **C++**:抽象策略类的析构函数必须声明为虚函数,避免多态场景下的内存泄漏;可通过智能指针(如unique_ptr)管理策略对象,简化内存管理;利用单例模式优化策略对象的创建,提升性能;注意纯虚函数的实现,确保所有具体策略类都实现了抽象接口的方法。

  • 纯C:函数指针需保证类型一致性,避免野指针;通过结构体封装策略相关数据,提升代码可读性;注意内存分配与释放的配对,避免内存泄漏;可通过宏定义或函数指针数组,简化策略的注册与切换逻辑。

六、总结

策略模式的核心精髓是“封装变化、解耦算法”,其本质是通过将易变的算法逻辑抽离为独立的策略组件,实现算法与客户端的解耦,让算法能够灵活切换、独立扩展。它不仅是一种代码组织方式,更是一种“面向变化”的设计思维——面对频繁变更的算法逻辑,不通过修改原有代码实现,而是通过新增策略组件完成扩展,完美契合“开闭原则”。

从多语言实现来看,不同语言的实现方式虽有差异,但核心逻辑高度一致:面向对象语言(C#、Python、C++)可通过接口/抽象类+继承/多态,自然映射策略模式的三大核心角色,代码结构清晰、贴合模式原生设计;Go语言借助接口和结构体,以“组合”思想实现轻量级策略管理,兼顾简洁性与高性能;纯C语言则通过结构体+函数指针,手动模拟面向对象特性,实现策略模式的核心逻辑,体现了模式思想的跨语言适配性。

在实际开发中,使用策略模式的关键是“权衡场景、规避缺陷”:

  • 简单场景(≤3个策略、无扩展计划):优先使用条件判断,避免过度设计,兼顾开发效率;

  • 复杂场景(≥5个策略、频繁扩展/修改):果断使用策略模式,结合策略工厂、单例、装饰器等技巧,解决类爆炸、客户端使用复杂等问题;

  • 跨语言开发场景:根据语言特性调整实现方式(如C的函数指针、Go的接口),核心保持“策略封装、动态切换”的设计思想;

  • 性能敏感场景:通过策略单例、对象池化等方式,降低性能开销,必要时可放弃策略模式,选择条件判断。

总之,策略模式是解决“算法动态切换、独立扩展”类问题的有效工具,合理使用可让复杂的算法逻辑更清晰、更易维护,滥用则会增加系统复杂度。开发者需结合业务实际,灵活运用策略模式及扩展技巧,在设计优雅与开发效率之间找到平衡,构建高可维护、高扩展的系统。

状态模式(State Pattern)是一种经典的行为型设计模式,其核心思想是:允许对象在内部状态发生改变时,动态调整自身行为,使得对象看起来仿佛“修改了自身的类”。该模式通过将不同状态封装为独立的状态对象,将状态转换逻辑委托给这些对象,彻底摒弃复杂的条件分支判断,有效提升代码的可维护性、扩展性与可读性,是解决“状态驱动行为变化”类问题的最优方案之一。

一、状态模式核心结构

状态模式的设计核心的是“状态与行为解耦”,通过明确的角色分工,实现状态流转的灵活管理与行为逻辑的模块化封装。其核心角色分为三类,各角色职责清晰、协同工作,构成完整的状态驱动体系:

1.1 上下文(Context)

作为状态的“容器”与“调度中心”,上下文类维护一个当前状态对象的引用,对外提供统一的交互接口(如请求处理方法),并负责将所有与状态相关的请求,委派给当前的状态对象处理。同时,上下文也提供状态切换的入口,允许状态对象通过它完成状态更新,自身无需关注具体的状态逻辑。

1.2 抽象状态(State)

定义所有具体状态类的公共行为契约,通常以接口(面向对象语言)或函数指针(无面向对象特性语言)的形式存在。它声明了与上下文交互的核心方法,规范了具体状态类必须实现的行为,确保上下文能通过统一接口调用不同状态的逻辑。

1.3 具体状态(ConcreteState)

抽象状态的具体实现,是状态行为的核心载体。它不仅封装了对应状态下的具体业务逻辑,还可包含状态转换的决策逻辑——根据业务规则,判断何时切换到其他状态,并通过上下文完成状态更新。不同的具体状态类,对应对象在不同状态下的差异化行为。

核心流转逻辑:上下文持有抽象状态引用 → 接收外部请求 → 委派请求给当前具体状态对象 → 具体状态执行行为逻辑 → 必要时通过上下文切换状态 → 上下文更新当前状态,完成一次状态驱动的行为闭环。

二、多语言实现状态模式

为便于不同技术栈开发者落地实践,本文以“状态切换演示”为统一案例(通过两种状态的交替切换,直观呈现状态模式的核心逻辑),分别提供C#、Python、Golang、C++及纯C语言的可运行实现。各实现均贴合语言特性,补充规范注释、异常处理与边界校验,兼顾实用性与严谨性,清晰展现状态模式在不同语言中的适配思路。

2.1 C# 实现(面向对象规范实现)

C# 作为强类型面向对象语言,通过接口严格定义抽象状态契约,依托类的继承与多态实现具体状态逻辑,上下文通过属性封装状态引用,确保状态管理的安全性与规范性,适配企业级业务系统开发。

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
98
using System;

/// <summary>
/// 抽象状态接口:定义状态的核心行为契约
/// </summary>
public interface IState
{
/// <summary>
/// 处理状态相关请求,包含状态转换逻辑
/// </summary>
/// <param name="context">上下文对象</param>
void Handle(Context context);
}

/// <summary>
/// 具体状态A:实现状态A的行为与转换逻辑
/// </summary>
public class ConcreteStateA : IState
{
public void Handle(Context context)
{
Console.WriteLine("执行状态A的核心行为,触发状态切换至B");
// 通过上下文切换状态,状态转换逻辑封装在具体状态中
context.State = new ConcreteStateB();
}
}

/// <summary>
/// 具体状态B:实现状态B的行为与转换逻辑
/// </summary>
public class ConcreteStateB : IState
{
public void Handle(Context context)
{
Console.WriteLine("执行状态B的核心行为,触发状态切换至A");
context.State = new ConcreteStateA();
}
}

/// <summary>
/// 上下文类:管理状态引用,委派请求处理
/// </summary>
public class Context
{
// 私有字段存储当前状态,通过属性对外暴露,避免直接修改
private IState _currentState;

/// <summary>
/// 初始化上下文,指定初始状态
/// </summary>
/// <param name="initialState">初始状态对象</param>
public Context(IState initialState)
{
State = initialState ?? throw new ArgumentNullException(nameof(initialState), "初始状态不可为null");
}

/// <summary>
/// 当前状态属性,设置时打印状态切换日志
/// </summary>
public IState State
{
get => _currentState;
set
{
_currentState = value;
Console.WriteLine($"状态切换完成,当前状态:{_currentState.GetType().Name}");
}
}

/// <summary>
/// 对外统一请求接口,委派给当前状态处理
/// </summary>
public void Request()
{
_currentState.Handle(this);
}
}

// 客户端测试代码
class Program
{
static void Main(string[] args)
{
try
{
// 初始化上下文,设置初始状态为A
Context context = new Context(new ConcreteStateA());
// 触发两次请求,观察状态交替切换
context.Request(); // 状态A → 状态B
context.Request(); // 状态B → 状态A
}
catch (Exception ex)
{
Console.WriteLine($"执行异常:{ex.Message}");
}
}
}

2.2 Python 实现(动态语言简洁实现)

Python 遵循“鸭子类型”,无需显式定义接口,通过基类约定抽象状态的行为,语法简洁、无繁琐类型声明。依托动态类型特性,上下文可灵活切换状态对象,适配快速开发、脚本开发及轻量级项目,完美保留状态模式的核心逻辑。

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
class State:
"""抽象状态基类:约定状态的核心行为"""
def handle(self, context):
"""处理状态请求,子类需重写该方法"""
raise NotImplementedError("具体状态类必须实现handle方法")

class ConcreteStateA(State):
"""具体状态A:实现状态A的行为与转换逻辑"""
def handle(self, context):
print("执行状态A的核心行为,触发状态切换至B")
# 切换状态,直接修改上下文的状态属性
context.state = ConcreteStateB()

class ConcreteStateB(State):
"""具体状态B:实现状态B的行为与转换逻辑"""
def handle(self, context):
print("执行状态B的核心行为,触发状态切换至A")
context.state = ConcreteStateA()

class Context:
"""上下文类:管理状态,委派请求处理"""
def __init__(self, initial_state):
"""初始化上下文,指定初始状态"""
if not isinstance(initial_state, State):
raise TypeError("初始状态必须是State的子类实例")
self._state = initial_state

@property
def state(self):
"""当前状态属性(只读)"""
return self._state

@state.setter
def state(self, new_state):
"""设置当前状态,打印切换日志"""
if not isinstance(new_state, State):
raise TypeError("新状态必须是State的子类实例")
self._state = new_state
print(f"状态切换完成,当前状态:{new_state.__class__.__name__}")

def request(self):
"""对外统一请求接口,委派给当前状态处理"""
self._state.handle(self)

# 客户端测试代码
if __name__ == "__main__":
try:
# 初始化上下文,设置初始状态为A
context = Context(ConcreteStateA())
# 触发请求,观察状态切换
context.request() # 状态A → 状态B
context.request() # 状态B → 状态A
except Exception as e:
print(f"执行异常:{str(e)}")

2.3 Golang 实现(接口至上轻量实现)

Go 语言无类和继承,核心遵循“组合优于继承”原则,通过接口定义抽象状态契约,结构体实现具体状态逻辑,代码极简高效。依托接口的隐式实现特性,无需显式声明继承关系,贴合Go语言“简洁、务实、高性能”的设计理念,适配高并发后端开发场景。

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
package main

import "fmt"

// State 抽象状态接口:定义状态的核心行为
type State interface {
Hontext *Context) // 处理请求,包含状态转换逻辑
}

// ConcreteStateA 具体状态A
type ConcreteStateA struct{}

// Handle 实现状态A的行为与转换逻辑
func (s *ConcreteStateA) Handle(context *Context) {
fmt.Println("执行状态A的核心行为,触发状态切换至B")
coetState(&ConcreteStateB{})
}

// ConcreteStateB 具体状态B
type ConcreteStateB struct{}

// Handle 实现状态B的行为与转换逻辑
func (s *ConcreteStateB) Handle(context *Context) {
fmln("执行状态B的核心行为,触发状态切换至A")
ntext.SetState(&ConcreteStateA{})
}

// Context 上下文结构体:管理状态,委派请求
type Context struct {
curre State // 当前状态引用
}

// NewContext 工厂函数:创建上下文实例,指定初始状态
func NewContext(initialState State) *Context {
ialState == nil {
t.Println("初始状态不可为nil")
return nil
}
rn &Context{currentState: initialState}
}

// SetState 设置当前状态,打印切换日志
func (c *Context) SetState(newState State) {
f newState == nil {
"新状态不可为nil")

}
c.curretate
rintf("状态切换完成,当前状态:%T\n", newState)
}

// Request 对外统一请求接口,委派给当前状态处理
func (c *Context) Request() {
c.currentState == nil {
tln("当前状态未初始化,无法处理请求")

}
e.Handle(c)
}

// 客户端测试代码
func main() {
// 初始化上下态为A
:= NewContext(&ConcreteStateA{})
if context == nil {
/ 触发请求,观察状态切换
ntext.Request() // 状态A → 状态B
ext.Request() // 状态B → 状态A
}
cont co return
}
/ context文,设置初始状 c.currentStat return fmt.Prin if fmt.PntState = newS return fmt.Println( i retu fmif initentStat cot.Printntext.S andle(c

2.4 C++ 实现(抽象类+多态经典实现)

C++ 作为经典面向对象语言,通过抽象类(纯虚函数)定义抽象状态接口,子类继承并实现具体状态逻辑,依托多态特性实现状态的灵活切换。需手动管理内存(通过虚析构函数避免内存泄漏),兼顾灵活性与高性能,适配底层开发、高频调用场景,是底层系统、高性能应用的优选实现方式。

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
#include <iostream>
#include <typeinfo>
using namespace std;

// 前向声明上下文类,解决交叉引用问题
class Context;

// 抽象状态类:纯虚函数定义接口
class State {
public:
virtual ~State() = default; // 虚析构函数,避免多态场景内存泄漏
/// <summary>
/// 处理状态请求,子类必须实现
/// </summary>
/// <param name="context">上下文对象指针</param>
virtual void Handle(Context* context) = 0;
};

// 上下文类:管理状态,委派请求处理
class Context {
private:
State* currentState; // 当前状态指针
public:
/// <summary>
/// 初始化上下文,指定初始状态
/// </summary>
/// <param name="initialState">初始状态指针</param>
Context(State* initialState) : currentState(initialState) {
if (initialState == nullptr)
throw invalid_argument("初始状态不可为nullptr");
}

/// <summary>
/// 析构函数:释放状态对象内存
/// </summary>
~Context() {
delete currentState;
currentState = nullptr;
}

/// <summary>
/// 设置当前状态,打印切换日志
/// </summary>
/// <param name="newState">新状态指针</param>
void SetState(State* newState) {
if (newState == nullptr)
throw invalid_argument("新状态不可为nullptr");
delete currentState; // 释放旧状态内存
currentState = newState;
cout << "状态切换完成,当前状态:" << typeid(*currentState).name() << endl;
}

/// <summary>
/// 对外统一请求接口,委派给当前状态处理
/// </summary>
void Request() {
currentState->Handle(this);
}
};

// 具体状态A:继承抽象状态,实现核心逻辑
class ConcreteStateA : public State {
public:
void Handle(Context* context) override {
cout << "执行状态A的核心行为,触发状态切换至B" << endl;
context->SetState(new ConcreteStateB());
}
};

// 具体状态B:继承抽象状态,实现核心逻辑
class ConcreteStateB : public State {
public:
void Handle(Context* context) override {
cout << "执行状态B的核心行为,触发状态切换至A" << endl;
context->SetState(new ConcreteStateA());
}
};

// 客户端测试代码
int main() {
try {
// 初始化上下文,设置初始状态为A
Context* context = new Context(new ConcreteStateA());
// 触发请求,观察状态切换
context->Request(); // 状态A → 状态B
context->Request(); // 状态B → 状态A

// 释放上下文内存(会自动释放状态对象)
delete context;
context = nullptr;
}
catch (const exception& e) {
cout << "执行异常:" << e.what() << endl;
}
return 0;
}

2.5 纯C语言实现(结构体+函数指针模拟实现)

纯C语言无面向对象特性,无类和多态,通过“结构体封装数据+函数指针封装行为”,手动模拟状态模式的核心逻辑。用结构体封装上下文和状态的属性,用函数指针模拟抽象状态的接口方法,需手动管理内存,代码虽略显冗余,但底层可控性强,适配嵌入式、底层开发等资源受限场景,完美还原“状态封装、行为委派”的核心思想。

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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 前向声明上下文结构体,解决交叉引用问题
typedef struct Context Context;

// 状态处理函数指针类型(模拟抽象状态接口)
typedef void (*StateHandleFunc)(Context*);

// 上下文结构体:管理当前状态(函数指针)
struct Context {
StateHandleFunc currentHandle; // 当前状态的处理函数
};

// 具体状态A的处理函数:实现状态A的行为与转换逻辑
void ConcreteStateA_Handle(Context* context);
// 具体状态B的处理函数:实现状态B的行为与转换逻辑
void ConcreteStateB_Handle(Context* context);

// 具体状态A的处理逻辑
void ConcreteStateA_Handle(Context* context) {
if (context == NULL) {
printf("上下文不可为NULL\n");
return;
}
printf("执行状态A的核心行为,触发状态切换至B\n");
// 切换状态:修改上下文的函数指针
context->currentHandle = ConcreteStateB_Handle;
printf("状态切换完成,当前状态:ConcreteStateB\n");
}

// 具体状态B的处理逻辑
void ConcreteStateB_Handle(Context* context) {
if (context == NULL) {
printf("上下文不可为NULL\n");
return;
}
printf("执行状态B的核心行为,触发状态切换至A\n");
context->currentHandle = ConcreteStateA_Handle;
printf("状态切换完成,当前状态:ConcreteStateA\n");
}

// 工厂函数:创建上下文实例,指定初始状态
Context* Context_Create(StateHandleFunc initialHandle) {
if (initialHandle == NULL) {
printf("初始状态处理函数不可为NULL\n");
return NULL;
}
Context* context = (Context*)malloc(sizeof(Context));
if (context == NULL) {
printf("内存分配失败,无法创建上下文\n");
return NULL;
}
context->currentHandle = initialHandle;
return context;
}

// 上下文请求处理函数:委派给当前状态的处理函数
void Context_Request(Context* context) {
if (context == NULL || context->currentHandle == NULL) {
printf("上下文未初始化或状态无效,无法处理请求\n");
return;
}
context->currentHandle(context);
}

// 释放上下文内存
void Context_Destroy(Context* context) {
if (context != NULL) {
free(context);
context = NULL;
}
}

// 客户端测试代码
int main() {
// 初始化上下文,设置初始状态为A
Context* context = Context_Create(ConcreteStateA_Handle);
if (context == NULL) {
return -1;
}

// 触发请求,观察状态切换
Context_Request(context); // 状态A → 状态B
Context_Request(context); // 状态B → 状态A

// 释放内存
Context_Destroy(context);
return 0;
}

三、状态模式的优缺点

状态模式的核心价值的是“解耦状态与行为,消除复杂条件分支”,其优缺点均围绕这一核心展开。在实际开发中,需结合业务场景的状态数量、流转复杂度和性能要求,权衡使用,避免过度设计或滥用,确保既发挥其核心优势,又规避潜在问题。

3.1 核心优点

  • 消除复杂条件分支,提升代码可读性:将不同状态的行为逻辑封装到独立的状态类中,彻底替代大量嵌套的if/elseswitch语句,让代码结构更清晰,逻辑更易理解。例如电商订单的多状态流转,若用条件判断需覆盖待支付、已支付、已发货等所有分支,而状态模式只需为每个状态定义独立逻辑,可读性大幅提升。

  • 状态与行为解耦,提升可维护性:每个状态的行为独立封装,修改某一状态的逻辑时,无需改动其他状态和上下文代码,降低维护成本。同时,状态逻辑的模块化的设计,也便于单独调试和测试。

  • 符合开闭原则,扩展性极强:新增状态时,只需添加新的具体状态类,实现抽象状态接口,无需修改上下文和已有状态代码,即可完成功能扩展。例如为电梯系统新增“故障”状态,仅需实现ConcreteStateFault类,上下文无需任何改动。

  • 状态流转清晰可控,便于问题定位:状态转换逻辑集中在具体状态类中,可通过日志快速跟踪状态流转路径,定位状态切换异常的问题。同时,上下文统一管理状态引用,避免状态混乱。

  • 状态行为可复用:相同的状态逻辑可在不同的上下文或业务场景中复用,例如“待机”状态,可在游戏角色、智能设备等多个场景中复用,减少代码冗余。

3.2 主要缺点

  • 类/结构体膨胀,增加维护成本:每个状态对应一个具体类(或结构体+函数),当状态数量较多时(如10个以上),会导致类/结构体数量激增,增加代码量和维护成本,甚至出现“类爆炸”问题。

  • 状态转换逻辑分散,全局把控难度大:状态转换逻辑分散在各个具体状态类中,对于复杂状态机(多状态、多流转规则),难以全局把控所有状态的流转关系,修改全局流转规则时,可能需要修改多个状态类。

  • 存在过度设计风险:对于简单状态场景(如仅2-3个状态,且无扩展计划),使用状态模式会增加设计复杂度,显得冗余。例如仅包含“开启/关闭”的设备状态,用简单的布尔变量+条件判断即可实现,无需引入状态模式。

  • 上下文与状态存在耦合:具体状态类通常需要持有上下文引用以完成状态切换,导致状态类与上下文的耦合度升高。若上下文的结构或接口发生变更,需同步修改所有关联的状态类。

  • 轻微性能开销:状态模式的多态调用(或函数指针调用)会带来轻微的性能开销,在极致性能要求的底层场景(如嵌入式实时系统),需谨慎使用。

四、状态模式的使用场景

状态模式的核心适用场景是“对象行为随状态动态变化,且状态流转复杂、需频繁扩展”的业务场景。以下结合具体场景及典型实战案例,帮助开发者快速判断是否适用,实现精准落地,避免滥用或错用。

4.1 核心适用场景

  • 对象行为随状态动态变化:对象在不同状态下表现出截然不同的行为,且状态切换频繁。典型案例:电梯(停止、运行、开门、关门、故障)、订单(待支付、已支付、已发货、已完成、已取消)、游戏角色(待机、移动、攻击、受伤、死亡)、网络连接(连接中、已连接、断开连接、重连)。

  • 避免复杂条件分支:代码中出现大量与状态相关的条件分支,且分支逻辑频繁修改、扩展。例如某设备控制逻辑,包含5种以上状态,每种状态对应不同的操作逻辑,若用条件判断会导致代码臃肿、难以维护,适合使用状态模式重构。

  • 状态转换规则明确:状态之间的流转规则清晰,且需要灵活扩展状态或修改转换逻辑。典型案例:编译器的语法分析状态机(不同语法节点对应不同解析状态,需支持新增语法规则)、工作流引擎(流程节点的状态流转,支持动态配置)。

  • 需要统一管理状态行为:不同状态下的行为差异较大,且希望将状态相关行为集中管理,便于测试和维护。典型案例:智能家居设备的模式切换(空调的制冷、制热、送风、除湿模式)、支付系统的支付状态管理(待支付、支付中、支付成功、支付失败)。

4.2 典型实战案例

  • 电商订单状态流转:订单从创建到完成,需经历待支付、已支付、已发货、已完成、已取消等多个状态,每个状态对应不同的业务逻辑(如待支付状态可取消订单,已支付状态可申请退款),状态转换规则明确,适合用状态模式实现,便于后续新增“退款中”“售后中”等状态。

  • 电梯控制系统:电梯包含停止、运行、开门、关门、故障等状态,不同状态下的行为逻辑不同(如运行状态无法开门,开门状态无法运行),状态转换需遵循严格规则,用状态模式可清晰管理各状态的行为和流转,避免复杂条件判断。

  • 游戏角色状态管理:游戏角色的待机、移动、攻击、受伤、死亡等状态,每种状态对应不同的动画、音效和行为逻辑(如受伤状态移动速度降低,死亡状态无法执行任何操作),状态切换频繁,用状态模式可灵活扩展新状态(如“眩晕”“无敌”),且不影响原有逻辑。

  • 网络连接状态管理:客户端与服务器的连接状态(连接中、已连接、断开连接、重连),不同状态下的行为逻辑不同(如已连接状态可发送数据,断开连接状态需触发重连),用状态模式可统一管理连接状态,简化重连逻辑和数据发送逻辑。

4.3 避坑场景

  • 状态数量极少且无扩展计划:若仅存在2-3个状态,且后续无新增状态的需求,用简单的条件判断即可实现,无需引入状态模式,避免过度设计。

  • 状态转换规则混乱无规律:状态模式依赖清晰的状态流转逻辑,若状态之间的转换无固定规则,会导致状态类逻辑混乱,难以维护,此时不适合使用状态模式。

  • 极致性能敏感的底层场景:如嵌入式实时系统、高频交易系统,状态模式的多态/函数指针调用会带来轻微性能开销,可能影响系统响应速度,需谨慎使用,可考虑用条件判断替代。

  • 上下文频繁变更的场景:若上下文的结构或接口频繁变更,会导致所有关联的状态类同步修改,增加维护成本,此时需先优化上下文设计,再考虑是否使用状态模式。

五、状态模式的扩展与实战技巧

在实际工程实践中,单纯的状态模式可能无法满足复杂场景的需求,需结合其他设计模式或优化技巧,解决状态模式的固有缺陷(如类膨胀、逻辑分散),提升系统的灵活性和可维护性。

5.1 状态管理器优化(解决逻辑分散问题)

对于复杂状态机(多状态、多流转规则),可引入“状态管理器”(StateManager)统一管理状态转换规则,替代分散在具体状态类中的转换逻辑。状态管理器负责校验状态转换的合法性、记录状态流转日志、维护状态流转规则表,具体状态类仅需实现自身的业务行为,无需关注状态转换逻辑,从而实现“行为与流转规则”的解耦。

例如,电商订单的状态流转规则可配置在状态管理器中,当订单需要切换状态时,由状态管理器校验流转是否合法(如待支付状态可切换至已支付或已取消,已发货状态不可切换至待支付),通过配置化的方式,减少状态类的逻辑冗余,便于全局修改流转规则。

5.2 与其他设计模式结合使用

  • 与单例模式结合:具体状态类通常无状态数据(行为逻辑固定),可设计为单例,减少对象创建开销。例如C#中通过静态只读实例、Python中通过__new__方法、Go中通过全局变量,实现状态对象的单例化,避免频繁创建状态对象。

  • 与工厂模式结合:通过状态工厂类(StateFactory)创建具体状态对象,降低上下文与具体状态类的耦合。上下文无需直接实例化具体状态类,只需通过工厂类获取状态对象,新增状态时,仅需修改工厂类,无需改动上下文代码。

  • 与策略模式结合:状态模式与策略模式结构相似,核心区别在于:状态模式关注“状态驱动的行为变化”,状态切换由内部逻辑触发;策略模式关注“算法替换”,策略选择由外部逻辑触发。两者结合可实现“状态+算法”的双层灵活扩展,例如游戏角色的“攻击状态”,可搭配不同的攻击策略(近战、远程、魔法),提升系统的灵活性。

  • 与观察者模式结合:当状态切换时,需要通知其他组件(如UI更新、日志记录),可结合观察者模式,让上下文作为被观察者,状态切换时通知所有观察者,实现状态变更的联动效果,避免状态类与其他组件的直接耦合。

5.3 多语言实现的注意事项

  • **C#**:通过接口定义抽象状态,用抽象类封装通用状态逻辑(如日志记录),减少重复代码;利用属性封装状态引用,避免外部直接修改状态;新增状态时,确保接口实现的一致性,可通过代码检查工具校验。

  • Python:无需严格遵循“接口-实现”结构,可通过鸭子类型简化设计;注意状态对象的内存管理,避免循环引用(如上下文与状态相互引用);利用装饰器封装通用逻辑(如状态切换日志),提升代码复用性。

  • Golang:依托接口的隐式实现特性,简化状态类定义;通过结构体组合复用通用状态逻辑(如将日志记录逻辑封装为单独的结构体,嵌入具体状态结构体);注意状态接口的方法签名一致性,避免隐式实现失败。

  • **C++**:抽象状态类的析构函数必须声明为虚函数,避免多态场景下的内存泄漏;可通过智能指针(如unique_ptr)管理状态对象,简化内存管理;注意纯虚函数的实现,确保所有具体状态类都实现了抽象接口的方法。

  • 纯C:函数指针需保证类型一致性,避免野指针;通过结构体封装状态相关数据,提升代码可读性;注意内存分配与释放的配对,避免内存泄漏;可通过宏定义简化状态切换的代码,减少冗余。

六、总结

状态模式的核心精髓是“将状态封装为独立对象,让行为随状态动态切换”,其本质是通过解耦状态与行为,消除复杂条件分支,提升代码的可维护性、扩展性与可读性。它不仅是一种代码组织方式,更是一种“状态驱动”的设计思维——让对象的行为由其内部状态决定,而非外部条件判断,从而让系统更易应对复杂的状态变化和需求迭代。

从多语言实现来看,不同语言的实现方式虽有差异,但核心逻辑高度一致:面向对象语言(C#、Python、C++)可通过接口/抽象类+继承/多态,自然映射状态模式的三大核心角色,代码结构清晰、贴合模式原生设计;Go语言借助接口和结构体,以“组合”思想实现轻量级状态管理,兼顾简洁性与高性能;纯C语言则通过结构体+函数指针,手动模拟面向对象特性,实现状态模式的核心逻辑,体现了模式思想的跨语言适配性。

在实际开发中,使用状态模式的关键是“权衡场景、规避缺陷”:

  • 简单场景(≤3个状态、无扩展计划):优先使用条件判断,避免过度设计,兼顾开发效率;

  • 复杂场景(≥5个状态、频繁扩展/修改):果断使用状态模式,结合状态管理器、单例、工厂等技巧,解决类膨胀、逻辑分散等问题;

  • 跨语言开发场景:根据语言特性调整实现方式(如C的函数指针、Go的接口),核心保持“状态封装、行为委派”的设计思想;

  • 性能敏感场景:谨慎使用,可通过优化状态切换逻辑、减少多态调用,降低性能开销。

总之,状态模式是解决“状态驱动行为变化”类问题的有效工具,合理使用可让复杂状态流转逻辑更清晰、更易维护,滥用则会增加系统复杂度。开发者需结合业务实际,灵活运用状态模式及扩展技巧,在设计优雅与开发效率之间找到平衡,构建高可维护、高扩展的系统。

在软件系统开发中,当多个对象存在频繁且复杂的交互时,对象间直接两两通信会形成紧密耦合的“网状依赖”,导致代码可读性差、维护成本高、扩展难度大。中介者模式(Mediator Pattern)作为经典的行为型设计模式,核心是引入中介者对象封装多对象交互逻辑,使所有对象仅与中介者通信、无需直接关联,将“网状交互”转化为“星型交互”,大幅降低系统耦合度,提升代码可维护性与可扩展性。本文将深入剖析中介者模式的核心结构,通过C#、Python、Go、C++、纯C多语言落地实现,拆解其优缺点与适用场景,总结设计精髓与工程实践要点。

一、中介者模式核心结构

中介者模式的核心是分离交互逻辑与业务逻辑,由中介者承担对象间的通信协调职责,让同事类专注于自身核心业务。其核心角色分为四类,职责分工明确,完整覆盖“交互协调-业务执行”全流程:

1.1 抽象中介者(Mediator)

定义中介者与同事类的通信接口,是两者交互的行为契约。通常包含两大核心方法:一是注册同事类,将同事对象纳入中介者管理范围;二是转发交互请求,将一个同事的请求传递给其他相关同事,无需关注具体交互逻辑的实现。

1.2 具体中介者(ConcreteMediator)

抽象中介者的具体实现,是整个模式的核心协调者。它维护所有同事类的引用,封装具体交互规则,接收同事类的请求并按业务逻辑转发给对应同事,实现多对象协同交互。同时,具体中介者可承载自身业务逻辑,进一步简化同事类职责。

1.3 抽象同事类(Colleague)

定义所有同事类的通用接口,封装共性行为。它持有抽象中介者的引用,使同事类可通过中介者与其他同事通信,无需直接引用。抽象同事类通常包含发送请求和接收请求的抽象方法,由具体同事类实现。

1.4 具体同事类(ConcreteColleague)

抽象同事类的具体实现,是业务逻辑的载体。它实现自身核心业务逻辑,与其他同事交互时,不直接调用对方方法,而是通过中介者转发请求;同时接收中介者转发的请求,执行相应处理逻辑。

核心逻辑链路:具体同事类 → 发送请求至中介者 → 具体中介者解析交互规则 → 转发请求至相关同事类 → 接收请求的同事类执行处理。全程同事类无直接交互,完全通过中介者完成通信。

二、多语言实现中介者模式

为便于开发者落地实践,本文以“在线聊天室”为经典案例,统一实现中介者模式:多个用户(具体同事类)通过聊天室(具体中介者)发送、接收消息,无需直接建立通信,仅需将消息发送至聊天室,由其统一转发给其他在线用户。所有实现均保证可运行,贴合各语言设计理念,补充规范注释、异常处理与边界判断,兼顾实用性与可读性,体现各语言对中介者模式的适配思路。

2.1 C# 实现(依托接口,贴合面向对象规范)

C# 作为强类型面向对象语言,通过接口严格定义抽象中介者与同事类的契约,利用泛型和集合优化同事类管理,依托GC自动管理内存,代码结构严谨、可读性高,适配企业级业务系统,是中介者模式的常用实现方式。

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
using System;
using System.Collections.Generic;

/// <summary>
/// 抽象中介者:聊天室接口,定义中介者与用户的通信契约
/// </summary>
public interface IChatRoomMediator
{
/// <summary>
/// 注册用户(将用户纳入聊天室管理)
/// </summary>
/// <param name="user">用户实例</param>
void RegisterUser(User user);

/// <summary>
/// 转发消息(将发送者的消息转发给其他所有用户)
/// </summary>
/// <param name="message">消息内容</param>
/// <param name="sender">消息发送者</param>
void SendMessage(string message, User sender);
}

/// <summary>
/// 具体中介者:在线聊天室,实现消息转发和用户管理逻辑
/// </summary>
public class ChatRoom : IChatRoomMediator
{
// 存储在线用户列表,线程安全考虑,使用锁机制
private readonly List<User> _users = new List<User>();
private readonly object _lockObj = new object();

public void RegisterUser(User user)
{
if (user == null)
throw new ArgumentNullException(nameof(user), "用户实例不可为null");

lock (_lockObj)
{
if (!_users.Contains(user))
{
_users.Add(user);
user.Mediator = this; // 为用户绑定中介者
Console.WriteLine($"用户【{user.Name}】加入聊天室");
}
}
}

public void SendMessage(string message, User sender)
{
if (sender == null)
throw new ArgumentNullException(nameof(sender), "发送者实例不可为null");
if (string.IsNullOrEmpty(message))
throw new ArgumentException("消息内容不可为空或空白", nameof(message));

lock (_lockObj)
{
// 遍历所有在线用户,转发消息(发送者不接收自己的消息)
foreach (var user in _users)
{
if (user != sender)
{
user.ReceiveMessage(message, sender.Name);
}
}
}
}
}

/// <summary>
/// 抽象同事类:用户,定义用户的通用行为
/// </summary>
public abstract class User
{
/// <summary>
/// 用户名
/// </summary>
public string Name { get; }

/// <summary>
/// 关联的中介者(聊天室)
/// </summary>
public IChatRoomMediator Mediator { get; set; }

public User(string name)
{
if (string.IsNullOrEmpty(name))
throw new ArgumentException("用户名不可为空或空白");
Name = name;
}

/// <summary>
/// 发送消息(通过中介者转发)
/// </summary>
/// <param name="message">消息内容</param>
public abstract void SendMessage(string message);

/// <summary>
/// 接收消息(处理中介者转发的消息)
/// </summary>
/// <param name="message">消息内容</param>
/// <param name="senderName">发送者用户名</param>
public abstract void ReceiveMessage(string message, string senderName);
}

/// <summary>
/// 具体同事类:普通用户,实现用户的发送和接收消息逻辑
/// </summary>
public class NormalUser : User
{
public NormalUser(string name) : base(name) { }

public override void SendMessage(string message)
{
if (Mediator == null)
throw new InvalidOperationException("用户未加入任何聊天室,无法发送消息");

Console.WriteLine($"\n{Name} 发送消息:{message}");
Mediator.SendMessage(message, this); // 通过中介者转发消息
}

public override void ReceiveMessage(string message, string senderName)
{
Console.WriteLine($"{Name} 收到 {senderName} 的消息:{message}");
}
}

// 客户端测试代码
class Program
{
static void Main(string[] args)
{
try
{
// 1. 初始化中介者(聊天室)
IChatRoomMediator chatRoom = new ChatRoom();

// 2. 初始化具体同事类(用户)
User user1 = new NormalUser("张三");
User user2 = new NormalUser("李四");
User user3 = new NormalUser("王五");

// 3. 注册用户到聊天室(绑定中介者)
chatRoom.RegisterUser(user1);
chatRoom.RegisterUser(user2);
chatRoom.RegisterUser(user3);

// 4. 模拟用户发送消息,测试中介者转发逻辑
user1.SendMessage("大家好!我是张三,很高兴加入聊天室~");
Console.WriteLine("---");
user2.SendMessage("张三你好!欢迎加入~");
Console.WriteLine("---");
user3.SendMessage("大家好呀!");
}
catch (Exception ex)
{
Console.WriteLine($"C# 执行异常:{ex.Message}");
}
}
}

2.2 Python 实现(动态语言特性,简洁灵活)

Python 遵循“鸭子类型”,无需显式定义抽象接口,通过基类约定中介者与同事类的行为,语法简洁、无需繁琐类型声明,依托GC自动管理内存,适配快速开发、脚本开发及轻量级项目,完整保留“解耦交互”核心逻辑。

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
class ChatRoomMediator:
"""抽象中介者:聊天室基类,约定中介者的核心行为"""
def register_user(self, user):
"""注册用户
Args:
user (User): 用户实例
"""
raise NotImplementedError("具体中介者需实现register_user方法")

def send_message(self, message, sender):
"""转发消息
Args:
message (str): 消息内容
sender (User): 消息发送者
"""
raise NotImplementedError("具体中介者需实现send_message方法")

class ConcreteChatRoom(ChatRoomMediator):
"""具体中介者:在线聊天室,实现用户管理和消息转发逻辑"""
def __init__(self):
self.users = [] # 存储在线用户列表

def register_user(self, user):
if user is None:
raise ValueError("用户实例不可为None")
if user not in self.users:
self.users.append(user)
user.mediator = self # 为用户绑定中介者
print(f"用户【{user.name}】加入聊天室")

def send_message(self, message, sender):
if sender is None:
raise ValueError("发送者实例不可为None")
if not message or message.strip() == "":
raise ValueError("消息内容不可为空或空白")
# 转发消息,发送者不接收自己的消息
for user in self.users:
if user != sender:
user.receive_message(message, sender.name)

class User:
"""抽象同事类:用户基类,约定用户的核心行为"""
def __init__(self, name):
if not name or name.strip() == "":
raise ValueError("用户名不可为空或空白")
self.name = name
self.mediator = None # 关联的中介者(初始为None)

def send_message(self, message):
"""发送消息(通过中介者转发)"""
raise NotImplementedError("具体用户需实现send_message方法")

def receive_message(self, message, sender_name):
"""接收消息(处理中介者转发的消息)"""
raise NotImplementedError("具体用户需实现receive_message方法")

class NormalUser(User):
"""具体同事类:普通用户,实现发送和接收消息的具体逻辑"""
def send_message(self, message):
if self.mediator is None:
raise RuntimeError("用户未加入任何聊天室,无法发送消息")
print(f"\n{self.name} 发送消息:{message}")
self.mediator.send_message(message, self) # 通过中介者转发消息

def receive_message(self, message, sender_name):
print(f"{self.name} 收到 {sender_name} 的消息:{message}")

# 客户端测试代码
if __name__ == "__main__":
try:
# 初始化中介者和用户
chat_room = ConcreteChatRoom()
user1 = NormalUser("张三")
user2 = NormalUser("李四")
user3 = NormalUser("王五")

# 注册用户到聊天室
chat_room.register_user(user1)
chat_room.register_user(user2)
chat_room.register_user(user3)

# 模拟用户发送消息
user1.send_message("大家好!我是张三,很高兴加入聊天室~")
print("---")
user2.send_message("张三你好!欢迎加入~")
print("---")
user3.send_message("大家好呀!")
except Exception as e:
print(f"Python 执行异常:{str(e)}")

2.3 Go 实现(接口至上,轻量化解耦)

Go 语言无类和继承,核心遵循“组合优于继承”,通过接口定义中介者与同事类的统一契约,结构体实现具体逻辑,代码极简高效,贴合Go“简洁、务实、高性能”的设计理念,适配高并发后端开发场景。

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package main

import (
"fmt"
"sync"
)

// 抽象中介者接口:定义聊天室的核心行为
type ChatRoomMediator interface {
RegisterUser(user User) // 注册用户
SendMessage(message string, sender User) // 转发消息
}

// 具体中介者:在线聊天室
type ConcreteChatRoom struct {
users []User // 在线用户列表
mu sync.RWMutex // 读写锁,保证并发安全
}

// NewConcreteChatRoom 工厂函数:创建聊天室实例
func NewConcreteChatRoom() *ConcreteChatRoom {
return &ConcreteChatRoom{
users: make([]User, 0),
}
}

// RegisterUser 实现注册用户逻辑
func (c *ConcreteChatRoom) RegisterUser(user User) {
if user == nil {
fmt.Println("Golang: 用户实例不可为nil")
return
}
c.mu.Lock()
defer c.mu.Unlock()
// 避免重复注册
for _, u := range c.users {
if u == user {
return
}
}
c.users = append(c.users, user)
user.SetMediator(c) // 为用户绑定中介者
fmt.Printf("用户【%s】加入聊天室\n", user.GetName())
}

// SendMessage 实现消息转发逻辑
func (c *ConcreteChatRoom) SendMessage(message string, sender User) {
if sender == nil {
fmt.Println("Golang: 发送者实例不可为nil")
return
}
if message == "" {
fmt.Println("Golang: 消息内容不可为空")
return
}
c.mu.RLock()
defer c.mu.RUnlock()
// 转发消息,发送者不接收自己的消息
for _, user := range c.users {
if user != sender {
user.ReceiveMessage(message, sender.GetName())
}
}
}

// 抽象同事类接口:定义用户的核心行为
type User interface {
GetName() string // 获取用户名
SetMediator(mediator ChatRoomMediator) // 绑定中介者
SendMessage(message string) // 发送消息
ReceiveMessage(message string, senderName string) // 接收消息
}

// 具体同事类:普通用户
type NormalUser struct {
name string
mediator ChatRoomMediator
}

// NewNormalUser 工厂函数:创建普通用户实例
func NewNormalUser(name string) *NormalUser {
if name == "" {
fmt.Println("Golang: 用户名不可为空")
return nil
}
return &NormalUser{
name: name,
}
}

func (u *NormalUser) GetName() string {
return u.name
}

func (u *NormalUser) SetMediator(mediator ChatRoomMediator) {
u.mediator = mediator
}

func (u *NormalUser) SendMessage(message string) {
if u.mediator == nil {
fmt.Printf("Golang: 用户【%s】未加入任何聊天室,无法发送消息\n", u.name)
return
}
fmt.Printf("\n%s 发送消息:%s\n", u.name, message)
u.mediator.SendMessage(message, u) // 通过中介者转发消息
}

func (u *NormalUser) ReceiveMessage(message string, senderName string) {
fmt.Printf("%s 收到 %s 的消息:%s\n", u.name, senderName, message)
}

// 客户端测试代码
func main() {
// 初始化中介者(聊天室)
chatRoom := NewConcreteChatRoom()
// 初始化具体同事类(用户)
user1 := NewNormalUser("张三")
user2 := NewNormalUser("李四")
user3 := NewNormalUser("王五")

// 注册用户到聊天室
chatRoom.RegisterUser(user1)
chatRoom.RegisterUser(user2)
chatRoom.RegisterUser(user3)

// 模拟用户发送消息
user1.SendMessage("大家好!我是张三,很高兴加入聊天室~")
fmt.Println("---")
user2.SendMessage("张三你好!欢迎加入~")
fmt.Println("---")
user3.SendMessage("大家好呀!")
}

2.4 C++ 实现(抽象类+多态,经典面向对象实现)

C++ 作为经典面向对象语言,通过抽象类(纯虚函数)定义中介者与同事类的接口,子类实现具体逻辑,依托继承和多态特性实现解耦,需手动管理内存(通过析构函数释放对象),兼顾灵活性与高性能,适配底层开发、高频调用场景,是底层系统、高性能应用的优选实现方式。

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#include <iostream>
#include <vector>
#include <string>
#include <mutex>
using namespace std;

// 前向声明:解决同事类与中介者的交叉引用问题
class User;

// 抽象中介者类(纯虚函数定义接口)
class ChatRoomMediator {
public:
virtual ~ChatRoomMediator() = default; // 虚析构函数,避免多态场景内存泄漏
/// <summary>
/// 注册用户
/// </summary>
/// <param name="user">用户指针</param>
virtual void registerUser(User* user) = 0;
/// <summary>
/// 转发消息
/// </summary>
/// <param name="message">消息内容</param>
/// <param name="sender">消息发送者指针</param>
virtual void sendMessage(const string& message, User* sender) = 0;
};

// 抽象同事类(纯虚函数定义接口)
class User {
protected:
string name; // 用户名
ChatRoomMediator* mediator; // 关联的中介者
public:
User(const string& name) : name(name), mediator(nullptr) {
if (name.empty())
throw invalid_argument("用户名不可为空");
}
virtual ~User() = default; // 虚析构函数

// 绑定中介者
void setMediator(ChatRoomMediator* med) { mediator = med; }
// 获取用户名
string getName() const { return name; }
/// <summary>
/// 发送消息(纯虚函数,由子类实现)
/// </summary>
/// <param name="message">消息内容</param>
virtual void sendMessage(const string& message) = 0;
/// <summary>
/// 接收消息(纯虚函数,由子类实现)
/// </summary>
/// <param name="message">消息内容</param>
/// <param name="senderName">发送者用户名</param>
virtual void receiveMessage(const string& message, const string& senderName) = 0;
};

// 具体中介者:在线聊天室
class ConcreteChatRoom : public ChatRoomMediator {
private:
vector<User*> users; // 在线用户列表(存储用户指针)
mutex mtx; // 互斥锁,保证并发安全
public:
~ConcreteChatRoom() override {
// 释放所有用户指针,避免内存泄漏
for (User* user : users) {
delete user;
}
users.clear();
}

void registerUser(User* user) override {
if (user == nullptr)
throw invalid_argument("用户实例不可为nullptr");
lock_guard<mutex> lock(mtx);
// 避免重复注册
for (User* u : users) {
if (u == user)
return;
}
users.push_back(user);
user->setMediator(this); // 绑定中介者
cout << "用户【" << user->getName() << "】加入聊天室" << endl;
}

void sendMessage(const string& message, User* sender) override {
if (sender == nullptr)
throw invalid_argument("发送者实例不可为nullptr");
if (message.empty())
throw invalid_argument("消息内容不可为空");
lock_guard<mutex> lock(mtx);
// 转发消息,发送者不接收自己的消息
for (User* user : users) {
if (user != sender) {
user->receiveMessage(message, sender->getName());
}
}
}
};

// 具体同事类:普通用户
class NormalUser : public User {
public:
NormalUser(const string& name) : User(name) {}

void sendMessage(const string& message) override {
if (mediator == nullptr)
throw runtime_error("用户未加入任何聊天室,无法发送消息");
cout << "\n" << name << " 发送消息:" << message << endl;
mediator->sendMessage(message, this); // 通过中介者转发消息
}

void receiveMessage(const string& message, const string& senderName) override {
cout << name << " 收到 " << senderName << " 的消息:" << message << endl;
}
};

// 客户端测试代码
int main() {
try {
// 1. 初始化中介者(聊天室)
ChatRoomMediator* chatRoom = new ConcreteChatRoom();

// 2. 初始化具体同事类(用户)并注册
User* user1 = new NormalUser("张三");
User* user2 = new NormalUser("李四");
User* user3 = new NormalUser("王五");
chatRoom->registerUser(user1);
chatRoom->registerUser(user2);
chatRoom->registerUser(user3);

// 3. 模拟用户发送消息
user1->sendMessage("大家好!我是张三,很高兴加入聊天室~");
cout << "---" << endl;
user2->sendMessage("张三你好!欢迎加入~");
cout << "---" << endl;
user3->sendMessage("大家好呀!");

// 4. 释放中介者内存(会自动释放所有用户内存)
delete chatRoom;
}
catch (const exception& e) {
cout << "C++ 执行异常:" << e.what() << endl;
}
return 0;
}

2.5 纯C语言实现(结构体+函数指针,模拟面向对象)

纯C语言无面向对象特性,无类和多态,通过“结构体封装数据+函数指针封装行为”,手动模拟中介者模式的核心逻辑。用结构体封装中介者和同事类的属性,用函数指针模拟抽象接口的方法,需手动管理内存,代码虽略显冗余,但底层可控性强,适配嵌入式、底层开发等资源受限场景,完美还原“解耦交互、集中协调”的核心思想。

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>

// 定义常量,避免魔法数字,提升代码可读性
#define MAX_USER_NAME_LEN 20 // 最大用户名长度
#define MAX_USERS 10 // 最大在线用户数(适配资源受限场景)
#define MAX_MESSAGE_LEN 100 // 最大消息长度

// 前向声明:解决结构体交叉引用问题
typedef struct User User;
typedef struct ChatRoomMediator ChatRoomMediator;

// 抽象中介者:结构体+函数指针模拟接口
typedef struct ChatRoomMediator {
User* users[MAX_USERS]; // 在线用户数组
int user_count; // 已注册用户数量
pthread_mutex_t mutex; // 互斥锁,保证并发安全
// 中介者方法:注册用户、转发消息
void (*register_user)(ChatRoomMediator* mediator, User* user);
void (*send_message)(ChatRoomMediator* mediator, const char* message, User* sender);
} ChatRoomMediator;

// 抽象同事类:结构体+函数指针模拟接口
typedef struct User {
char name[MAX_USER_NAME_LEN]; // 用户名
ChatRoomMediator* mediator; // 关联的中介者
// 用户方法:发送消息、接收消息
void (*send_message)(User* user, const char* message);
void (*receive_message)(User* user, const char* message, const char* sender_name);
} User;

// 具体中介者方法:注册用户
void chat_room_register_user(ChatRoomMediator* mediator, User* user) {
if (mediator == NULL || user == NULL) {
printf("C: 中介者或用户实例无效\n");
return;
}
pthread_mutex_lock(&mediator->mutex);

// 校验用户数量上限和重复注册
if (mediator->user_count >= MAX_USERS) {
printf("C: 聊天室用户数量已达上限,无法注册\n");
pthread_mutex_unlock(&mediator->mutex);
return;
}
for (int i = 0; i < mediator->user_count; i++) {
if (mediator->users[i] == user) {
printf("C: 用户已注册,无需重复操作\n");
pthread_mutex_unlock(&mediator->mutex);
return;
}
}
// 注册用户并绑定中介者
mediator->users[mediator->user_count++] = user;
user->mediator = mediator;
printf("用户【%s】加入聊天室\n", user->name);
pthread_mutex_unlock(&mediator->mutex);
}

// 具体中介者方法:转发消息
void chat_room_send_message(ChatRoomMediator* mediator, const char* message, User* sender) {
if (mediator == NULL || sender == NULL || message == NULL || strlen(message) == 0) {
printf("C: 中介者、发送者或消息内容无效\n");
return;
}
pthread_mutex_lock(&mediator->mutex);

// 转发消息,发送者不接收自己的消息
for (int i = 0; i < mediator->user_count; i++) {
if (mediator->users[i] != sender) {
mediator->users[i]->receive_message(mediator->users[i], message, sender->name);
}
}
pthread_mutex_unlock(&mediator->mutex);
}

// 具体同事类方法:发送消息
void normal_user_send_message(User* user, const char* message) {
if (user == NULL || message == NULL || strlen(message) == 0) {
printf("C: 用户或消息内容无效\n");
return;
}
if (user->mediator == NULL) {
printf("C: 用户【%s】未加入任何聊天室,无法发送消息\n", user->name);
return;
}
// 截取消息,避免内存溢出
char msg[MAX_MESSAGE_LEN];
strncpy(msg, message, MAX_MESSAGE_LEN - 1);
msg[MAX_MESSAGE_LEN - 1] = '\0';

printf("\n%s 发送消息:%s\n", user->name, msg);
user->mediator->send_message(user->mediator, msg, user); // 通过中介者转发
}

// 具体同事类方法:接收消息
void normal_user_receive_message(User* user, const char* message, const char* sender_name) {
if (user == NULL || message == NULL || sender_name == NULL) {
printf("C: 用户、消息或发送者信息无效\n");
return;
}
printf("%s 收到 %s 的消息:%s\n", user->name, sender_name, message);
}

// 工厂函数:创建聊天室(中介者)实例
ChatRoomMediator* create_chat_room() {
ChatRoomMediator* mediator = (ChatRoomMediator*)malloc(sizeof(ChatRoomMediator));
if (mediator == NULL) {
printf("C: 内存分配失败,无法创建聊天室\n");
return NULL;
}
// 初始化用户数组和计数
memset(mediator->users, 0, sizeof(mediator->users));
mediator->user_count = 0;
// 初始化互斥锁
pthread_mutex_init(&mediator->mutex, NULL);
// 绑定中介者方法
mediator->register_user = chat_room_register_user;
mediator->send_message = chat_room_send_message;
return mediator;
}

// 工厂函数:创建普通用户(同事类)实例
User* create_normal_user(const char* name) {
if (name == NULL || strlen(name) == 0 || strlen(name) >= MAX_USER_NAME_LEN) {
printf("C: 用户名无效(为空或长度过长)\n");
return NULL;
}
User* user = (User*)malloc(sizeof(User));
if (user == NULL) {
printf("C: 内存分配失败,无法创建用户\n");
return NULL;
}
// 复制用户名,避免野指针
strncpy(user->name, name, MAX_USER_NAME_LEN - 1);
user->name[MAX_USER_NAME_LEN - 1] = '\0';
user->mediator = NULL;
// 绑定用户方法
user->send_message = normal_user_send_message;
user->receive_message = normal_user_receive_message;
return user;
}

// 释放用户内存
void destroy_user(User* user) {
if (user != NULL) {
free(user);
user = NULL;
}
}

// 释放聊天室内存
void destroy_chat_room(ChatRoomMediator* mediator) {
if (mediator != NULL) {
pthread_mutex_destroy(&mediator->mutex);
free(mediator);
mediator = NULL;
}
}

// 客户端测试代码
int main() {
// 1. 创建中介者(聊天室)
ChatRoomMediator* chat_room = create_chat_room();
if (chat_room == NULL) return -1;

// 2. 创建具体同事类(用户)
User* user1 = create_normal_user("张三");
User* user2 = create_normal_user("李四");
User* user3 = create_normal_user("王五");
if (user1 == NULL || user2 == NULL || user3 == NULL) {
destroy_user(user1);
destroy_user(user2);
destroy_user(user3);
destroy_chat_room(chat_room);
return -1;
}

// 3. 注册用户到聊天室
chat_room->register_user(chat_room, user1);
chat_room->register_user(chat_room, user2);
chat_room->register_user(chat_room, user3);

// 4. 模拟用户发送消息
user1->send_message(user1, "大家好!我是张三,很高兴加入聊天室~");
printf("---\n");
user2->send_message(user2, "张三你好!欢迎加入~");
printf("---\n");
user3->send_message(user3, "大家好呀!");

// 5. 释放内存
destroy_user(user1);
destroy_user(user2);
destroy_user(user3);
destroy_chat_room(chat_room);
return 0;
}

三、中介者模式的优缺点

中介者模式的核心价值是“解耦多对象间的复杂交互,集中管理协调逻辑”,其优缺点均围绕这一核心展开。在实际开发中,需结合业务场景的交互复杂度、对象数量和性能要求,权衡使用,避免过度设计或滥用,确保既发挥其核心优势,又规避潜在问题。

3.1 核心优点

  • 降低系统耦合度,符合迪米特法则:同事类之间不再直接交互,仅通过中介者通信,消除了对象间的网状依赖,将“多对多”耦合转化为“一对多”耦合,大幅提升代码的可维护性和可扩展性,符合“最少知道原则”。

  • 集中管理交互逻辑,简化维护:所有对象间的交互规则都封装在中介者中,无需分散在各个同事类中,修改交互逻辑时仅需修改中介者代码,无需改动同事类,降低维护成本,提升开发效率。

  • 简化同事类设计,聚焦核心业务:同事类无需关注其他同事的实现细节和交互逻辑,只需实现自身的核心业务逻辑,以及与中介者的通信方法,职责单一,代码更简洁、清晰。

  • 提升系统灵活性,便于扩展:新增同事类时,仅需让其实现抽象同事类接口,并注册到中介者中,无需修改原有同事类和中介者的核心逻辑;修改交互规则时,仅需调整中介者的转发逻辑,扩展性极强。

  • 便于调试和问题定位:所有交互都经过中介者转发,可在中介者中统一添加日志、监控等功能,快速定位交互过程中的问题,降低调试难度。

3.2 主要缺点

  • 中介者易成为“上帝类”,复杂度攀升:当系统中同事类数量增多、交互逻辑复杂时,中介者会承担过多的协调职责,导致代码臃肿、逻辑繁琐,难以维护和扩展,违背“单一职责原则”。

  • 存在性能瓶颈风险:所有交互都需经过中介者转发,若中介者处理逻辑复杂(如多条件判断、批量转发),在高频交互场景下,会增加系统响应延迟,成为性能瓶颈。

  • 可能违反开闭原则:若新增的交互规则需要修改中介者的核心逻辑,而非仅扩展中介者,会违反“开闭原则”,增加系统的迭代风险。

  • 中介者依赖过重,容错性降低:中介者是整个交互体系的核心,若中介者出现故障,会导致所有同事类之间的交互中断,系统容错性降低,需额外设计降级、容错机制。

四、中介者模式的使用场景

中介者模式的核心适用场景是“多个对象之间存在频繁、复杂的交互,导致对象间耦合度高、维护困难”的业务场景。以下结合具体场景及典型实战案例,帮助开发者快速判断是否适用,实现精准落地,避免滥用或错用。

4.1 核心适用场景

  • 多组件交互场景:GUI界面中的多组件协同(如窗体中的按钮、输入框、下拉框、弹窗等),通过中介者(窗体控制器)协调组件间的交互,避免组件间直接耦合(如按钮点击后更新输入框内容、隐藏下拉框)。

  • 分布式系统通信场景:微服务架构中,多个微服务之间的通信,通过消息中间件、API网关(中介者)转发请求,避免服务间直接调用,降低服务耦合,提升系统的可扩展性。

  • 多人协作场景:在线聊天室、游戏大厅、协同办公工具等,用户/玩家之间的消息转发、状态同步,通过中介者统一处理,简化多用户间的交互逻辑。

  • 复杂流程协同场景:订单流程、支付流程等,涉及订单、支付、库存、物流、通知等多个模块,通过中介者协调各模块的交互(如支付成功后,通知库存扣减、物流创建、短信推送)。

  • 网络协议交互场景:TCP/IP协议栈中,各层协议(物理层、数据链路层、网络层、传输层)之间的交互,通过中间层(中介者)转发数据,避免各层直接耦合,简化协议实现。

  • 设备联动场景:智能家居系统中,多个设备(灯光、空调、窗帘、传感器)之间的联动,通过网关(中介者)协调设备状态同步和指令转发,避免设备间直接通信。

4.2 典型实战案例

  • 在线聊天室:本文实现的经典案例,多个用户通过聊天室(中介者)发送和接收消息,用户无需直接交互,由聊天室统一转发,简化多用户通信逻辑,降低耦合,同时便于扩展(如新增管理员用户、消息过滤功能)。

  • 电商订单流程:订单创建后,需联动支付、库存、物流、通知等模块,通过订单中介者协调各模块交互:支付模块完成支付后,通知中介者,中介者再分别调用库存扣减、物流创建、短信通知逻辑,各模块无需直接关联,便于单独升级和维护。

  • GUI组件交互:桌面应用的登录窗体,包含用户名输入框、密码输入框、登录按钮、重置按钮、提示框等组件,通过窗体控制器(中介者)协调交互:点击登录按钮时,中介者校验输入框内容,若校验通过则提交登录请求,若失败则通过提示框显示错误信息,组件间无直接依赖。

  • 游戏大厅系统:多人在线游戏中,玩家的加入、退出、聊天、状态同步等操作,通过游戏大厅(中介者)统一处理,玩家无需直接与其他玩家通信,中介者负责转发消息、同步状态,提升系统并发处理能力。

4.3 不适用场景

  • 对象间交互简单、数量少的场景:若仅2-3个对象,且交互逻辑简单,直接通信更简洁,引入中介者会增加系统复杂度,属于过度设计。

  • 高频交互、对性能要求极高的场景:如高频交易系统,中介者的转发逻辑会增加响应延迟,影响系统性能,不建议使用。

  • 交互规则频繁变化的场景:若交互规则频繁迭代,会导致中介者代码频繁修改,违背开闭原则,增加维护成本。

五、总结

中介者模式作为一种核心的行为型设计模式,核心精髓是通过引入中介者对象,将多对象间的网状依赖转化为星型依赖,实现“解耦交互、集中协调”的核心目标。其本质是将分散在多个对象中的交互逻辑,抽取到中介者中统一管理,让同事类聚焦自身核心业务,提升系统的可维护性、可扩展性和可读性。

从多语言实现来看,不同语言的实现方式虽有差异,但核心逻辑高度一致:面向对象语言(C#、Python、Go、C++)可通过接口/抽象类+实现类的方式,自然映射中介者模式的四大核心角色,依托多态、继承等特性简化代码;纯C语言则通过结构体+函数指针模拟面向对象特性,手动实现核心逻辑,虽代码冗余,但底层可控,适配资源受限场景。各语言的实现均需关注边界处理、异常捕获和并发安全,确保代码可运行、高可靠。

在工程实践中,使用中介者模式的关键的是“平衡”:既要发挥其解耦、集中管理的优势,也要规避中介者成为“上帝类”的风险。实践中可通过以下方式优化:一是拆分细粒度中介者,将复杂交互逻辑拆分到多个中介者中,遵循单一职责原则;二是结合其他设计模式,如工厂模式创建中介者和同事类、观察者模式优化中介者与同事类的通信,提升系统灵活性;三是针对高频交互场景,优化中介者转发逻辑,减少性能损耗;四是为中介者设计容错机制,避免核心节点故障导致整个系统瘫痪。

总之,中介者模式是解决多对象复杂交互问题的有效工具,并非所有场景都适用,需结合业务实际权衡取舍。合理使用可使系统结构更清晰、扩展更灵活,滥用则会增加系统复杂度和维护成本。开发者在实际开发中,应先分析对象间的交互复杂度,再决定是否引入中介者模式,确保设计既符合设计原则,又贴合工程实践需求。

观察者模式(Observer Pattern)是一种经典的行为型设计模式,其核心思想是定义对象间的一对多依赖关系,当一个对象(被观察者)的状态发生变化时,所有依赖于它的对象(观察者)都会被自动通知并更新,实现“状态变更-自动通知-同步更新”的闭环逻辑。这种模式将被观察者与观察者解耦,让两者专注于自身核心职责,大幅提升代码的灵活性、可扩展性与可维护性,是处理对象间联动场景的核心设计范式。

观察者模式的核心价值在于“解耦联动逻辑,简化状态同步”,它避免了被观察者与观察者之间的直接耦合,通过抽象接口建立通信桥梁,使得新增观察者或修改观察者逻辑时,无需改动被观察者代码,完美契合“单一职责原则”与“开闭原则”。在实际开发中,诸如消息通知、状态同步、事件监听等场景,均能通过观察者模式高效实现。

一、观察者模式的核心结构

观察者模式的结构清晰,角色分工明确,核心由四个角色构成,各角色协同工作,共同实现“状态变更通知与同步更新”的核心目标,确保联动逻辑的解耦与可扩展:

1.1 被观察者(Subject)

也称为主题(Subject),是状态变化的核心载体,负责维护一组观察者的引用,提供注册(添加观察者)、注销(移除观察者)和通知(状态变更时触发所有观察者更新)的核心方法。被观察者无需知晓具体观察者的实现细节,仅需通过抽象观察者接口与观察者交互。

1.2 抽象观察者(Observer)

定义观察者的统一接口,声明一个更新方法(如Update()),是所有具体观察者的行为契约。当被观察者状态发生变化时,会调用该接口的更新方法,触发观察者执行相应的业务逻辑。

1.3 具体观察者(Concrete Observer)

实现抽象观察者接口,是接收被观察者通知并执行具体更新逻辑的类。它持有被观察者的引用(可选),当收到通知时,根据被观察者的状态变化执行相应的业务操作,完成自身状态的同步或业务逻辑的触发。

1.4 客户端(Client)

负责创建被观察者和具体观察者实例,完成观察者的注册/注销操作,触发被观察者的状态变更。客户端无需关心被观察者与观察者之间的通信细节,仅需通过公开接口完成对象初始化与交互。

核心逻辑链路:客户端 → 实例化被观察者与具体观察者 → 注册观察者到被观察者 → 被观察者状态变更 → 被观察者遍历所有注册的观察者 → 调用观察者的更新方法 → 所有观察者同步更新。整个过程中,被观察者与观察者通过抽象接口通信,完全解耦。

二、多语言实现观察者模式

为便于开发者落地实践,本文以“天气预警系统”为经典案例,实现多语言版本的观察者模式:被观察者为“天气中心”,负责推送天气状态变更;具体观察者为“手机APP”“气象看板”“短信通知”,接收天气变更通知并执行对应逻辑。所有实现均保证完整可运行,贴合各语言设计理念,补充规范注释、异常处理和边界判断,兼顾实用性与可读性,同时体现各语言对观察者模式的适配方式。

2.1 C# 实现(依托接口,贴合面向对象规范)

C# 作为强类型面向对象语言,通过接口严格定义被观察者与观察者的契约,利用泛型提升代码通用性,依托GC自动管理内存,代码结构严谨、可读性高,适配企业级业务系统开发,是观察者模式最常用的实现方式之一。

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
using System;
using System.Collections.Generic;

/// <summary>
/// 抽象观察者接口:定义观察者的更新契约
/// </summary>
public interface IObserver
{
/// <summary>
/// 接收被观察者通知,执行更新逻辑
/// </summary>
/// <param name="weatherInfo">被观察者传递的状态信息(天气信息)</param>
void Update(string weatherInfo);
}

/// <summary>
/// 被观察者(主题):天气中心
/// 负责维护观察者列表,触发状态变更通知
/// </summary>
public class WeatherSubject
{
// 存储注册的观察者列表(线程安全考虑,使用锁机制)
private readonly List<IObserver> _observers = new List<IObserver>();
// 被观察者的核心状态:当前天气信息
private string _currentWeather;

/// <summary>
/// 注册观察者(添加到观察者列表)
/// </summary>
/// <param name="observer">具体观察者实例</param>
public void RegisterObserver(IObserver observer)
{
if (observer == null)
throw new ArgumentNullException(nameof(observer), "观察者实例不可为null");

lock (_observers)
{
if (!_observers.Contains(observer))
_observers.Add(observer);
}
}

/// <summary>
/// 注销观察者(从观察者列表中移除)
/// </summary>
/// <param name="observer">具体观察者实例</param>
public void RemoveObserver(IObserver observer)
{
if (observer == null)
throw new ArgumentNullException(nameof(observer), "观察者实例不可为null");

lock (_observers)
{
_observers.Remove(observer);
}
}

/// <summary>
/// 通知所有注册的观察者(状态变更时触发)
/// </summary>
private void NotifyObservers()
{
lock (_observers)
{
// 遍历所有观察者,调用更新方法
foreach (var observer in _observers)
{
observer.Update(_currentWeather);
}
}
}

/// <summary>
/// 更新天气状态(模拟天气变化)
/// </summary>
/// <param name="weatherInfo">新的天气信息</param>
public void SetWeather(string weatherInfo)
{
if (string.IsNullOrEmpty(weatherInfo))
throw new ArgumentException("天气信息不可为空或空白", nameof(weatherInfo));

// 更新自身状态
_currentWeather = weatherInfo;
// 状态变更后,通知所有观察者
NotifyObservers();
}
}

/// <summary>
/// 具体观察者1:手机APP
/// 接收天气通知,显示天气信息
/// </summary>
public class PhoneAppObserver : IObserver
{
public void Update(string weatherInfo)
{
Console.WriteLine($"C# 手机APP:收到天气更新 - {weatherInfo},已同步显示");
}
}

/// <summary>
/// 具体观察者2:气象看板
/// 接收天气通知,更新看板展示
/// </summary>
public class WeatherBoardObserver : IObserver
{
public void Update(string weatherInfo)
{
Console.WriteLine($"C# 气象看板:收到天气更新 - {weatherInfo},已刷新看板");
}
}

// 客户端测试代码
class Program
{
static void Main()
{
try
{
// 1. 初始化被观察者(天气中心)
var weatherSubject = new WeatherSubject();

// 2. 初始化具体观察者
IObserver phoneApp = new PhoneAppObserver();
IObserver weatherBoard = new WeatherBoardObserver();

// 3. 注册观察者到被观察者
weatherSubject.RegisterObserver(phoneApp);
weatherSubject.RegisterObserver(weatherBoard);

// 4. 模拟天气状态变更,触发通知
Console.WriteLine("C# 模拟天气状态变更:晴转多云");
weatherSubject.SetWeather("晴转多云,气温18-25℃");

// 5. 注销观察者(模拟用户卸载APP)
weatherSubject.RemoveObserver(phoneApp);
Console.WriteLine("\nC# 模拟手机APP注销观察者");

// 6. 再次模拟天气状态变更
Console.WriteLine("C# 模拟天气状态变更:多云转雨");
weatherSubject.SetWeather("多云转雨,气温16-22℃,注意携带雨具");
}
catch (Exception ex)
{
Console.WriteLine($"C# 执行异常:{ex.Message}");
}
}
}

2.2 Python 实现(动态语言特性,简洁灵活)

Python 遵循“鸭子类型”,无需显式定义抽象接口,通过类的方法约定观察者与被观察者的行为,语法简洁灵活,无需繁琐的类型声明,依托GC自动管理内存,适配快速开发、脚本开发及轻量级项目场景,完整保留观察者模式的核心逻辑与解耦特性。

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
# 抽象观察者:通过方法约定行为(无需显式定义接口)
class Observer:
def update(self, weather_info):
"""接收被观察者通知,执行更新逻辑
Args:
weather_info (str): 被观察者传递的天气信息
""" raise NotImplementedError("具体观察者必须重写update方法")

# 被观察者(主题):天气中心
class WeatherSubject:
def __init__(self):
# 存储注册的观察者列表
self._observers = []
# 当前天气状态
self._current_weather = None

def register_observer(self, observer):
"""注册观察者""" if observer is None:
raise ValueError("观察者实例不可为None")
if observer not in self._observers:
self._observers.append(observer)

def remove_observer(self, observer):
"""注销观察者""" if observer in self._observers:
self._observers.remove(observer)

def _notify_observers(self):
"""通知所有注册的观察者(私有方法,内部触发)""" for observer in self._observers:
# 调用观察者的update方法,传递天气信息
observer.update(self._current_weather)

def set_weather(self, weather_info):
"""更新天气状态,触发通知""" if not weather_info or weather_info.strip() == "":
raise ValueError("天气信息不可为空或空白")
self._current_weather = weather_info
print(f"\nPython 模拟天气状态变更:{weather_info.split(',')[0]}")
self._notify_observers()

# 具体观察者1:手机APP
class PhoneAppObserver(Observer):
def update(self, weather_info):
print(f"Python 手机APP:收到天气更新 - {weather_info},已同步显示")

# 具体观察者2:气象看板
class WeatherBoardObserver(Observer):
def update(self, weather_info):
print(f"Python 气象看板:收到天气更新 - {weather_info},已刷新看板")

# 客户端测试代码
if __name__ == "__main__":
try:
# 初始化被观察者和观察者
weather_subject = WeatherSubject()
phone_app = PhoneAppObserver()
weather_board = WeatherBoardObserver()

# 注册观察者
weather_subject.register_observer(phone_app)
weather_subject.register_observer(weather_board)

# 模拟天气变更
weather_subject.set_weather("晴转多云,气温18-25℃")

# 注销观察者
weather_subject.remove_observer(phone_app)
print("Python 模拟手机APP注销观察者")

# 再次模拟天气变更
weather_subject.set_weather("多云转雨,气温16-22℃,注意携带雨具")
except Exception as e:
print(f"Python 执行异常:{str(e)}")

2.3 Go 实现(接口至上,轻量化解耦)

Go 语言无类和继承概念,核心遵循“组合优于继承”的设计哲学,通过接口定义被观察者与观察者的统一契约,结构体实现具体逻辑,代码极简、高效,贴合Go语言“简洁、务实、高性能”的设计理念,适配高并发、高性能的后端开发场景。

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package main

import (
"fmt"
"sync"
)

// 抽象观察者接口:定义更新方法契约
type Observer interface {
Update(weatherInfo string) // 接收天气信息,执行更新
}

// 被观察者(主题):天气中心
type WeatherSubject struct {
observers []Observer // 观察者列表
mu sync.RWMutex // 读写锁,保证并发安全
weather string // 当前天气状态
}

// NewWeatherSubject 工厂函数:创建天气中心实例
func NewWeatherSubject() *WeatherSubject {
return &WeatherSubject{
observers: make([]Observer, 0),
}
}

// RegisterObserver 注册观察者
func (w *WeatherSubject) RegisterObserver(observer Observer) {
if observer == nil {
fmt.Println("Golang: 观察者实例不可为nil")
return
}
w.mu.Lock()
defer w.mu.Unlock()
// 避免重复注册
for _, obs := range w.observers {
if obs == observer {
return
}
}
w.observers = append(w.observers, observer)
}

// RemoveObserver 注销观察者
func (w *WeatherSubject) RemoveObserver(observer Observer) {
if observer == nil {
fmt.Println("Golang: 观察者实例不可为nil")
return
}
w.mu.Lock()
defer w.mu.Unlock()
// 查找并移除观察者
for i, obs := range w.observers {
if obs == observer {
w.observers = append(w.observers[:i], w.observers[i+1:]...)
break
}
}
}

// notifyObservers 通知所有观察者(私有方法)
func (w *WeatherSubject) notifyObservers() {
w.mu.RLock()
defer w.mu.RUnlock()
// 遍历观察者,触发更新
for _, observer := range w.observers {
observer.Update(w.weather)
}
}

// SetWeather 更新天气状态,触发通知
func (w *WeatherSubject) SetWeather(weatherInfo string) {
if weatherInfo == "" {
fmt.Println("Golang: 天气信息不可为空")
return
}
w.mu.Lock()
w.weather = weatherInfo
w.mu.Unlock()
fmt.Printf("\nGolang 模拟天气状态变更:%s\n", weatherInfo)
w.notifyObservers()
}

// 具体观察者1:手机APP
type PhoneAppObserver struct{}

func (p *PhoneAppObserver) Update(weatherInfo string) {
fmt.Printf("Golang 手机APP:收到天气更新 - %s,已同步显示\n", weatherInfo)
}

// 具体观察者2:气象看板
type WeatherBoardObserver struct{}

func (w *WeatherBoardObserver) Update(weatherInfo string) {
fmt.Printf("Golang 气象看板:收到天气更新 - %s,已刷新看板\n", weatherInfo)
}

// 客户端测试代码
func main() {
// 初始化被观察者
weatherSubject := NewWeatherSubject()
// 初始化具体观察者
phoneApp := &PhoneAppObserver{}
weatherBoard := &WeatherBoardObserver{}

// 注册观察者
weatherSubject.RegisterObserver(phoneApp)
weatherSubject.RegisterObserver(weatherBoard)

// 模拟天气变更
weatherSubject.SetWeather("晴转多云,气温18-25℃")

// 注销观察者
weatherSubject.RemoveObserver(phoneApp)
fmt.Println("Golang 模拟手机APP注销观察者")

// 再次模拟天气变更
weatherSubject.SetWeather("多云转雨,气温16-22℃,注意携带雨具")
}

2.4 C++ 实现(抽象类+多态,经典面向对象实现)

C++ 作为经典面向对象语言,通过抽象类(纯虚函数)定义被观察者与观察者的接口,子类实现具体逻辑,依托继承和多态特性实现解耦,需手动管理内存(通过析构函数释放对象),兼顾灵活性与高性能,适配底层开发、高频调用场景,是底层系统、高性能应用的优选实现方式。

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#include <iostream>
#include <vector>
#include <string>
#include <mutex>
using namespace std;

// 抽象观察者类(纯虚函数定义接口)
class Observer {
public:
virtual ~Observer() = default; // 虚析构函数,避免多态场景内存泄漏
/// <summary>
/// 接收被观察者通知,执行更新逻辑
/// </summary>
/// <param name="weatherInfo">天气信息</param>
virtual void Update(const string& weatherInfo) = 0; // 纯虚函数
};

// 被观察者(主题):天气中心
class WeatherSubject {
private:
vector<Observer*> observers; // 存储观察者指针列表
string currentWeather; // 当前天气状态
mutex mtx; // 互斥锁,保证并发安全
public:
~WeatherSubject() {
// 释放观察者指针,避免内存泄漏
for (Observer* obs : observers) {
delete obs;
}
observers.clear();
}

/// <summary>
/// 注册观察者
/// </summary>
/// <param name="observer">观察者指针</param>
void RegisterObserver(Observer* observer) {
if (observer == nullptr) {
throw invalid_argument("观察者实例不可为nullptr");
}
lock_guard<mutex> lock(mtx);
// 避免重复注册
for (Observer* obs : observers) {
if (obs == observer) {
return;
}
}
observers.push_back(observer);
}

/// <summary>
/// 注销观察者
/// </summary>
/// <param name="observer">观察者指针</param>
void RemoveObserver(Observer* observer) {
if (observer == nullptr) {
throw invalid_argument("观察者实例不可为nullptr");
}
lock_guard<mutex> lock(mtx);
// 查找并移除观察者
for (auto it = observers.begin(); it != observers.end(); ++it) {
if (*it == observer) {
delete* it; // 释放观察者内存
observers.erase(it);
break;
}
}
}

/// <summary>
/// 通知所有观察者(私有方法)
/// </summary>
void NotifyObservers() {
lock_guard<mutex> lock(mtx);
for (Observer* obs : observers) {
obs->Update(currentWeather);
}
}

/// <summary>
/// 更新天气状态,触发通知
/// </summary>
/// <param name="weatherInfo">新的天气信息</param>
void SetWeather(const string& weatherInfo) {
if (weatherInfo.empty()) {
throw invalid_argument("天气信息不可为空");
}
currentWeather = weatherInfo;
cout << "\nC++ 模拟天气状态变更:" << weatherInfo << endl;
NotifyObservers();
}
};

// 具体观察者1:手机APP
class PhoneAppObserver : public Observer {
public:
void Update(const string& weatherInfo) override {
cout << "C++ 手机APP:收到天气更新 - " << weatherInfo << ",已同步显示" << endl;
}
};

// 具体观察者2:气象看板
class WeatherBoardObserver : public Observer {
public:
void Update(const string& weatherInfo) override {
cout << "C++ 气象看板:收到天气更新 - " << weatherInfo << ",已刷新看板" << endl;
}
};

// 客户端测试代码
int main() {
try {
// 初始化被观察者
WeatherSubject* weatherSubject = new WeatherSubject();

// 初始化具体观察者并注册
Observer* phoneApp = new PhoneAppObserver();
Observer* weatherBoard = new WeatherBoardObserver();
weatherSubject->RegisterObserver(phoneApp);
weatherSubject->RegisterObserver(weatherBoard);

// 模拟天气变更
weatherSubject->SetWeather("晴转多云,气温18-25℃");

// 注销观察者
weatherSubject->RemoveObserver(phoneApp);
cout << "C++ 模拟手机APP注销观察者" << endl;

// 再次模拟天气变更
weatherSubject->SetWeather("多云转雨,气温16-22℃,注意携带雨具");

// 释放被观察者内存
delete weatherSubject;
}
catch (const exception& e) {
cout << "C++ 执行异常:" << e.what() << endl;
}
return 0;
}

2.5 纯C语言实现(结构体+函数指针,模拟面向对象)

纯C语言无面向对象特性,无类和多态,通过“结构体封装数据+函数指针封装行为”,手动模拟观察者模式的核心逻辑。用结构体封装被观察者和观察者的属性,用函数指针模拟抽象接口的方法,需手动管理内存,代码虽略显冗余,但底层可控性强,适配嵌入式、底层开发等资源受限场景,完美还原“状态联动、解耦通信”的核心思想。

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>

// 定义最大天气信息长度,避免内存溢出
#define MAX_WEATHER_LEN 128
// 定义最大观察者数量,适配资源受限场景
#define MAX_OBSERVERS 10

// 抽象观察者:结构体+函数指针模拟接口
typedef struct {
// 更新方法:接收天气信息,执行更新逻辑
void (*update)(void*, const char*);
// 观察者私有数据(可选)
void* data;
} Observer;

// 被观察者(主题):天气中心
typedef struct {
Observer* observers[MAX_OBSERVERS]; // 观察者数组(固定长度,适配嵌入式)
int observer_count; // 已注册的观察者数量
char current_weather[MAX_WEATHER_LEN]; // 当前天气状态
pthread_mutex_t mutex; // 互斥锁,保证并发安全
// 被观察者方法:注册、注销、通知
void (*register_observer)(void*, Observer*);
void (*remove_observer)(void*, Observer*);
void (*notify_observers)(void*);
void (*set_weather)(void*, const char*);
} WeatherSubject;

// 具体观察者更新方法1:手机APP
void phone_app_update(void* observer, const char* weather_info) {
(void)observer; // 未使用观察者私有数据,仅占位
printf("C 手机APP:收到天气更新 - %s,已同步显示\n", weather_info);
}

// 具体观察者更新方法2:气象看板
void weather_board_update(void* observer, const char* weather_info) {
(void)observer;
printf("C 气象看板:收到天气更新 - %s,已刷新看板\n", weather_info);
}

// 被观察者方法:注册观察者
void weather_subject_register(void* subject, Observer* observer) {
if (subject == NULL || observer == NULL || observer->update == NULL) {
printf("C: 被观察者或观察者实例无效\n");
return;
}
WeatherSubject* sub = (WeatherSubject*)subject;
pthread_mutex_lock(&sub->mutex);

// 检查是否达到最大观察者数量,是否重复注册
if (sub->observer_count >= MAX_OBSERVERS) {
printf("C: 观察者数量已达上限,无法注册\n");
pthread_mutex_unlock(&sub->mutex);
return;
}
for (int i = 0; i < sub->observer_count; i++) {
if (sub->observers[i] == observer) {
printf("C: 观察者已注册,无需重复操作\n");
pthread_mutex_unlock(&sub->mutex);
return;
}
}
// 注册观察者
sub->observers[sub->observer_count++] = observer;
pthread_mutex_unlock(&sub->mutex);
}

// 被观察者方法:注销观察者
void weather_subject_remove(void* subject, Observer* observer) {
if (subject == NULL || observer == NULL) {
printf("C: 被观察者或观察者实例无效\n");
return;
}
WeatherSubject* sub = (WeatherSubject*)subject;
pthread_mutex_lock(&sub->mutex);

// 查找并移除观察者
int index = -1;
for (int i = 0; i < sub->observer_count; i++) {
if (sub->observers[i] == observer) {
index = i;
break;
}
}
if (index == -1) {
printf("C: 未找到该观察者,无法注销\n");
pthread_mutex_unlock(&sub->mutex);
return;
}
// 移动数组,覆盖被移除的观察者
for (int i = index; i < sub->observer_count - 1; i++) {
sub->observers[i] = sub->observers[i + 1];
}
sub->observer_count--;
pthread_mutex_unlock(&sub->mutex);
}

// 被观察者方法:通知所有观察者
void weather_subject_notify(void* subject) {
if (subject == NULL) {
printf("C: 被观察者实例无效\n");
return;
}
WeatherSubject* sub = (WeatherSubject*)subject;
pthread_mutex_lock(&sub->mutex);

// 遍历所有观察者,触发更新方法
for (int i = 0; i < sub->observer_count; i++) {
sub->observers[i]->update(sub->observers[i], sub->current_weather);
}
pthread_mutex_unlock(&sub->mutex);
}

// 被观察者方法:更新天气状态,触发通知
void weather_subject_set_weather(void* subject, const char* weather_info) {
if (subject == NULL || weather_info == NULL || strlen(weather_info) == 0) {
printf("C: 被观察者实例无效或天气信息为空\n");
return;
}
WeatherSubject* sub = (WeatherSubject*)subject;
pthread_mutex_lock(&sub->mutex);

// 复制天气信息,避免野指针
strncpy(sub->current_weather, weather_info, MAX_WEATHER_LEN - 1);
sub->current_weather[MAX_WEATHER_LEN - 1] = '\0';
pthread_mutex_unlock(&sub->mutex);

printf("\nC 模拟天气状态变更:%s\n", sub->current_weather);
weather_subject_notify(sub);
}

// 创建观察者实例
Observer* observer_create(void (*update)(void*, const char*), void* data) {
if (update == NULL) {
printf("C: 观察者更新方法不可为NULL\n");
return NULL;
}
Observer* observer = (Observer*)malloc(sizeof(Observer));
if (observer == NULL) {
printf("C: 内存分配失败,无法创建观察者\n");
return NULL;
}
observer->update = update;
observer->data = data;
return observer;
}

// 创建被观察者实例
WeatherSubject* weather_subject_create() {
WeatherSubject* subject = (WeatherSubject*)malloc(sizeof(WeatherSubject));
if (subject == NULL) {
printf("C: 内存分配失败,无法创建被观察者\n");
return NULL;
}
// 初始化观察者数组和计数
memset(subject->observers, 0, sizeof(subject->observers));
subject->observer_count = 0;
// 初始化互斥锁
pthread_mutex_init(&subject->mutex, NULL);
// 绑定被观察者方法
subject->register_observer = weather_subject_register;
subject->remove_observer = weather_subject_remove;
subject->notify_observers = weather_subject_notify;
subject->set_weather = weather_subject_set_weather;
return subject;
}

// 释放观察者内存
void observer_destroy(Observer* observer) {
if (observer != NULL) {
free(observer);
observer = NULL;
}
}

// 释放被观察者内存
void weather_subject_destroy(WeatherSubject* subject) {
if (subject != NULL) {
pthread_mutex_destroy(&subject->mutex);
free(subject);
subject = NULL;
}
}

// 客户端测试代码
int main() {
// 1. 创建被观察者(天气中心)
WeatherSubject* weather_subject = weather_subject_create();
if (weather_subject == NULL) return -1;

// 2. 创建具体观察者
Observer* phone_app = observer_create(phone_app_update, NULL);
Observer* weather_board = observer_create(weather_board_update, NULL);
if (phone_app == NULL || weather_board == NULL) {
observer_destroy(phone_app);
observer_destroy(weather_board);
weather_subject_destroy(weather_subject);
return -1;
}

// 3. 注册观察者
weather_subject->register_observer(weather_subject, phone_app);
weather_subject->register_observer(weather_subject, weather_board);

// 4. 模拟天气变更
weather_subject->set_weather(weather_subject, "晴转多云,气温18-25℃");

// 5. 注销观察者
weather_subject->remove_observer(weather_subject, phone_app);
printf("C 模拟手机APP注销观察者\n");

// 6. 再次模拟天气变更
weather_subject->set_weather(weather_subject, "多云转雨,气温16-22℃,注意携带雨具");

// 7. 释放内存
observer_destroy(phone_app);
observer_destroy(weather_board);
weather_subject_destroy(weather_subject);
return 0;
}

三、观察者模式的优缺点

观察者模式的核心价值是“解耦对象间的联动逻辑,实现状态同步的自动化”,其优缺点均围绕这一核心展开。在实际开发中,需结合业务场景的复杂度、联动规模和性能要求,权衡使用,避免过度设计或滥用,确保既发挥其核心优势,又规避潜在问题。

3.1 核心优点

  • 解耦性极强,符合开闭原则:被观察者与观察者完全解耦,两者仅通过抽象接口通信,被观察者无需知晓具体观察者的实现,观察者也无需关心被观察者的内部逻辑;新增观察者时,无需修改被观察者代码,扩展成本低。

  • 实现状态联动自动化:被观察者状态变更时,所有注册的观察者会被自动通知并更新,无需手动触发,减少代码冗余,提升开发效率,尤其适用于多对象联动的场景。

  • 观察者可灵活扩展:同一被观察者可注册多个不同的观察者,实现不同的业务逻辑(如天气变更同时触发APP通知、看板更新、短信推送),且观察者之间相互独立,互不影响。

  • 职责单一,代码清晰:被观察者专注于状态管理和通知触发,观察者专注于接收通知并执行自身业务逻辑,符合“单一职责原则”,代码结构清晰,便于维护和迭代。

  • 支持广播通信:被观察者的通知可广播给所有注册的观察者,无需逐一调用,适合需要“一对多”联动的场景(如消息通知、系统告警)。

3.2 主要缺点

  • 观察者过多时,通知效率下降:当被观察者注册的观察者数量过多时,状态变更后通知所有观察者会产生一定的性能开销,导致通知延迟,尤其在高频状态变更场景下更为明显。

  • 可能导致循环依赖:若观察者与被观察者之间相互引用,可能形成循环依赖,导致内存泄漏(如C++、C语言中未及时释放指针,Python中循环引用未被GC回收)。

  • 通知顺序不可控:默认情况下,被观察者会按注册顺序通知观察者,若业务逻辑依赖通知顺序,需额外设计排序机制,增加代码复杂度。

  • 调试难度增加:状态变更的联动链路较长,当出现更新异常时,需逐一排查被观察者和所有观察者的逻辑,定位问题的难度提升,增加调试成本。

  • 可能出现“无效通知”:若观察者仅关注部分状态变更,而被观察者的所有状态变更都会触发通知,会导致观察者接收无效通知,浪费系统资源。

四、观察者模式的使用场景

观察者模式的核心适用场景是“存在一对多对象依赖,且需要在被观察者状态变更时,自动同步所有依赖对象的状态”的业务场景。以下结合具体场景及典型实战案例,帮助开发者快速判断是否适用,实现精准落地,避免滥用或错用。

4.1 核心适用场景

  • 消息通知场景:系统中的消息推送、告警通知、邮件/短信发送等,如用户注册成功后,触发短信通知、邮件验证、积分赠送等多个操作,通过观察者模式实现联动。

  • 状态同步场景:多个组件需要同步同一状态,如电商平台的商品库存变更,同步更新商品详情页、购物车、订单系统的库存显示,避免数据不一致。

  • 事件监听场景:GUI界面的事件响应(如按钮点击、鼠标移动)、系统事件的监听(如日志记录、性能监控),通过观察者模式触发对应的事件处理逻辑。

  • 发布-订阅场景:发布者(被观察者)发布消息,订阅者(观察者)接收消息并处理,如消息队列的发布订阅模式、公众号的推文推送,本质是观察者模式的延伸。

  • 数据更新同步场景:数据库数据变更后,同步更新缓存、索引、统计报表等,如用户信息修改后,同步更新Redis缓存、用户画像统计数据。

  • 插件化扩展场景:系统插件的注册与触发,如IDE的插件机制,插件注册到主程序(被观察者),主程序触发特定事件时,所有插件(观察者)执行对应的逻辑。

4.2 典型实战案例

  • 天气预警系统:本文实现的案例,天气中心(被观察者)推送天气变更,手机APP、气象看板、短信通知(观察者)同步更新,实现多终端天气信息同步。

  • 电商订单状态变更:订单状态从“待支付”变为“已支付”时,触发库存扣减、物流创建、积分增加、消息通知等多个操作,每个操作作为观察者,接收订单状态变更通知并执行。

  • GUI界面事件处理:如桌面应用的按钮点击事件,按钮(被观察者)被点击后,通知所有注册的事件处理器(观察者),执行弹窗、页面跳转、数据提交等逻辑。

  • 消息队列发布订阅:如RabbitMQ的Topic交换机,发布者发布消息(被观察者),所有订阅该主题的消费者(观察者)接收消息并处理,实现消息的广播与解耦。

  • 日志收集系统:系统中各模块(被观察者)产生日志时,通知日志收集器(观察者),日志收集器统一处理日志(存储、分析、告警),无需各模块单独处理日志逻辑。

五、总结

观察者模式的核心是“定义一对多依赖,实现状态变更的自动通知与同步更新”,它是“单一职责原则”和“开闭原则”的典型落地方式,通过抽象接口解耦被观察者与观察者,让两者专注于自身核心职责,大幅提升代码的灵活性、可扩展性和可维护性。其本质是“封装联动逻辑,实现自动化同步”,避免了对象间的直接耦合,简化了多对象联动的实现。

从多语言实现来看,尽管各语言的语法特性、设计理念差异显著,但核心逻辑高度统一,且均能适配自身的语言特性,完整实现观察者模式的核心价值:

  • 面向对象语言(C#、C++):通过接口/抽象类严格遵循观察者模式的四大角色,依托多态特性实现解耦,代码结构规范、可维护性强,适配企业级系统和高性能场景;

  • 动态语言(Python):利用鸭子类型简化接口定义,无需显式声明抽象接口,语法简洁灵活,依托GC自动管理内存,适配快速开发、轻量级项目场景;

  • Go语言:以“接口为核心”,通过接口定义契约,结构体实现具体逻辑,结合读写锁保证并发安全,代码极简高效,贴合高并发、高性能的后端开发需求;

  • 纯C语言:通过结构体+函数指针模拟面向对象特性,手动封装数据和行为,依托互斥锁保证并发安全,虽代码冗余,但底层可控性强,适配嵌入式、底层开发等资源受限场景,完美还原观察者模式“解耦联动”的核心思想。

在工程实践中,使用观察者模式需把握三个核心原则:一是判断业务场景是否存在“一对多”的联动需求,是否需要自动同步状态,简单的一对一联动无需引入观察者模式;二是控制观察者的数量,避免过多观察者导致通知效率下降,高频状态变更场景可优化为异步通知;三是避免循环依赖,合理管理对象生命周期,防止内存泄漏。

总体而言,观察者模式是处理多对象联动、状态同步场景的核心设计工具,尤其在消息通知、事件监听、发布订阅等场景中价值显著。理解观察者模式的核心,不在于照搬代码结构,而在于掌握“解耦联动逻辑、实现自动化同步”的设计思路,合理运用可让代码结构更清晰、职责更单一、扩展更灵活,提升系统的可维护性和复用性。

迭代器模式(Iterator Pattern)是一种经典的行为型设计模式,其核心目标是提供一种标准化方式顺序访问聚合对象中的各个元素,同时不暴露该对象的内部存储结构。它通过将遍历逻辑与集合本身解耦,让集合类专注于数据的存储与管理,迭代器类专注于遍历行为的实现,大幅提升代码的灵活性、可维护性与可扩展性,是处理集合遍历场景的核心设计范式。

迭代器模式的核心价值在于“分离遍历与存储”,它屏蔽了不同集合(如数组、链表、哈希表)的底层实现差异,为外部提供统一的遍历接口,使得调用者无需关心集合的内部结构,只需通过迭代器即可完成元素的遍历,完美契合“单一职责原则”与“开闭原则”。

一、迭代器模式的核心结构

迭代器模式的结构清晰,角色分工明确,核心由四个角色构成,各角色协同工作,共同实现“解耦遍历逻辑与集合存储”的核心目标,确保遍历行为的标准化与集合的封装性:

1.1 抽象迭代器(Iterator)

定义遍历聚合对象的通用接口,是所有具体迭代器的行为契约。通常包含两个核心方法:HasNext()(判断当前位置是否还有下一个元素,返回布尔值)和Next()(获取当前位置的下一个元素,并将遍历指针移动到下一位),部分场景可扩展Reset()(重置遍历指针)、Remove()(移除当前元素)等方法。

1.2 具体迭代器(Concrete Iterator)

实现抽象迭代器接口,是迭代器模式的核心执行类。它内部持有对应具体聚合对象的引用,维护当前遍历的位置指针,负责完成实际的遍历逻辑,同时处理迭代边界(如越界判断),确保遍历的安全性。

1.3 抽象聚合类(Aggregate)

定义聚合对象的通用接口,声明集合的核心行为(如添加、删除元素),并提供一个创建迭代器的抽象方法(如CreateIterator())。抽象聚合类的核心作用是规范具体聚合类的行为,同时为外部提供获取迭代器的统一入口。

1.4 具体聚合类(Concrete Aggregate)

实现抽象聚合类的接口,是被遍历的目标集合,负责存储具体的元素数据(如数组、链表等底层结构)。它会返回一个与自身匹配的具体迭代器实例,将遍历逻辑委托给迭代器,自身仅专注于数据的存储与管理。

核心逻辑链路:客户端 → 实例化具体聚合类(存储数据) → 调用聚合类的CreateIterator()方法获取具体迭代器 → 通过迭代器的HasNext()Next()方法遍历元素 → 完成遍历。整个过程中,客户端无需关注集合的内部结构,仅与迭代器接口交互。

二、多语言实现迭代器模式

为便于开发者落地实践,本文以“字符串集合遍历”为经典案例,实现多语言版本的迭代器模式,覆盖面向对象语言、动态语言、面向过程语言及Go语言的特色实现。所有实现均保证完整可运行,贴合各语言设计理念,补充规范注释、异常处理和边界判断,兼顾实用性与可读性,同时体现各语言对迭代器模式的适配方式。

2.1 C# 实现(依托内置接口,贴合语言特性)

C# 语言内置了IEnumerator(抽象迭代器)和IEnumerable(抽象聚合类)接口,天然适配迭代器模式,开发者可直接复用内置接口实现,无需自定义抽象层,代码简洁且符合.NET框架规范,适配企业级系统开发。

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
98
99
100
101
102
103
104
105
106
107
108
109
using System;
using System.Collections;

/// <summary>
/// 具体迭代器:实现系统内置抽象迭代器接口IEnumerator
/// 负责字符串集合的遍历逻辑,维护遍历位置,处理边界判断
/// </summary>
public class StringIterator : IEnumerator
{
// 持有具体聚合对象的元素数组
private readonly string[] _items;
// 当前遍历位置指针(初始值为-1,代表未开始遍历)
private int _position = -1;

/// <summary>
/// 初始化迭代器,绑定要遍历的集合元素
/// </summary>
/// <param name="items">字符串集合数组</param>
public StringIterator(string[] items)
{
_items = items ?? throw new ArgumentNullException(nameof(items), "遍历的集合不可为null");
}

/// <summary>
/// 获取当前位置的元素
/// </summary>
public object Current
{
get
{
// 处理越界异常,避免非法访问
if (_position < 0 || _position >= _items.Length)
{
throw new InvalidOperationException("迭代器位置非法,无法获取当前元素");
}
return _items[_position];
}
}

/// <summary>
/// 移动到下一个元素,返回是否存在下一个元素
/// </summary>
/// <returns>是否存在下一个元素</returns>
public bool MoveNext()
{
_position++;
// 判断当前位置是否在集合范围内
return _position < _items.Length;
}

/// <summary>
/// 重置迭代器位置,重新开始遍历
/// </summary>
public void Reset()
{
_position = -1;
}
}

/// <summary>
/// 具体聚合类:实现系统内置抽象聚合接口IEnumerable
/// 负责存储字符串元素,提供创建迭代器的方法
/// </summary>
public class StringCollection : IEnumerable
{
// 底层存储元素的数组
private readonly string[] _items;

/// <summary>
/// 初始化字符串集合
/// </summary>
/// <param name="items">要存储的字符串数组</param>
public StringCollection(string[] items)
{
_items = items ?? throw new ArgumentNullException(nameof(items), "集合元素不可为null");
}

/// <summary>
/// 创建并返回对应的具体迭代器实例
/// </summary>
/// <returns>字符串集合迭代器</returns>
public IEnumerator GetEnumerator()
{
return new StringIterator(_items);
}
}

// 客户端测试代码
class Program
{
static void Main()
{
try
{
// 初始化具体聚合对象(存储数据)
var collection = new StringCollection(new[] { "C#", "迭代器模式", "设计模式" });
// 借助foreach遍历(C# foreach本质是调用迭代器的MoveNext和Current)
Console.WriteLine("C# 迭代器遍历结果:");
foreach (var item in collection)
{
Console.WriteLine(item);
}
}
catch (Exception ex)
{
Console.WriteLine($"遍历异常:{ex.Message}");
}
}
}

2.2 Python 实现(依托迭代器协议,简洁高效)

Python 不强制要求定义抽象接口,而是通过“迭代器协议”(实现__iter____next__方法)实现迭代器功能,语法简洁灵活,无需繁琐的接口声明,依托GC自动管理内存,适配快速开发、脚本开发及轻量级项目场景,完美贴合Python“简洁实用”的设计理念。

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
# 具体迭代器:实现Python迭代器协议(__iter__和__next__方法)
class StringIterator:
def __init__(self, items):
"""初始化迭代器""" # 绑定要遍历的集合元素
self._items = items if items is not None else []
# 当前遍历位置指针(初始值为0,Python中数组下标从0开始)
self._index = 0

def __iter__(self):
"""迭代器协议要求:返回自身,支持for-in遍历""" return self

def __next__(self):
"""迭代器协议要求:获取下一个元素,无元素时抛出StopIteration终止遍历""" if self._index < len(self._items):
# 获取当前元素并移动指针
result = self._items[self._index]
self._index += 1
return result
# 迭代终止标志,for-in会自动捕获该异常并结束遍历
raise StopIteration("迭代已结束,无更多元素")

# 具体聚合类:实现__iter__方法,返回迭代器实例
class StringCollection:
def __init__(self, items):
"""初始化字符串集合""" self._items = items if items is not None else []

def __iter__(self):
"""返回具体迭代器实例,供外部遍历""" return StringIterator(self._items)

# 客户端测试代码
if __name__ == "__main__":
try:
# 初始化集合并遍历
collection = StringCollection(["Python", "迭代器模式", "设计模式"])
print("Python 迭代器遍历结果:")
for item in collection:
print(item)
except StopIteration as e:
print(f"迭代异常:{e}")
except Exception as e:
print(f"其他异常:{str(e)}")

2.3 Go 实现(接口+结构体,轻量化实现)

Go 语言无类和继承概念,核心遵循“组合优于继承”的设计哲学,通过接口定义迭代器和聚合类的契约,结构体实现具体的迭代器和聚合逻辑,代码极简、高效,贴合Go语言“简洁、务实、高性能”的设计理念,适配高并发、高性能的后端开发场景。

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
package main

import (
"fmt"
)

// 抽象迭代器接口:定义遍历的统一契约
type Iterator interface {
HasNext() bool // 判断是否有下一个元素
Next() string // 获取下一个元素
Reset() // 重置迭代器位置
}

// 具体迭代器:字符串集合迭代器
type StringIterator struct {
items []string // 要遍历的元素数组
index int // 当前遍历位置指针
}

// NewStringIterator 工厂函数:创建字符串迭代器实例
func NewStringIterator(items []string) *StringIterator {
if items == nil {
items = make([]string, 0)
}
return &StringIterator{
items: items,
index: 0,
}
}

// HasNext 判断是否有下一个元素
func (it *StringIterator) HasNext() bool {
return it.index < len(it.items)
}

// Next 获取下一个元素,若无元素则返回空字符串
func (it *StringIterator) Next() string {
if it.HasNext() {
item := it.items[it.index]
it.index++
return item
}
fmt.Println("迭代已结束,无更多元素")
return ""
}

// Reset 重置迭代器位置,重新开始遍历
func (it *StringIterator) Reset() {
it.index = 0
}

// 抽象聚合类接口:定义创建迭代器的契约
type Aggregate interface {
CreateIterator() Iterator // 创建迭代器
}

// 具体聚合类:字符串集合
type StringCollection struct {
items []string // 底层存储元素的数组
}

// NewStringCollection 工厂函数:创建字符串集合实例
func NewStringCollection(items []string) *StringCollection {
if items == nil {
items = make([]string, 0)
}
return &StringCollection{
items: items,
}
}

// CreateIterator 创建并返回迭代器实例
func (c *StringCollection) CreateIterator() Iterator {
return NewStringIterator(c.items)
}

// 客户端测试代码
func main() {
// 初始化具体聚合对象
collection := NewStringCollection([]string{"Go", "迭代器模式", "设计模式"})
// 获取迭代器
iterator := collection.CreateIterator()

fmt.Println("Go 迭代器遍历结果:")
// 遍历元素
for iterator.HasNext() {
fmt.Println(iterator.Next())
}

// 重置迭代器并重新遍历
iterator.Reset()
fmt.Println("Go 迭代器重置后遍历结果:")
for iterator.HasNext() {
fmt.Println(iterator.Next())
}
}

2.4 C++ 实现(抽象类+模板,通用化设计)

C++ 作为经典面向对象语言,通过抽象类(纯虚函数)定义迭代器和聚合类的接口,结合模板机制实现通用化设计,支持多种数据类型的遍历,兼顾灵活性与高性能,需手动管理内存,适配底层开发、高频调用场景,是底层系统、高性能应用的优选实现方式。

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
98
99
100
101
102
103
104
105
106
107
108
109
#include <iostream>
#include <vector>
#include <string>
#include <stdexcept>
using namespace std;

// 抽象迭代器(模板类,支持任意数据类型)
template <typename T>
class Iterator {
public:
virtual ~Iterator() = default; // 虚析构函数,避免多态场景下内存泄漏
virtual bool HasNext() = 0; // 纯虚函数:判断是否有下一个元素
virtual T Next() = 0; // 纯虚函数:获取下一个元素
virtual void Reset() = 0; // 纯虚函数:重置迭代器位置
};

// 具体迭代器(模板类,适配字符串、int等多种类型)
template <typename T>
class ConcreteIterator : public Iterator<T> {
private:
vector<T> items; // 存储要遍历的元素
int index; // 当前遍历位置指针
public:
// 初始化迭代器,绑定集合元素
ConcreteIterator(const vector<T>& items) : items(items), index(0) {}

// 判断是否有下一个元素
bool HasNext() override {
return index < items.size();
}

// 获取下一个元素,越界时抛出异常
T Next() override {
if (HasNext()) {
return items[index++];
}
throw out_of_range("迭代器越界,无更多元素");
}

// 重置迭代器位置
void Reset() override {
index = 0;
}
};

// 抽象聚合类(模板类,支持任意数据类型)
template <typename T>
class Aggregate {
public:
virtual ~Aggregate() = default;
virtual Iterator<T>* CreateIterator() = 0; // 纯虚函数:创建迭代器
virtual void Add(const T& item) = 0; // 纯虚函数:添加元素
};

// 具体聚合类(模板类,字符串集合)
template <typename T>
class ConcreteAggregate : public Aggregate<T> {
private:
vector<T> items; // 底层存储元素的vector容器
public:
// 添加元素到集合
void Add(const T& item) override {
items.push_back(item);
}

// 创建并返回具体迭代器实例
Iterator<T>* CreateIterator() override {
return new ConcreteIterator<T>(items);
}
};

// 客户端测试代码
int main() {
try {
// 初始化具体聚合对象(字符串类型)
ConcreteAggregate<string> collection;
// 向集合中添加元素
collection.Add("C++");
collection.Add("迭代器模式");
collection.Add("设计模式");

// 创建迭代器
Iterator<string>* iterator = collection.CreateIterator();
cout << "C++ 迭代器遍历结果:" << endl;

// 遍历元素
while (iterator->HasNext()) {
cout << iterator->Next() << endl;
}

// 重置迭代器并重新遍历
iterator->Reset();
cout << "C++ 迭代器重置后遍历结果:" << endl;
while (iterator->HasNext()) {
cout << iterator->Next() << endl;
}

// 释放内存,避免内存泄漏
delete iterator;
}
catch (const out_of_range& e) {
cout << "迭代异常:" << e.what() << endl;
}
catch (const exception& e) {
cout << "其他异常:" << e.what() << endl;
}

return 0;
}

2.5 纯C语言实现(结构体+函数指针,模拟实现)

纯C语言无面向对象特性,无类和多态,通过“结构体封装数据+函数指针封装行为”,手动模拟迭代器模式的核心逻辑。用结构体封装聚合对象和迭代器的属性,用函数指针模拟抽象接口的方法,需手动管理内存,代码虽略显冗余,但底层可控性强,适配嵌入式、底层开发等资源受限场景,完美还原“遍历与存储解耦”的核心思想。

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 定义最大字符串长度,避免内存溢出
#define MAX_STR_LEN 32

// 抽象迭代器:用函数指针模拟接口方法
typedef struct {
char** items; // 遍历的元素数组(字符串指针数组)
int length; // 数组长度
int index; // 当前遍历位置指针
int (*HasNext)(void*); // 函数指针:判断是否有下一个元素
char* (*Next)(void*); // 函数指针:获取下一个元素
void (*Reset)(void*); // 函数指针:重置迭代器位置
} Iterator;

// 具体迭代器方法:判断是否有下一个元素
int has_next(void* iterator) {
if (iterator == NULL) {
printf("迭代器实例为空,无法判断是否有下一个元素\n");
return 0;
}
Iterator* it = (Iterator*)iterator;
return it->index < it->length ? 1 : 0;
}

// 具体迭代器方法:获取下一个元素
char* next(void* iterator) {
if (iterator == NULL) {
printf("迭代器实例为空,无法获取元素\n");
return NULL;
}
Iterator* it = (Iterator*)iterator;
if (has_next(it)) {
// 申请内存存储返回的字符串,避免野指针
char* item = (char*)malloc(MAX_STR_LEN * sizeof(char));
if (item == NULL) {
printf("内存分配失败,无法获取元素\n");
return NULL;
}
strncpy(item, it->items[it->index], MAX_STR_LEN - 1);
item[MAX_STR_LEN - 1] = '\0'; // 确保字符串结束符
it->index++;
return item;
}
printf("迭代已结束,无更多元素\n");
return NULL;
}

// 具体迭代器方法:重置迭代器位置
void reset(void* iterator) {
if (iterator == NULL) {
printf("迭代器实例为空,无法重置\n");
return;
}
Iterator* it = (Iterator*)iterator;
it->index = 0;
}

// 抽象聚合类:用结构体+函数指针模拟接口
typedef struct {
char** items; // 存储元素的字符串指针数组
int length; // 数组长度
Iterator* (*CreateIterator)(void*); // 函数指针:创建迭代器
} Aggregate;

// 具体聚合类方法:创建迭代器实例
Iterator* create_iterator(void* aggregate) {
if (aggregate == NULL) {
printf("聚合对象为空,无法创建迭代器\n");
return NULL;
}
Aggregate* agg = (Aggregate*)aggregate;
// 申请迭代器内存
Iterator* it = (Iterator*)malloc(sizeof(Iterator));
if (it == NULL) {
printf("内存分配失败,无法创建迭代器\n");
return NULL;
}
// 绑定聚合对象的元素和长度
it->items = agg->items;
it->length = agg->length;
it->index = 0;
// 绑定迭代器方法(函数指针赋值)
it->HasNext = has_next;
it->Next = next;
it->Reset = reset;
return it;
}

// 释放迭代器内存(避免内存泄漏)
void iterator_destroy(Iterator* it) {
if (it != NULL) {
free(it);
it = NULL;
}
}

// 客户端测试代码
int main() {
// 1. 初始化集合数据(字符串数组)
char* items[] = {"C语言", "迭代器模式", "设计模式"};
int len = sizeof(items) / sizeof(char*);

// 2. 初始化聚合对象
Aggregate aggregate;
aggregate.items = items;
aggregate.length = len;
aggregate.CreateIterator = create_iterator;

// 3. 创建迭代器
Iterator* iterator = aggregate.CreateIterator(&aggregate);
if (iterator == NULL) {
return -1;
}

// 4. 遍历元素
printf("C语言 迭代器遍历结果:\n");
char* item = NULL;
while (iterator->HasNext(iterator)) {
item = iterator->Next(iterator);
if (item != NULL) {
printf("%s\n", item);
free(item); // 释放获取元素时申请的内存
item = NULL;
}
}

// 5. 重置迭代器并重新遍历
iterator->Reset(iterator);
printf("C语言 迭代器重置后遍历结果:\n");
while (iterator->HasNext(iterator)) {
item = iterator->Next(iterator);
if (item != NULL) {
printf("%s\n", item);
free(item);
item = NULL;
}
}

// 6. 释放迭代器内存
iterator_destroy(iterator);
return 0;
}

三、迭代器模式的优缺点

迭代器模式的核心价值是“解耦遍历逻辑与集合存储,提供统一遍历接口”,其优缺点均围绕这一核心展开。在实际开发中,需结合集合的复杂度、遍历场景的需求和性能要求,权衡使用,避免过度设计或滥用,确保既发挥其核心优势,又规避潜在问题。

3.1 核心优点

  • 解耦遍历与集合,符合单一职责原则:集合类专注于数据的存储与管理,迭代器类专注于遍历逻辑的实现,两者职责分离,降低代码耦合度,便于后期维护和迭代。

  • 统一遍历接口,屏蔽底层差异:无论集合的底层存储结构(数组、链表、哈希表、树等)如何,外部都通过统一的迭代器接口遍历元素,调用者无需关心集合的内部实现,降低使用成本,提升代码的通用性。

  • 支持多态遍历,扩展灵活:可针对同一集合实现多种迭代器(如正序遍历、逆序遍历、过滤遍历、深度优先遍历),无需修改集合本身,新增遍历逻辑只需新增迭代器,符合“开闭原则”。

  • 简化集合扩展,提升系统可维护性:新增集合类型时,只需实现对应的迭代器,无需修改已有遍历代码,降低扩展成本;同时,遍历逻辑的修改仅需调整迭代器,不影响集合和调用者。

  • 保护集合封装性:迭代器仅暴露遍历接口,不暴露集合的内部存储结构和操作细节,避免外部直接操作集合内部数据,提升代码的安全性和封装性。

3.2 主要缺点

  • 增加代码复杂度,冗余度提升:对于简单集合(如小型数组),直接使用下标遍历或语言内置遍历方式即可,引入迭代器会增加额外的类/结构体、接口和方法定义,导致代码冗余,增加开发和维护成本。

  • 存在轻微性能开销:相比直接遍历集合(如数组下标访问),迭代器的方法调用(如HasNext()Next())会带来轻微的运行时开销,对于高频遍历、低延迟要求的场景(如底层算法、高频数据处理),可能影响性能。

  • 双向迭代/随机访问实现复杂:基础迭代器仅支持单向顺序遍历,若需实现双向迭代(向前/向后遍历)或随机访问(直接访问指定位置元素),需扩展迭代器接口,增加设计复杂度和实现成本。

  • 多线程场景下需额外处理同步:在多线程并发遍历集合时,若集合发生修改(添加、删除元素),迭代器可能出现遍历异常(如 ConcurrentModificationException),需额外添加同步机制,增加代码复杂度。

四、迭代器模式的使用场景

迭代器模式的核心适用场景是“需要统一遍历接口、隐藏集合内部结构,或需要灵活扩展遍历逻辑”的业务场景。以下结合具体场景及典型实战案例,帮助开发者快速判断是否适用,实现精准落地,避免滥用或错用。

4.1 核心适用场景

  • 隐藏集合内部结构的场景:当需要对外屏蔽集合的底层实现(如自定义容器、复杂数据结构、第三方集合库),仅暴露遍历能力,避免外部依赖集合的内部结构时,如框架级别的集合组件、自定义数据容器。

  • 统一多集合遍历方式的场景:系统中存在多种集合类型(数组、链表、哈希表、树等),需为调用者提供一致的遍历接口,降低调用者的使用成本,如通用工具类、集合框架(如C#的ICollection、Java的Collection)。

  • 自定义遍历逻辑的场景:需要实现特殊遍历规则(如过滤元素、逆序遍历、深度优先/广度优先遍历树/图结构、分页遍历),且遍历逻辑可能频繁变化时,如数据筛选工具、树形结构遍历、分页查询结果遍历。

  • 跨语言/框架的集合操作场景:在多语言协作或通用框架中,需标准化集合遍历行为,确保不同语言、不同组件的遍历接口一致,如跨语言RPC框架中的集合传输、通用数据处理组件。

  • 集合扩展频繁的场景:系统中频繁新增集合类型,且需要保证遍历接口的一致性,无需修改已有遍历代码,如插件化系统中的集合扩展、动态数据结构的遍历。

4.2 典型实战案例

  • 编程语言内置集合框架:如C#的IEnumerable/IEnumerator、Java的Iterator/Iterable、C++的STL迭代器、Python的迭代器协议,均是迭代器模式的经典实现,为开发者提供统一的集合遍历接口。

  • 数据库查询结果集遍历:如JDBC的ResultSet、ORM框架(MyBatis、EF Core)的查询结果迭代器,将数据库查询结果封装为集合,通过迭代器逐行遍历,屏蔽数据库底层存储差异。

  • 树形结构/图结构遍历:如二叉树的前序、中序、后序遍历,图的深度优先(DFS)、广度优先(BFS)遍历,通过自定义迭代器实现不同的遍历逻辑,无需修改树/图的核心结构。

  • 分页数据遍历:如电商平台的商品列表分页、后台系统的订单分页查询,通过迭代器封装分页逻辑,调用者只需通过迭代器获取下一页数据,无需关心分页的实现细节。

  • 自定义容器开发:如开发自定义的缓存容器、队列、栈等数据结构,通过迭代器暴露遍历能力,隐藏容器的内部存储结构(如数组、链表),提升容器的封装性和可扩展性。

五、总结

迭代器模式的核心是“分离遍历逻辑与数据存储,提供统一的遍历接口”,它是“单一职责原则”和“开闭原则”的典型落地方式,通过将遍历行为抽离为独立的迭代器,让集合专注于数据管理,迭代器专注于遍历实现,大幅提升代码的灵活性、可维护性和可扩展性。其本质是“封装遍历变化”,屏蔽不同集合的底层差异,为外部提供标准化的遍历体验。

从多语言实现来看,尽管各语言的语法特性、设计理念差异显著,但核心逻辑高度统一,且均能适配自身的语言特性,完整实现迭代器模式的核心价值:

  • 面向对象语言(C#、C++):通过接口/抽象类严格遵循迭代器模式的四大角色,结合语言内置特性(如C#的IEnumerable、C++的模板)实现通用化、规范化的迭代器,贴合经典设计范式,适配企业级系统和高性能场景;

  • 动态语言(Python):利用原生迭代器协议简化实现,无需显式定义抽象接口,更注重实用性和简洁性,通过__iter____next__方法快速实现迭代器功能,适配快速开发场景;

  • Go语言:以“接口为核心”,通过接口定义迭代器和聚合类的契约,结构体实现具体逻辑,轻量化实现解耦目标,贴合高并发、高性能的后端开发需求,代码简洁高效;

  • 纯C语言:通过结构体+函数指针模拟面向对象特性,手动封装数据和遍历行为,虽代码冗余,但底层可控性强,适配嵌入式、底层开发等资源受限场景,完美还原迭代器模式“解耦遍历与存储”的核心思想。

在工程实践中,使用迭代器模式需把握三个核心原则:一是避免过度设计,对于简单集合(如小型数组),直接使用语言内置的遍历方式即可,无需引入迭代器;二是权衡性能与灵活性,高频遍历、低延迟的场景需谨慎使用,或选择轻量级迭代器实现;三是注重接口设计,确保迭代器接口的通用性和扩展性,便于后续新增遍历逻辑和集合类型。

总体而言,迭代器模式是处理集合遍历场景的核心设计工具,尤其在集合类型多样、遍历逻辑复杂、需要隐藏内部结构的场景中价值显著。理解迭代器模式的核心,不在于照搬代码结构,而在于掌握“遍历逻辑与数据存储解耦”的设计思路,合理运用可让代码结构更清晰、职责更单一、扩展更灵活,提升系统的可维护性和复用性。

命令模式(Command Pattern)是一种经典的行为型设计模式,其核心思想是将请求封装为独立的命令对象,通过命令对象作为中介,实现请求发起者(调用者)与请求执行者(接收者)的解耦。这种设计不仅允许使用不同的请求参数化对象,还支持命令的队列执行、批量处理、撤销与重做,以及日志记录与系统恢复,是提升代码扩展性、灵活性和可维护性的重要设计手段。

命令模式的核心价值在于“行为封装与解耦”——它将“做什么”(命令)与“谁来做”(接收者)、“何时做”(调用者)分离,让调用者无需关注接收者的具体实现,只需触发命令执行即可,完美契合“开闭原则”,为系统的扩展和维护提供了极大便利。

一、命令模式的核心结构

命令模式的结构清晰,角色分工明确,通常包含5个核心角色,各角色协同工作,构建起“请求发起→命令传递→业务执行”的完整链路,确保解耦性和扩展性:

1.1 抽象命令(Command)

定义命令执行的统一接口,是所有具体命令的通用契约,通常仅包含一个核心执行方法(如Execute()),部分场景可扩展撤销(Undo())、重做(Redo())等方法,规范命令的行为标准。

1.2 具体命令(Concrete Command)

实现抽象命令接口,是命令模式的核心实现类。它内部持有接收者的引用,在Execute()方法中调用接收者的具体业务方法,完成命令的实际执行,同时可封装命令执行所需的参数,实现请求的参数化。

1.3 接收者(Receiver)

真正执行命令核心业务逻辑的类,包含具体的业务操作(如文件读写、数据计算、接口调用等)。接收者不关心命令的存在,仅专注于自身业务逻辑的实现,被具体命令调用以完成请求。

1.4 调用者/请求者(Invoker)

负责调用命令对象执行请求,是命令的发起入口。它不直接与接收者交互,仅持有命令对象(可单个或多个),提供触发命令执行的方法,还可实现命令的队列管理、批量执行等功能。

1.5 客户端(Client)

负责创建具体命令对象,绑定命令与接收者的关联关系,将命令对象传递给调用者,是整个命令模式的初始化入口。客户端决定创建哪些命令、绑定哪些接收者,以及如何触发命令执行。

核心逻辑链路:客户端 → 构建具体命令(绑定接收者) → 调用者接收命令 → 调用者触发命令执行 → 具体命令调用接收者业务方法 → 完成请求。整个链路中,调用者与接收者完全解耦,仅通过命令对象交互。

二、多语言实现命令模式

为便于开发者落地实践,本文以“文件操作(创建文件)”为经典案例,实现多语言版本的命令模式:接收者负责文件创建的核心业务,具体命令封装文件创建请求,调用者触发命令执行,清晰呈现代理模式“解耦与封装”的核心特性。所有实现均保证完整可运行,贴合各语言设计理念,添加规范注释,兼顾实用性与可读性,同时补充异常处理细节,提升代码健壮性。

2.1 C# 实现(面向对象标准实现)

C# 作为强类型面向对象语言,通过接口定义抽象命令,类实现具体命令与调用者,依托对象组合绑定接收者,借助GC自动管理内存,代码结构严谨、可读性高,适配企业级业务系统开发,是命令模式最常用的实现方式之一。

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
using System;
using System.IO;

// 接收者:文件操作类(负责核心业务逻辑)
public class FileReceiver
{
/// <summary>
/// 核心业务方法:创建文件
/// </summary>
/// <param name="fileName">文件名称(含路径)</param>
public void CreateFile(string fileName)
{
try
{
if (!File.Exists(fileName))
{
// 创建文件并及时关闭,避免资源泄漏
using (FileStream stream = File.Create(fileName)) { }
Console.WriteLine($"C#: 接收者成功创建文件 - {fileName}");
}
else
{
Console.WriteLine($"C#: 文件 {fileName} 已存在,无需重复创建");
}
}
catch (Exception ex)
{
Console.WriteLine($"C#: 创建文件失败,异常信息:{ex.Message}");
}
}
}

// 抽象命令接口(定义命令执行的统一契约)
public interface ICommand
{
/// <summary>
/// 命令执行方法
/// </summary>
void Execute();
}

// 具体命令:创建文件命令(封装请求与接收者关联)
public class CreateFileCommand : ICommand
{
// 持有接收者引用,通过构造函数注入,实现解耦
private readonly FileReceiver _receiver;
// 命令执行所需参数
private readonly string _fileName;

/// <summary>
/// 初始化创建文件命令
/// </summary>
/// <param name="receiver">文件操作接收者</param>
/// <param name="fileName">要创建的文件名称</param>
public CreateFileCommand(FileReceiver receiver, string fileName)
{
_receiver = receiver ?? throw new ArgumentNullException(nameof(receiver), "接收者实例不可为null");
_fileName = fileName ?? throw new ArgumentNullException(nameof(fileName), "文件名称不可为null");
}

/// <summary>
/// 执行命令:调用接收者的核心业务方法
/// </summary>
public void Execute()
{
_receiver.CreateFile(_fileName);
}
}

// 调用者:命令触发与管理(不直接依赖接收者)
public class CommandInvoker
{
// 持有当前要执行的命令
private ICommand _command;

/// <summary>
/// 设置要执行的命令
/// </summary>
/// <param name="command">具体命令实例</param>
public void SetCommand(ICommand command)
{
_command = command;
}

/// <summary>
/// 触发命令执行
/// </summary>
public void ExecuteCommand()
{
if (_command != null)
{
_command.Execute();
}
else
{
Console.WriteLine("C#: 未设置任何命令,无法执行");
}
}
}

// 客户端(初始化命令、绑定接收者、触发执行)
class Program
{
static void Main(string[] args)
{
// 1. 初始化接收者(核心业务执行者)
FileReceiver receiver = new FileReceiver();
// 2. 初始化具体命令(绑定接收者与参数)
ICommand createCommand = new CreateFileCommand(receiver, "csharp_file.txt");
// 3. 初始化调用者(命令触发者)
CommandInvoker invoker = new CommandInvoker();

// 4. 执行命令
invoker.SetCommand(createCommand);
invoker.ExecuteCommand();
}
}

2.2 Python 实现(动态语言简洁实现)

Python 遵循“鸭子类型”,无需显式定义接口,通过类的继承关系实现抽象命令逻辑,语法简洁灵活,无需繁琐的类型声明,依托GC自动管理内存,适配快速开发、脚本开发及轻量级项目场景,完整保留命令模式的核心逻辑与解耦特性。

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
import os

# 接收者:文件操作类(核心业务逻辑实现)
class FileReceiver:
def create_file(self, file_name):
"""核心业务方法:创建文件""" try:
if not os.path.exists(file_name):
# 以写入模式创建文件,自动关闭资源
with open(file_name, "w", encoding="utf-8") as f:
pass
print(f"Python: 接收者成功创建文件 - {file_name}")
else:
print(f"Python: 文件 {file_name} 已存在,无需重复创建")
except Exception as e:
print(f"Python: 创建文件失败,异常信息:{str(e)}")

# 抽象命令(基类,定义统一执行接口)
class Command:
def execute(self):
"""命令执行抽象方法,子类必须实现""" raise NotImplementedError("子类必须重写execute方法,实现命令逻辑")

# 具体命令:创建文件命令(封装请求与接收者)
class CreateFileCommand(Command):
def __init__(self, receiver, file_name):
"""初始化创建文件命令""" self.receiver = receiver # 持有接收者引用
self.file_name = file_name # 命令执行参数

def execute(self):
"""执行命令:调用接收者的核心业务方法""" self.receiver.create_file(self.file_name)

# 调用者:命令触发与管理
class CommandInvoker:
def __init__(self):
self._command = None # 持有当前命令

def set_command(self, command):
"""设置要执行的命令""" self._command = command

def execute_command(self):
"""触发命令执行""" if self._command:
self._command.execute()
else:
print("Python: 未设置任何命令,无法执行")

# 客户端调用
if __name__ == "__main__":
# 初始化接收者、命令、调用者
receiver = FileReceiver()
create_command = CreateFileCommand(receiver, "python_file.txt")
invoker = CommandInvoker()

# 执行命令
invoker.set_command(create_command)
invoker.execute_command()

2.3 Golang 实现(接口至上的极简实现)

Go 语言无类和继承概念,核心遵循“组合优于继承”的设计哲学,通过接口定义命令的统一契约,结构体实现具体命令、接收者和调用者,代码极简、高效,贴合Go语言“简洁、务实、高性能”的设计理念,适配高并发、高性能的后端开发场景。

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
package main

import (
"fmt"
"os"
)

// 接收者:文件操作结构体(核心业务逻辑实现)
type FileReceiver struct{}

// CreateFile 核心业务方法:创建文件
func (f *FileReceiver) CreateFile(fileName string) {
// 检查文件是否存在
if _, err := os.Stat(fileName); os.IsNotExist(err) {
// 创建文件并延迟关闭,避免资源泄漏
file, err := os.Create(fileName)
if err != nil {
fmt.Printf("Golang: 创建文件失败,异常信息:%v\n", err)
return
}
defer file.Close()
fmt.Printf("Golang: 接收者成功创建文件 - %s\n", fileName)
} else {
fmt.Printf("Golang: 文件 %s 已存在,无需重复创建\n", fileName)
}
}

// 抽象命令接口(定义命令执行的统一契约)
type Command interface {
Execute() // 命令执行方法
}

// 具体命令:创建文件命令(封装接收者与参数)
type CreateFileCommand struct {
receiver *FileReceiver // 持有接收者引用
fileName string // 命令执行参数
}

// NewCreateFileCommand 工厂函数:创建创建文件命令实例
func NewCreateFileCommand(receiver *FileReceiver, fileName string) *CreateFileCommand {
return &CreateFileCommand{
receiver: receiver,
fileName: fileName,
}
}

// Execute 执行命令:调用接收者的核心业务方法
func (c *CreateFileCommand) Execute() {
c.receiver.CreateFile(c.fileName)
}

// 调用者:命令触发与管理
type CommandInvoker struct {
command Command // 持有当前命令
}

// SetCommand 设置要执行的命令
func (i *CommandInvoker) SetCommand(command Command) {
i.command = command
}

// ExecuteCommand 触发命令执行
func (i *CommandInvoker) ExecuteCommand() {
if i.command != nil {
i.command.Execute()
} else {
fmt.Println("Golang: 未设置任何命令,无法执行")
}
}

// 客户端调用
func main() {
// 初始化接收者
receiver := &FileReceiver{}
// 初始化具体命令(绑定接收者与参数)
createCommand := NewCreateFileCommand(receiver, "golang_file.txt")
// 初始化调用者
invoker := &CommandInvoker{}

// 执行命令
invoker.SetCommand(createCommand)
invoker.ExecuteCommand()
}

2.4 C++ 实现(面向对象经典实现)

C++ 作为经典面向对象语言,通过抽象类(纯虚函数)定义抽象命令,子类实现具体命令与接收者,依托继承关系和对象组合实现解耦,需手动管理内存(通过析构函数释放对象),兼顾灵活性与高性能,适配底层开发、高频调用场景,是底层系统、高性能应用的优选实现方式。

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

// 接收者:文件操作类(核心业务逻辑实现)
class FileReceiver {
public:
/// <summary>
/// 核心业务方法:创建文件
/// </summary>
/// <param name="fileName">文件名称(含路径)</param>
void createFile(const string& fileName) {
// 检查文件是否存在
ifstream file(fileName);
if (!file.good()) {
// 创建文件并关闭
ofstream newFile(fileName);
if (newFile.is_open()) {
newFile.close();
cout << "C++: 接收者成功创建文件 - " << fileName << endl;
} else {
cout << "C++: 创建文件失败,无法打开文件流" << endl;
}
} else {
cout << "C++: 文件 " << fileName << " 已存在,无需重复创建" << endl;
}
file.close();
}
};

// 抽象命令类(纯虚函数定义统一契约)
class Command {
public:
virtual ~Command() = default; // 虚析构函数,避免多态场景下内存泄漏
virtual void execute() = 0; // 纯虚函数,命令执行方法,子类必须实现
};

// 具体命令:创建文件命令(封装接收者与参数)
class CreateFileCommand : public Command {
private:
FileReceiver* receiver; // 持有接收者指针
string fileName; // 命令执行参数
public:
/// <summary>
/// 初始化创建文件命令
/// </summary>
/// <param name="recv">文件操作接收者指针</param>
/// <param name="name">要创建的文件名称</param>
CreateFileCommand(FileReceiver* recv, string name) : receiver(recv), fileName(name) {}

/// <summary>
/// 执行命令:调用接收者的核心业务方法
/// </summary>
void execute() override {
if (receiver != nullptr) {
receiver->createFile(fileName);
} else {
cout << "C++: 接收者实例为空,无法执行命令" << endl;
}
}
};

// 调用者:命令触发与管理
class CommandInvoker {
private:
Command* command; // 持有当前命令指针
public:
CommandInvoker() : command(nullptr) {}
~CommandInvoker() {
// 释放命令对象,避免内存泄漏
delete command;
command = nullptr;
}

/// <summary>
/// 设置要执行的命令
/// </summary>
/// <param name="cmd">具体命令指针</param>
void setCommand(Command* cmd) {
// 先释放原有命令,避免内存泄漏
delete command;
command = cmd;
}

/// <summary>
/// 触发命令执行
/// </summary>
void executeCommand() {
if (command != nullptr) {
command->execute();
} else {
cout << "C++: 未设置任何命令,无法执行" << endl;
}
}
};

// 客户端调用
int main() {
// 初始化接收者
FileReceiver* receiver = new FileReceiver();
// 初始化具体命令(绑定接收者与参数)
Command* createCommand = new CreateFileCommand(receiver, "cpp_file.txt");
// 初始化调用者
CommandInvoker* invoker = new CommandInvoker();

// 执行命令
invoker->setCommand(createCommand);
invoker->executeCommand();

// 释放资源,避免内存泄漏
delete receiver;
delete invoker;
return 0;
}

2.5 纯C语言实现(结构体+函数指针模拟实现)

纯C语言无面向对象特性,无类和多态,通过“结构体封装数据+函数指针封装行为”,手动模拟命令模式的核心逻辑,用函数指针实现命令的统一执行接口,用结构体封装接收者、命令和调用者,需手动管理内存,代码虽略显冗余,但底层可控性强,适配嵌入式、底层开发等资源受限场景,完美还原“命令封装、解耦调用”的核心思想。

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <string.h>

// 接收者:文件操作结构体(封装数据,无额外属性时可仅作为标识)
typedef struct {
// 可扩展接收者属性(如文件路径前缀、权限等)
} FileReceiver;

// 接收者核心业务方法:创建文件
void file_receiver_create_file(FileReceiver* receiver, const char* fileName) {
(void)receiver; // 未使用接收者属性,仅占位,保证接口一致性
// 检查文件是否存在
if (access(fileName, F_OK) != 0) {
FILE* file = fopen(fileName, "w");
if (file != NULL) {
fclose(file);
printf("C: 接收者成功创建文件 - %s\n", fileName);
} else {
printf("C: 创建文件失败,无法打开文件\n");
}
} else {
printf("C: 文件 %s 已存在,无需重复创建\n", fileName);
}
}

// 抽象命令:函数指针(模拟命令执行的统一接口)
typedef void (*CommandExecuteFunc)(void*);

// 具体命令结构体:封装接收者、执行参数、执行函数
typedef struct {
FileReceiver* receiver; // 持有接收者引用
const char* fileName; // 命令执行参数
CommandExecuteFunc execute;// 命令执行函数指针
} CreateFileCommand;

// 具体命令执行逻辑:调用接收者的核心业务方法
void create_file_command_execute(void* command) {
if (command == NULL) {
printf("C: 命令实例为空,无法执行\n");
return;
}
CreateFileCommand* cmd = (CreateFileCommand*)command;
if (cmd->receiver != NULL && cmd->fileName != NULL) {
file_receiver_create_file(cmd->receiver, cmd->fileName);
} else {
printf("C: 接收者或文件名称为空,无法执行命令\n");
}
}

// 调用者结构体:持有命令,负责触发执行
typedef struct {
void* command; // 通用命令指针(适配所有具体命令)
} CommandInvoker;

// 调用者方法:设置要执行的命令
void command_invoker_set_command(CommandInvoker* invoker, void* cmd) {
if (invoker != NULL) {
invoker->command = cmd;
} else {
printf("C: 调用者实例为空,无法设置命令\n");
}
}

// 调用者方法:触发命令执行
void command_invoker_execute_command(CommandInvoker* invoker) {
if (invoker == NULL) {
printf("C: 调用者实例为空,无法执行命令\n");
return;
}
if (invoker->command != NULL) {
CreateFileCommand* cmd = (CreateFileCommand*)invoker->command;
// 调用命令的执行函数
cmd->execute(invoker->command);
} else {
printf("C: 未设置任何命令,无法执行\n");
}
}

// 创建接收者实例
FileReceiver* file_receiver_create() {
FileReceiver* receiver = (FileReceiver*)malloc(sizeof(FileReceiver));
if (receiver == NULL) {
printf("C: 接收者实例创建失败\n");
}
return receiver;
}

// 创建创建文件命令实例
CreateFileCommand* create_file_command_create(FileReceiver* receiver, const char* fileName) {
if (receiver == NULL || fileName == NULL) {
printf("C: 接收者或文件名称为空,无法创建命令\n");
return NULL;
}
CreateFileCommand* cmd = (CreateFileCommand*)malloc(sizeof(CreateFileCommand));
if (cmd == NULL) {
printf("C: 命令实例创建失败\n");
return NULL;
}
cmd->receiver = receiver;
cmd->fileName = fileName;
cmd->execute = create_file_command_execute;
return cmd;
}

// 创建调用者实例
CommandInvoker* command_invoker_create() {
CommandInvoker* invoker = (CommandInvoker*)malloc(sizeof(CommandInvoker));
if (invoker == NULL) {
printf("C: 调用者实例创建失败\n");
}
invoker->command = NULL;
return invoker;
}

// 释放资源
void file_receiver_destroy(FileReceiver* receiver) {
if (receiver != NULL) {
free(receiver);
receiver = NULL;
}
}

void create_file_command_destroy(CreateFileCommand* cmd) {
if (cmd != NULL) {
free(cmd);
cmd = NULL;
}
}

void command_invoker_destroy(CommandInvoker* invoker) {
if (invoker != NULL) {
free(invoker);
invoker = NULL;
}
}

// 客户端调用
int main() {
// 1. 初始化接收者
FileReceiver* receiver = file_receiver_create();
if (receiver == NULL) return -1;

// 2. 初始化具体命令(绑定接收者与参数)
CreateFileCommand* createCommand = create_file_command_create(receiver, "c_file.txt");
if (createCommand == NULL) {
file_receiver_destroy(receiver);
return -1;
}

// 3. 初始化调用者
CommandInvoker* invoker = command_invoker_create();
if (invoker == NULL) {
create_file_command_destroy(createCommand);
file_receiver_destroy(receiver);
return -1;
}

// 4. 执行命令
command_invoker_set_command(invoker, createCommand);
command_invoker_execute_command(invoker);

// 5. 释放资源,避免内存泄漏
command_invoker_destroy(invoker);
create_file_command_destroy(createCommand);
file_receiver_destroy(receiver);

return 0;
}

三、命令模式的优缺点

命令模式的核心价值是“解耦调用者与接收者、灵活管理请求”,其优缺点均围绕这一核心展开。在实际开发中,需结合业务场景的复杂度、扩展需求和性能要求,权衡使用,避免过度设计或滥用,确保既发挥其核心优势,又规避潜在问题,实现架构设计的合理性。

3.1 核心优点

  • 解耦性极强,符合开闭原则:调用者与接收者完全解耦,调用者无需知道接收者的具体实现、方法名和参数,只需调用命令的执行方法即可;新增命令时,只需实现抽象命令接口/结构体,无需修改现有调用者和接收者代码,扩展性极强。

  • 请求管理灵活:支持命令的队列执行、批量处理、异步执行,可将多个命令组合成复合命令(如事务操作),实现“要么全部执行,要么全部回滚”的逻辑,适配复杂业务场景。

  • 支持撤销与重做:通过扩展Undo()Redo()方法,可实现命令的回滚和重复执行(如文本编辑器的撤销功能、软件的操作回滚),提升用户体验和系统容错性。

  • 便于日志记录与系统恢复:可将命令的执行过程序列化到日志,系统崩溃或异常后,通过重新执行日志中的命令,恢复系统状态,尤其适用于金融、电商等对数据一致性要求高的场景。

  • 请求参数化与复用:命令对象可封装请求所需的参数,同一个命令可被不同调用者复用,也可通过修改参数实现不同的请求逻辑,提升代码复用率。

3.2 主要缺点

  • 类/结构体数量膨胀:每个具体命令都需要对应一个类/结构体,当系统中命令数量较多时,会导致类/结构体数量激增,增加代码的复杂度和维护成本。

  • 存在轻微性能开销:命令模式通过“调用者→命令→接收者”的三层调用链路实现请求执行,相比直接调用接收者方法,会增加少量运行时开销,对极致性能优化的场景(如高频调用、低延迟需求)不友好。

  • 逻辑间接性提升,调试难度增加:请求的执行链路变长,出现问题时,需依次排查调用者、命令、接收者三层逻辑,定位问题的难度提升,增加调试成本。

  • 复合命令实现复杂:当需要实现命令的组合、撤销/重做时,逻辑会变得复杂(如需要维护命令执行历史、处理命令依赖关系),增加开发难度。

四、命令模式的使用场景

命令模式的核心适用场景是“需要解耦请求发起与请求执行、且需灵活管理请求(如队列、撤销、日志)”的业务场景。以下结合具体场景及典型实战案例,帮助开发者快速判断是否适用,实现精准落地,避免滥用或错用。

4.1 核心适用场景

  • GUI交互场景:按钮点击、菜单选择、快捷键操作等(如点击“保存”按钮,触发保存命令,按钮无需知道如何保存,只需触发命令执行),典型案例:桌面应用的菜单操作、网页按钮交互。

  • 撤销/重做功能场景:文本编辑器、绘图工具、代码编辑器的撤销(Undo)、重做(Redo)操作,通过记录命令执行历史,实现操作的回滚和重复执行。

  • 批处理与异步执行场景:命令队列、任务调度、异步任务处理(如定时任务执行、消息队列消费、批量数据处理),将多个命令放入队列,依次执行或异步执行,提升系统吞吐量。

  • 事务操作场景:数据库事务、分布式事务的提交/回滚,将多个操作封装为复合命令,要么全部执行成功,要么全部回滚,确保数据一致性,典型案例:电商订单创建(扣减库存、创建订单、扣减余额)。

  • 远程调用与RPC场景:RPC框架中,将客户端的请求封装为命令对象,通过网络传输到服务端,服务端执行命令并返回结果,屏蔽网络通信细节,实现客户端与服务端的解耦。

  • 日志恢复与审计场景:金融、电商、政务系统的操作日志,记录用户的每一步操作(命令),系统故障后重新执行命令恢复数据,同时用于操作审计,追溯操作行为。

  • 插件化与扩展场景:系统插件开发,将插件的功能封装为命令,主程序通过调用命令实现插件功能,无需修改主程序代码,提升系统的可扩展性。

4.2 典型实战案例

  • 文本编辑器的撤销/重做:如Notepad++、VS Code的撤销功能,每一次编辑操作(输入、删除、格式调整)都封装为命令,存入命令历史栈,撤销时弹出最后一个命令执行Undo(),重做时重新执行命令。

  • 电商订单处理:订单创建流程包含“扣减库存、创建订单记录、扣减用户余额、发送通知”四个步骤,将每个步骤封装为命令,组合成复合命令,执行成功则提交,任意步骤失败则全部回滚,确保数据一致性。

  • 消息队列与任务调度:如RabbitMQ、Kafka的消息消费,将每条消息的处理逻辑封装为命令,消费者从队列中获取命令并执行,实现异步处理和解耦;定时任务框架(如Quartz),将定时任务封装为命令,到点触发执行。

  • RPC框架的请求封装:如Dubbo、gRPC,客户端发起的请求被封装为命令对象(包含请求参数、接口信息),通过网络传输到服务端,服务端解析命令并执行对应的业务逻辑,返回结果。

  • 游戏开发的技能释放:游戏中角色的技能释放,将每个技能封装为命令,包含技能效果、冷却时间等参数,调用者(角色)触发技能命令,接收者(技能系统)执行技能效果,支持技能的队列释放、撤销(如技能打断)。

五、总结

命令模式的核心是“请求封装、解耦调用、灵活管理”,它通过将请求封装为独立的命令对象,实现了调用者与接收者的完全解耦,让请求的执行、组合、撤销、日志记录变得灵活可控,是行为型设计模式中“解耦与扩展”的典型代表。其本质是将“行为”抽象为对象,让行为具备可复用、可管理、可扩展的特性,完美契合“开闭原则”和“单一职责原则”。

从多语言实现来看,尽管各语言的语法特性差异显著,但核心逻辑高度统一,且均能适配自身的设计理念,完整实现命令模式的核心价值:

  • 面向对象语言(C#、Python、C++):通过接口/抽象类定义命令的统一契约,具体命令绑定接收者并实现执行逻辑,依托面向对象的封装、多态特性,实现逻辑清晰、易于维护的代码结构,适配大多数业务场景,尤其适合企业级系统开发;

  • Go语言:遵循“接口至上”和“组合优于继承”,通过接口定义命令规范,结构体实现命令、接收者和调用者,代码极简高效,贴合高并发、高性能的后端开发需求,是后端服务开发的优选实现方式;

  • 纯C语言:通过结构体+函数指针模拟面向对象特性,用函数指针实现命令的统一执行接口,用结构体封装数据和行为,手动管理内存和命令生命周期,底层可控性强,适配嵌入式、底层开发等资源受限场景,虽代码冗余,但能完整还原命令模式的核心思想。

在工程实践中,使用命令模式需把握三个核心原则:一是判断业务场景是否需要解耦请求发起与执行,是否需要命令的队列、撤销、日志等功能,简单的方法调用无需引入命令模式,避免过度设计;二是控制命令的数量和复杂度,命令过多时可通过复合命令、命令工厂等方式简化管理;三是权衡性能开销,高频调用、低延迟的场景需谨慎使用,或选择轻量级的命令实现方式。

总体而言,命令模式是解耦系统、提升扩展性和灵活性的高效工具,尤其在GUI交互、事务处理、异步任务、日志恢复等场景中价值显著。合理使用命令模式,可让代码结构更清晰、职责更单一、扩展更灵活,同时提升系统的可维护性和容错性,是每一位开发者必备的架构设计工具。

模板方法模式(Template Method Pattern)是一种经典的行为型设计模式,其核心思想是定义一个算法的骨架流程,将算法中不变的核心步骤固化在父类的模板方法中,而将算法中可变的具体实现步骤延迟到子类中实现。通过这种“固定骨架、灵活填充”的设计,既能保证算法整体流程的一致性,又能让子类在不改变算法结构的前提下,灵活定制具体步骤的实现,是软件设计中实现代码复用、规范流程、提升扩展性的重要手段。

模板方法模式的核心价值在于“封装不变、扩展可变”,它将多个子类共有的通用逻辑提取到父类中,避免代码冗余,同时通过抽象方法预留扩展点,让子类按需实现差异化逻辑,完美契合“开闭原则”——对扩展开放,对修改关闭。

一、模板方法模式的核心结构

模板方法模式的结构简洁清晰,核心角色分为两类,二者协同工作,实现“骨架固定、细节灵活”的设计目标,确保算法流程的一致性与实现的灵活性:

1.1 抽象类(Abstract Class)

模板方法模式的核心角色,负责定义算法的骨架流程。它包含两种类型的方法:

  • 模板方法(Template Method):定义算法的整体执行流程,通常为非抽象方法(可加final修饰,防止子类重写破坏流程),依次调用算法的各个步骤,包括不变的通用步骤和可变的抽象步骤。

  • 基本方法:构成算法流程的具体步骤,分为两种:一是抽象方法(Abstract Method),由子类实现的可变步骤,父类仅定义方法签名,不提供具体实现;二是具体方法(Concrete Method),父类提供的不变通用步骤,子类可直接复用,无需重写(也可根据需求重写,称为“钩子方法”)。

1.2 具体子类(Concrete Class)

继承抽象类,负责实现抽象类中定义的抽象方法,即填充算法流程中可变的具体步骤。子类无需修改算法的整体流程,仅需专注于自身业务场景下的步骤实现,确保算法流程的一致性,同时实现差异化逻辑。

核心逻辑链路:客户端调用抽象类的模板方法 → 模板方法按固定流程调用基本方法(通用步骤+子类实现的可变步骤) → 完成算法执行。整个过程中,算法骨架不变,可变细节由子类灵活实现。

二、多语言实现模板方法模式

为便于开发者落地实践,本文以“饮品制作流程”为经典案例,实现多语言版本的模板方法模式:饮品制作的整体流程(烧水→冲泡→倒入容器→添加配料)为固定骨架,其中“冲泡”和“添加配料”为可变步骤(不同饮品实现不同),“烧水”和“倒入容器”为通用步骤(所有饮品均可复用)。所有实现均保证完整可运行,贴合各语言设计理念,添加规范注释,兼顾实用性与可读性。

2.1 C# 实现(面向对象标准实现)

C# 作为强类型面向对象语言,通过抽象类定义算法骨架,抽象方法定义可变步骤,具体方法定义通用步骤,模板方法用final(C#中为sealed)修饰防止子类破坏流程,代码结构严谨、可读性高,适配企业级业务系统开发,是最常用的实现方式之一。

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
using System;

// 抽象类:饮品制作模板(定义算法骨架)
public abstract class BeverageTemplate
{
/// <summary>
/// 模板方法:定义饮品制作的固定流程(不可重写,防止破坏流程)
/// </summary>
public sealed void MakeBeverage()
{
BoilWater(); // 通用步骤:烧水
Brew(); // 可变步骤:冲泡(子类实现)
PourInCup(); // 通用步骤:倒入容器
AddCondiments(); // 可变步骤:添加配料(子类实现)
Console.WriteLine("饮品制作完成!\n");
}

/// <summary>
/// 具体方法:通用步骤 - 烧水(所有饮品复用)
/// </summary>
protected void BoilWater()
{
Console.WriteLine("步骤1:烧开水(100℃)");
}

/// <summary>
/// 抽象方法:可变步骤 - 冲泡(子类实现差异化逻辑)
/// </summary>
protected abstract void Brew();

/// <summary>
/// 具体方法:通用步骤 - 倒入容器(所有饮品复用)
/// </summary>
protected void PourInCup()
{
Console.WriteLine("步骤3:将饮品倒入杯子中");
}

/// <summary>
/// 抽象方法:可变步骤 - 添加配料(子类实现差异化逻辑)
/// </summary>
protected abstract void AddCondiments();
}

// 具体子类1:咖啡制作(实现可变步骤)
public class Coffee : BeverageTemplate
{
protected override void Brew()
{
Console.WriteLine("步骤2:用沸水冲泡咖啡粉");
}

protected override void AddCondiments()
{
Console.WriteLine("步骤4:添加牛奶和方糖");
}
}

// 具体子类2:茶制作(实现可变步骤)
public class Tea : BeverageTemplate
{
protected override void Brew()
{
Console.WriteLine("步骤2:用沸水冲泡茶叶");
}

protected override void AddCondiments()
{
Console.WriteLine("步骤4:添加柠檬片");
}
}

// 客户端调用(仅依赖抽象类,符合依赖倒置原则)
class Program
{
static void Main(string[] args)
{
Console.WriteLine("制作咖啡:");
BeverageTemplate coffee = new Coffee();
coffee.MakeBeverage();

Console.WriteLine("制作茶:");
BeverageTemplate tea = new Tea();
tea.MakeBeverage();
}
}

2.2 Python 实现(动态语言简洁实现)

Python 遵循“鸭子类型”,无需显式定义抽象类(可借助abc模块模拟),通过类的继承关系实现模板方法模式,语法简洁灵活,无需繁琐的类型声明,依托GC自动管理内存,适配快速开发、脚本开发及轻量级项目场景,完整保留模板方法模式的核心逻辑。

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
from abc import ABC, abstractmethod

# 抽象类:饮品制作模板(借助ABC模块模拟抽象类)
class BeverageTemplate(ABC):
def make_beverage(self):
"""模板方法:定义饮品制作的固定流程(不可重写,Python中通过命名规范约束)""" self.boil_water() # 通用步骤:烧水
self.brew() # 可变步骤:冲泡(子类实现)
self.pour_in_cup() # 通用步骤:倒入容器
self.add_condiments() # 可变步骤:添加配料(子类实现)
print("饮品制作完成!\n")

def boil_water(self):
"""具体方法:通用步骤 - 烧水(所有饮品复用)""" print("步骤1:烧开水(100℃)")

@abstractmethod
def brew(self):
"""抽象方法:可变步骤 - 冲泡(子类必须实现)""" pass

def pour_in_cup(self):
"""具体方法:通用步骤 - 倒入容器(所有饮品复用)""" print("步骤3:将饮品倒入杯子中")

@abstractmethod
def add_condiments(self):
"""抽象方法:可变步骤 - 添加配料(子类必须实现)""" pass

# 具体子类1:咖啡制作
class Coffee(BeverageTemplate):
def brew(self):
print("步骤2:用沸水冲泡咖啡粉")

def add_condiments(self):
print("步骤4:添加牛奶和方糖")

# 具体子类2:茶制作
class Tea(BeverageTemplate):
def brew(self):
print("步骤2:用沸水冲泡茶叶")

def add_condiments(self):
print("步骤4:添加柠檬片")

# 客户端调用
if __name__ == "__main__":
print("制作咖啡:")
coffee = Coffee()
coffee.make_beverage()

print("制作茶:")
tea = Tea()
tea.make_beverage()

2.3 Go 实现(接口+结构体的极简实现)

Go 语言无类和继承概念,核心遵循“组合优于继承”的设计哲学,通过接口定义可变步骤的契约,结构体嵌入实现通用步骤,模板方法定义固定流程,代码极简、高效,贴合Go语言“简洁、务实、高性能”的设计理念,适配高并发、高性能的后端开发场景。

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
package main

import (
"fmt"
)

// 定义可变步骤的接口(抽象方法契约)
type BeverageInterface interface {
Brew() // 可变步骤:冲泡
AddCondiments() // 可变步骤:添加配料
}

// 抽象模板结构体:实现通用步骤,嵌入接口实现可变步骤
type BeverageTemplate struct {
BeverageInterface // 嵌入接口,实现依赖注入
}

// 模板方法:定义固定流程(不可重写,Go中通过结构体方法实现)
func (t *BeverageTemplate) MakeBeverage() {
t.BoilWater() // 通用步骤:烧水
t.Brew() // 可变步骤:调用接口方法(子类实现)
t.PourInCup() // 通用步骤:倒入容器
t.AddCondiments() // 可变步骤:调用接口方法(子类实现)
fmt.Println("饮品制作完成!\n")
}

// 通用步骤:烧水(所有饮品复用)
func (t *BeverageTemplate) BoilWater() {
fmt.Println("步骤1:烧开水(100℃)")
}

// 通用步骤:倒入容器(所有饮品复用)
func (t *BeverageTemplate) PourInCup() {
fmt.Println("步骤3:将饮品倒入杯子中")
}

// 具体子类1:咖啡制作(实现接口的可变步骤)
type Coffee struct{}

func (c *Coffee) Brew() {
fmt.Println("步骤2:用沸水冲泡咖啡粉")
}

func (c *Coffee) AddCondiments() {
fmt.Println("步骤4:添加牛奶和方糖")
}

// 具体子类2:茶制作(实现接口的可变步骤)
type Tea struct{}

func (t *Tea) Brew() {
fmt.Println("步骤2:用沸水冲泡茶叶")
}

func (t *Tea) AddCondiments() {
fmt.Println("步骤4:添加柠檬片")
}

// 客户端调用
func main() {
fmt.Println("制作咖啡:")
coffee := &Coffee{}
coffeeTemplate := &BeverageTemplate{BeverageInterface: coffee}
coffeeTemplate.MakeBeverage()

fmt.Println("制作茶:")
tea := &Tea{}
teaTemplate := &BeverageTemplate{BeverageInterface: tea}
teaTemplate.MakeBeverage()
}

2.4 C++ 实现(面向对象经典实现)

C++ 作为经典面向对象语言,通过抽象类(纯虚函数)定义可变步骤,具体方法定义通用步骤,模板方法用final修饰防止子类重写,依托继承关系实现代码复用,需手动管理内存,兼顾灵活性与高性能,适配底层开发、高频调用场景,是底层系统、高性能应用的优选实现方式。

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
#include <iostream>
using namespace std;

// 抽象类:饮品制作模板(定义算法骨架)
class BeverageTemplate {
public:
// 模板方法:固定流程,final修饰防止子类重写
final void makeBeverage() {
boilWater(); // 通用步骤:烧水
brew(); // 可变步骤:冲泡(纯虚函数,子类实现)
pourInCup(); // 通用步骤:倒入容器
addCondiments(); // 可变步骤:添加配料(纯虚函数,子类实现)
cout << "饮品制作完成!\n" << endl;
}

protected:
// 具体方法:通用步骤 - 烧水(所有饮品复用)
void boilWater() {
cout << "步骤1:烧开水(100℃)" << endl;
}

// 纯虚函数:可变步骤 - 冲泡(子类必须实现)
virtual void brew() = 0;

// 具体方法:通用步骤 - 倒入容器(所有饮品复用)
void pourInCup() {
cout << "步骤3:将饮品倒入杯子中" << endl;
}

// 纯虚函数:可变步骤 - 添加配料(子类必须实现)
virtual void addCondiments() = 0;

// 析构函数:虚析构,避免多态场景下内存泄漏
virtual ~BeverageTemplate() = default;
};

// 具体子类1:咖啡制作
class Coffee : public BeverageTemplate {
protected:
void brew() override {
cout << "步骤2:用沸水冲泡咖啡粉" << endl;
}

void addCondiments() override {
cout << "步骤4:添加牛奶和方糖" << endl;
}
};

// 具体子类2:茶制作
class Tea : public BeverageTemplate {
protected:
void brew() override {
cout << "步骤2:用沸水冲泡茶叶" << endl;
}

void addCondiments() override {
cout << "步骤4:添加柠檬片" << endl;
}
};

// 客户端调用
int main() {
cout << "制作咖啡:" << endl;
BeverageTemplate* coffee = new Coffee();
coffee->makeBeverage();
delete coffee; // 释放资源

cout << "制作茶:" << endl;
BeverageTemplate* tea = new Tea();
tea->makeBeverage();
delete tea; // 释放资源

return 0;
}

2.5 纯C语言实现(结构体+函数指针模拟实现)

纯C语言无面向对象特性,无类和继承,通过“结构体封装数据+函数指针封装行为”,手动模拟模板方法模式的核心逻辑,用函数指针实现可变步骤,用普通函数实现通用步骤,需手动管理内存,代码虽略显冗余,但底层可控性强,适配嵌入式、底层开发等资源受限场景,完美还原“固定骨架、灵活填充”的核心思想。

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
#include <stdio.h>
#include <stdlib.h>

// 定义可变步骤的函数指针(模拟抽象方法)
typedef void (*BrewFunc)();
typedef void (*AddCondimentsFunc)();

// 抽象模板结构体:封装通用步骤和可变步骤的函数指针
typedef struct {
BrewFunc brew; // 可变步骤:冲泡
AddCondimentsFunc add_condiments; // 可变步骤:添加配料
} BeverageTemplate;

// 通用步骤:烧水(所有饮品复用)
void boil_water() {
printf("步骤1:烧开水(100℃)\n");
}

// 通用步骤:倒入容器(所有饮品复用)
void pour_in_cup() {
printf("步骤3:将饮品倒入杯子中\n");
}

// 模板方法:定义固定流程
void make_beverage(BeverageTemplate* template) {
if (template == NULL || template->brew == NULL || template->add_condiments == NULL) {
printf("模板初始化失败,无法制作饮品\n");
return;
}
boil_water();
template->brew();
pour_in_cup();
template->add_condiments();
printf("饮品制作完成!\n\n");
}

// 具体实现1:咖啡的冲泡和添加配料方法
void coffee_brew() {
printf("步骤2:用沸水冲泡咖啡粉\n");
}
void coffee_add_condiments() {
printf("步骤4:添加牛奶和方糖\n");
}

// 具体实现2:茶的冲泡和添加配料方法
void tea_brew() {
printf("步骤2:用沸水冲泡茶叶\n");
}
void tea_add_condiments() {
printf("步骤4:添加柠檬片\n");
}

// 创建咖啡模板实例
BeverageTemplate* create_coffee_template() {
BeverageTemplate* template = (BeverageTemplate*)malloc(sizeof(BeverageTemplate));
if (template == NULL) return NULL;
template->brew = coffee_brew;
template->add_condiments = coffee_add_condiments;
return template;
}

// 创建茶模板实例
BeverageTemplate* create_tea_template() {
BeverageTemplate* template = (BeverageTemplate*)malloc(sizeof(BeverageTemplate));
if (template == NULL) return NULL;
template->brew = tea_brew;
template->add_condiments = tea_add_condiments;
return template;
}

// 释放模板资源
void destroy_template(BeverageTemplate* template) {
if (template != NULL) {
free(template);
template = NULL;
}
}

// 客户端调用
int main() {
printf("制作咖啡:\n");
BeverageTemplate* coffee = create_coffee_template();
make_beverage(coffee);
destroy_template(coffee);

printf("制作茶:\n");
BeverageTemplate* tea = create_tea_template();
make_beverage(tea);
destroy_template(tea);

return 0;
}

三、模板方法模式的优缺点

模板方法模式的核心价值是“封装不变、扩展可变”,其优缺点均围绕这一核心展开。在实际开发中,需结合业务场景的流程复杂度、扩展需求,权衡使用,避免过度设计或滥用,确保既发挥其核心优势,又规避潜在问题,实现架构设计的合理性。

3.1 核心优点

  • 代码复用性高:将多个子类共有的通用逻辑提取到抽象类中,避免代码冗余,子类仅需实现差异化的可变步骤,减少重复开发工作量,提升开发效率。

  • 流程规范统一:模板方法定义了算法的固定骨架,确保所有子类的执行流程一致,避免因子类实现差异导致流程混乱,提升系统的规范性和可维护性。

  • 扩展性强,符合开闭原则:新增业务场景时,无需修改抽象类的模板方法和通用步骤,仅需新增子类并实现抽象方法即可,实现“对扩展开放,对修改关闭”,降低代码修改风险。

  • 解耦通用逻辑与差异化逻辑:抽象类负责封装通用逻辑,子类负责实现差异化逻辑,职责划分清晰,便于代码的维护和迭代,降低后期修改成本。

3.2 主要缺点

  • 灵活性受限:模板方法固定了算法的整体流程,若需修改流程结构,必须修改抽象类的模板方法,违反开闭原则,难以适配流程频繁变化的场景。

  • 子类依赖抽象类:子类的实现依赖抽象类的模板设计,若抽象类发生修改(如新增通用步骤),可能导致所有子类需要调整,增加维护成本。

  • 抽象类复杂度提升:当算法流程步骤较多、通用逻辑复杂时,抽象类的代码会变得繁琐,难以理解和维护,尤其当通用步骤与可变步骤耦合较深时,会降低代码可读性。

  • 子类数量可能激增:每新增一种差异化场景,就需要新增一个子类,若场景过多,会导致子类数量激增,增加系统的管理成本。

四、模板方法模式的使用场景

模板方法模式的核心适用场景是“存在多个具有相同执行流程、仅部分步骤实现不同的业务场景”。以下结合具体场景及典型实战案例,帮助开发者快速判断是否适用,实现精准落地,避免滥用或错用。

4.1 核心适用场景

  • 流程固定、细节可变的场景:多个业务场景的执行流程完全一致,仅部分步骤的具体实现不同,如各类文件解析(读取文件→解析内容→校验数据→保存结果)、各类任务执行(初始化→执行核心逻辑→日志记录→清理资源)。

  • 需要统一流程规范的场景:要求所有子类遵循统一的执行流程,避免流程混乱,如框架开发中的流程模板、企业级系统中的业务流程规范(如订单处理、支付流程)。

  • 需要提升代码复用的场景:多个子类存在大量重复的通用逻辑,需提取到父类中复用,减少代码冗余,如各类工具类、业务服务的通用流程封装。

  • 需要灵活扩展的场景:后期可能新增多种差异化场景,且新增场景不改变原有流程,仅需扩展具体步骤,如插件开发、多类型数据处理。

4.2 典型实战案例

  • 框架开发中的流程模板:如Spring的生命周期、JUnit的单元测试流程,框架定义固定的执行骨架(初始化→执行核心逻辑→销毁),开发者仅需实现核心逻辑步骤,无需关注整体流程。

  • 文件解析工具:如Excel、CSV、JSON等多种格式的文件解析,整体流程均为“读取文件→解析内容→校验数据→保存结果”,仅解析步骤的实现不同,通过模板方法模式封装通用流程,子类实现差异化解析逻辑。

  • 业务流程规范:如电商系统的订单处理流程(创建订单→库存扣减→支付验证→订单确认→日志记录),通用步骤(日志记录、参数校验)提取到抽象类,子类实现不同类型订单(普通订单、秒杀订单)的差异化步骤。

  • 工具类封装:如各类数据导出工具(导出Excel、PDF、Word),流程均为“查询数据→格式化数据→生成文件→下载文件”,仅生成文件的步骤不同,通过模板方法模式提升代码复用率。

  • 游戏开发中的角色技能释放:不同角色的技能释放流程一致(前置准备→技能释放→效果展示→冷却计时),仅技能释放和效果展示的逻辑不同,通过模板方法模式规范流程,灵活扩展不同角色的技能。

五、总结

模板方法模式的核心是“固定骨架、灵活填充”,它通过抽象类封装算法的不变流程,用抽象方法预留扩展点,让子类实现差异化细节,既保证了流程的一致性和代码复用性,又实现了功能的灵活扩展,是“开闭原则”的典型落地方式。其本质是将“流程控制”与“细节实现”分离,让抽象类负责把控全局流程,子类专注于具体细节,提升代码的可维护性和扩展性。

从多语言实现来看,尽管各语言的语法特性差异显著,但核心逻辑高度统一,且均能适配自身的设计理念,完整实现模板方法模式的核心价值:

  • 面向对象语言(C#、Python、C++):通过抽象类+继承关系实现,抽象类定义模板方法和通用步骤,子类继承并实现抽象方法,依托面向对象的封装、多态特性,实现逻辑清晰、易于维护的代码结构,适配大多数业务场景;

  • Go语言:遵循“组合优于继承”,通过接口定义可变步骤契约,结构体嵌入接口实现通用步骤,模板方法定义固定流程,代码极简高效,贴合高并发、高性能的后端开发需求;

  • 纯C语言:通过结构体+函数指针模拟面向对象特性,用函数指针实现可变步骤,用普通函数实现通用步骤,手动管理模板实例,底层可控性强,适配嵌入式、底层开发等资源受限场景,虽代码冗余,但能完整还原“固定骨架、灵活填充”的核心思想。

在工程实践中,使用模板方法模式需把握三个核心原则:一是判断业务场景是否具备“固定流程、细节可变”的特征,流程频繁变化的场景不适合使用;二是控制抽象类的复杂度,避免通用步骤与可变步骤耦合,确保职责单一;三是合理设计扩展点,抽象方法的定义需贴合业务扩展需求,避免过度设计抽象方法导致子类实现成本增加。

总体而言,模板方法模式是实现代码复用、规范流程、提升扩展性的高效工具,尤其在框架开发、业务流程规范、工具类封装等场景中价值显著。合理使用模板方法模式,可让代码结构更清晰、流程更规范、扩展更灵活,是每一位开发者必备的架构设计工具。

0%