访问者模式

访问者模式(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语言则需通过结构体+函数指针模拟面向对象特性,虽实现繁琐,但能在资源受限场景中还原模式核心逻辑。

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