访问者模式
“对一组对象的各种操作”集中封装到一个独立的对象里
适用的场景
数据结构稳定,但操作经常变化!(比如编译器、文档结构、语法树)
想把行为和数据结构解耦(就像你想加新功能但又不想改原来的类)
当你想对一堆对象做很多不同的事,而这些对象结构不常改时,就请访问者来做客
基本结构
┌──────────────┐ ┌───────────────┐
│ Element │<───┐ │ Visitor │
│(被访问的东西)│ │ │(访问逻辑) │
└─────▲────────┘ │ └────▲──────────┘
│ │ │
┌─────┴─────┐ ┌────┴─────┐ ┌──┴──────────┐
│ConcreteA │ │ConcreteB │ │PrintVisitor │
└───────────┘ └──────────┘ └─────────────┘Element定义一个Accept(visitor)方法,让访问者进来访问Visitor定义针对不同Element的访问方法,比如VisitA(),VisitB()。
示例
csharp
// 元素接口
interface IElement
{
void Accept(IVisitor visitor);
}
// 具体元素A
class File : IElement
{
public string Name = "File.txt";
public void Accept(IVisitor visitor) => visitor.VisitFile(this);
}
// 具体元素B
class Folder : IElement
{
public string Name = "Documents";
public void Accept(IVisitor visitor) => visitor.VisitFolder(this);
}
// 访问者接口
interface IVisitor
{
void VisitFile(File file);
void VisitFolder(Folder folder);
}
// 具体访问者
class PrintVisitor : IVisitor
{
public void VisitFile(File file) => Console.WriteLine("访问文件: " + file.Name);
public void VisitFolder(Folder folder) => Console.WriteLine("访问文件夹: " + folder.Name);
}
// 使用
var elements = new List<IElement> { new File(), new Folder() };
var visitor = new PrintVisitor();
foreach (var elem in elements)
elem.Accept(visitor);GDScript示例
gdscript
class Element:
func accept(visitor: Visitor):
pass
class FileElement extends Element:
var name = "file.txt"
func accept(visitor): visitor.visit_file(self)
class FolderElement extends Element:
var name = "documents"
func accept(visitor): visitor.visit_folder(self)
class Visitor:
func visit_file(file): pass
func visit_folder(folder): pass
class PrintVisitor extends Visitor:
func visit_file(file): print("访问文件: ", file.name)
func visit_folder(folder): print("访问文件夹: ", folder.name)
# 用法
func _ready():
var elements = [FileElement.new(), FolderElement.new()]
var visitor = PrintVisitor.new()
for elem in elements:
elem.accept(visitor)