访问者模式(Visitor Pattern)是一种行为设计模式,它允许你在不改变对象结构的前提下,定义作用于这些对象的新操作。访问者模式将操作的逻辑与对象结构分离,使得添加新的操作变得更加容易。
实际应用场景
假设我们有一个文件系统,其中包含不同类型的文件和文件夹。我们希望对这些文件和文件夹执行不同的操作,例如计算总大小、显示文件信息等。使用访问者模式,我们可以将这些操作分离出来,并在不修改文件和文件夹类的情况下添加新的操作。
访问者模式的实现步骤
- 定义元素接口:定义一个接口或抽象类,声明一个接受访问者的方法。
- 实现具体元素类:具体元素类实现这个接口,并在接受访问者的方法中调用访问者的相应方法。
- 定义访问者接口:定义一个访问者接口,声明一组访问方法,每个方法对应一个具体元素类。
- 实现具体访问者类:具体访问者类实现访问者接口,并在每个访问方法中实现具体的操作。
代码示例
以下是一个使用访问者模式的文件系统示例:
#include <iostream>
#include <vector>
#include <memory>
// 访问者接口
class File;
class Folder;
class Visitor {
public:
virtual void visit(File* file) = 0;
virtual void visit(Folder* folder) = 0;
virtual ~Visitor() = default;
};
// 元素接口
class Element {
public:
virtual void accept(Visitor* visitor) = 0;
virtual ~Element() = default;
};
// 文件类
class File : public Element {
public:
File(const std::string& name, int size) : name(name), size(size) {}
void accept(Visitor* visitor) override {
visitor->visit(this);
}
std::string getName() const {
return name;
}
int getSize() const {
return size;
}
private:
std::string name;
int size;
};
// 文件夹类
class Folder : public Element {
public:
Folder(const std::string& name) : name(name) {}
void add(std::shared_ptr<Element> element) {
elements.push_back(element);
}
void accept(Visitor* visitor) override {
visitor->visit(this);
for (auto& element : elements) {
element->accept(visitor);
}
}
std::string getName() const {
return name;
}
const std::vector<std::shared_ptr<Element>>& getElements() const {
return elements;
}
private:
std::string name;
std::vector<std::shared_ptr<Element>> elements;
};
// 具体访问者类:计算总大小
class SizeVisitor : public Visitor {
public:
void visit(File* file) override {
totalSize += file->getSize();
}
void visit(Folder* folder) override {
// 对文件夹本身不做处理,只处理其中的文件和子文件夹
}
int getTotalSize() const {
return totalSize;
}
private:
int totalSize = 0;
};
// 具体访问者类:显示文件信息
class InfoVisitor : public Visitor {
public:
void visit(File* file) override {
std::cout << "File: " << file->getName() << ", Size: " << file->getSize() << " bytes" << std::endl;
}
void visit(Folder* folder) override {
std::cout << "Folder: " << folder->getName() << std::endl;
}
};
int main() {
// 创建文件和文件夹
auto file1 = std::make_shared<File>("file1.txt", 100);
auto file2 = std::make_shared<File>("file2.txt", 200);
auto folder = std::make_shared<Folder>("myFolder");
folder->add(file1);
folder->add(file2);
// 使用SizeVisitor计算总大小
SizeVisitor sizeVisitor;
folder->accept(&sizeVisitor);
std::cout << "Total size: " << sizeVisitor.getTotalSize() << " bytes" << std::endl;
// 使用InfoVisitor显示文件信息
InfoVisitor infoVisitor;
folder->accept(&infoVisitor);
return 0;
}
代码解析
-
访问者接口
Visitor
:- 定义了两个方法
visit(File* file)
和visit(Folder* folder)
,分别用于访问文件和文件夹。
- 定义了两个方法
-
元素接口
Element
:- 定义了一个
accept(Visitor* visitor)
方法,具体元素类需要实现这个方法。
- 定义了一个
-
具体元素类
File
和Folder
:File
类和Folder
类实现了Element
接口。- 在
accept
方法中,调用访问者的相应方法。
-
具体访问者类
SizeVisitor
和InfoVisitor
:SizeVisitor
用于计算总大小。InfoVisitor
用于显示文件信息。
-
客户端代码:
- 创建文件和文件夹,并将文件添加到文件夹中。
- 使用
SizeVisitor
计算总大小。 - 使用
InfoVisitor
显示文件信息。
总结
访问者模式通过将操作与对象结构分离,使得在不修改对象类的情况下添加新的操作变得更加容易。在这个示例中,我们定义了文件和文件夹的结构,并通过访问者模式添加了计算总大小和显示文件信息的操作。这样可以使代码更加灵活和可扩展。