访问者
更新: 6/22/2025 字数: 0 字 时长: 0 分钟
定义
访问者模式 (Visitor Pattern) 表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作,实现算法与对象结构的分离。
角色
- 访问者 (Visitor):声明访问操作接口
- 具体访问者 (ConcreteVisitor):实现访问操作
- 元素 (Element):定义 accept 接口
- 具体元素 (ConcreteElement):实现 accept 接口
- 对象结构 (ObjectStructure):维护元素集合,提供访问入口
- 客户端 (Client):创建访问者和对象结构
实现
文档处理系统
java
// 元素接口
public interface DocumentElement {
void accept(DocumentVisitor visitor);
}
// 具体元素:文本段落
public class TextParagraph implements DocumentElement {
private String content;
public TextParagraph(String content) {
this.content = content;
}
public String getContent() {
return content;
}
@Override
public void accept(DocumentVisitor visitor) {
visitor.visit(this);
}
}
// 具体元素:图像
public class ImageElement implements DocumentElement {
private String src;
private String altText;
public ImageElement(String src, String altText) {
this.src = src;
this.altText = altText;
}
public String getSrc() {
return src;
}
public String getAltText() {
return altText;
}
@Override
public void accept(DocumentVisitor visitor) {
visitor.visit(this);
}
}
// 具体元素:表格
public class TableElement implements DocumentElement {
private List<List<String>> data;
public TableElement(List<List<String>> data) {
this.data = data;
}
public List<List<String>> getData() {
return data;
}
@Override
public void accept(DocumentVisitor visitor) {
visitor.visit(this);
}
}
// 访问者接口
public interface DocumentVisitor {
void visit(TextParagraph paragraph);
void visit(ImageElement image);
void visit(TableElement table);
}
// 具体访问者:HTML 导出
public class HtmlExportVisitor implements DocumentVisitor {
private StringBuilder html = new StringBuilder();
public String getHtml() {
return html.toString();
}
@Override
public void visit(TextParagraph paragraph) {
html.append("<p>").append(paragraph.getContent()).append("</p>\n");
}
@Override
public void visit(ImageElement image) {
html.append("<img src=\"")
.append(image.getSrc())
.append("\" alt=\"")
.append(image.getAltText())
.append("\">\n");
}
@Override
public void visit(TableElement table) {
html.append("<table border=\"1\">\n");
for (List<String> row : table.getData()) {
html.append(" <tr>\n");
for (String cell : row) {
html.append(" <td>").append(cell).append("</td>\n");
}
html.append(" </tr>\n");
}
html.append("</table>\n");
}
}
// 具体访问者:文本导出
public class TextExportVisitor implements DocumentVisitor {
private StringBuilder text = new StringBuilder();
public String getText() {
return text.toString();
}
@Override
public void visit(TextParagraph paragraph) {
text.append(paragraph.getContent()).append("\n\n");
}
@Override
public void visit(ImageElement image) {
text.append("[图像:").append(image.getAltText()).append("]\n");
}
@Override
public void visit(TableElement table) {
for (List<String> row : table.getData()) {
for (String cell : row) {
text.append(cell).append("\t");
}
text.append("\n");
}
text.append("\n");
}
}
// 对象结构:文档
public class Document {
private List<DocumentElement> elements = new ArrayList<>();
public void addElement(DocumentElement element) {
elements.add(element);
}
public void accept(DocumentVisitor visitor) {
for (DocumentElement element : elements) {
element.accept(visitor);
}
}
}
// 使用示例
public class DocumentDemo {
public static void main(String[] args) {
// 创建文档
Document document = new Document();
document.addElement(new TextParagraph("欢迎访问我们的网站"));
document.addElement(new ImageElement("logo.png", "公司 Logo"));
document.addElement(new TextParagraph("产品列表:"));
// 添加表格
List<List<String>> tableData = List.of(
List.of("产品", "价格", "库存"),
List.of("手机", "¥3999", "100"),
List.of("笔记本", "¥8999", "50")
);
document.addElement(new TableElement(tableData));
// HTML 导出
HtmlExportVisitor htmlVisitor = new HtmlExportVisitor();
document.accept(htmlVisitor);
System.out.println("=== HTML 导出 ===");
System.out.println(htmlVisitor.getHtml());
// 文本导出
TextExportVisitor textVisitor = new TextExportVisitor();
document.accept(textVisitor);
System.out.println("=== 文本导出 ===");
System.out.println(textVisitor.getText());
}
}
编译器抽象语法树
java
// 抽象语法树节点接口
public interface AstNode {
void accept(AstVisitor visitor);
}
// 具体节点:变量声明
public class VariableDeclaration implements AstNode {
private String type;
private String name;
public VariableDeclaration(String type, String name) {
this.type = type;
this.name = name;
}
public String getType() {
return type;
}
public String getName() {
return name;
}
@Override
public void accept(AstVisitor visitor) {
visitor.visit(this);
}
}
// 具体节点:赋值语句
public class Assignment implements AstNode {
private String variable;
private String value;
public Assignment(String variable, String value) {
this.variable = variable;
this.value = value;
}
public String getVariable() {
return variable;
}
public String getValue() {
return value;
}
@Override
public void accept(AstVisitor visitor) {
visitor.visit(this);
}
}
// 具体节点:函数调用
public class FunctionCall implements AstNode {
private String functionName;
private List<String> arguments;
public FunctionCall(String functionName, List<String> arguments) {
this.functionName = functionName;
this.arguments = arguments;
}
public String getFunctionName() {
return functionName;
}
public List<String> getArguments() {
return arguments;
}
@Override
public void accept(AstVisitor visitor) {
visitor.visit(this);
}
}
// 访问者接口
public interface AstVisitor {
void visit(VariableDeclaration node);
void visit(Assignment node);
void visit(FunctionCall node);
}
// 具体访问者:类型检查
public class TypeCheckVisitor implements AstVisitor {
private Map<String, String> variables = new HashMap<>();
@Override
public void visit(VariableDeclaration node) {
System.out.println("声明变量:" + node.getName() + " (" + node.getType() + ")");
variables.put(node.getName(), node.getType());
}
@Override
public void visit(Assignment node) {
String varType = variables.get(node.getVariable());
if (varType == null) {
System.err.println("错误:未声明变量 " + node.getVariable());
} else {
System.out.println("赋值检查:" + node.getVariable() + " = " + node.getValue());
// 实际类型检查逻辑...
}
}
@Override
public void visit(FunctionCall node) {
System.out.println("函数调用检查:" + node.getFunctionName() +
"(" + String.join(", ", node.getArguments()) + ")");
// 实际参数检查逻辑...
}
}
// 具体访问者:代码生成
public class CodeGenerationVisitor implements AstVisitor {
private StringBuilder code = new StringBuilder();
public String getCode() {
return code.toString();
}
@Override
public void visit(VariableDeclaration node) {
code.append(node.getType()).append(" ")
.append(node.getName()).append(";\n");
}
@Override
public void visit(Assignment node) {
code.append(node.getVariable()).append(" = ")
.append(node.getValue()).append(";\n");
}
@Override
public void visit(FunctionCall node) {
code.append(node.getFunctionName())
.append("(")
.append(String.join(", ", node.getArguments()))
.append(");\n");
}
}
// 对象结构:AST 树
public class AstTree {
private List<AstNode> nodes = new ArrayList<>();
public void addNode(AstNode node) {
nodes.add(node);
}
public void accept(AstVisitor visitor) {
for (AstNode node : nodes) {
node.accept(visitor);
}
}
}
// 使用示例
public class CompilerDemo {
public static void main(String[] args) {
// 创建 AST 树
AstTree ast = new AstTree();
ast.addNode(new VariableDeclaration("int", "count"));
ast.addNode(new Assignment("count", "10"));
ast.addNode(new FunctionCall("print", List.of("\"Count: \"", "count")));
// 类型检查
TypeCheckVisitor typeChecker = new TypeCheckVisitor();
ast.accept(typeChecker);
// 代码生成
CodeGenerationVisitor codeGen = new CodeGenerationVisitor();
ast.accept(codeGen);
System.out.println("\n=== 生成代码 ===");
System.out.println(codeGen.getCode());
}
}
优缺点
优点:
- 开闭原则:新增操作无需修改元素类
- 单一职责原则:相关操作集中到访问者
- 算法与结构分离
- 跨类层次操作
- 状态累积(访问者可维护状态)
- 支持复杂对象结构
- 便于添加新操作
缺点:
- 违反封装原则(需暴露内部细节)
- 增加新元素类困难
- 破坏元素类接口稳定性
- 访问者依赖具体元素类
- 对象结构变更成本高
- 可能破坏元素类封装
应用场景
- 编译器抽象语法树处理
- 文档对象模型 (DOM) 处理
- 复杂报表生成
- 静态代码分析
- 机器人路径规划
- 游戏引擎实体处理
- 数据序列化/反序列化
- 财务系统计算引擎
与其他模式的关系
- 与组合模式:常一起处理树形结构
- 与解释器模式:访问者可用于解释器节点
- 与装饰器模式:都可扩展功能但机制不同
- 与迭代器模式:都可遍历对象结构
- 与策略模式:访问者封装算法策略
- 与责任链模式:访问者操作可委托
JDK 中的访问者模式
- Java NIO FileVisitor
- Java Annotation Processing (ElementVisitor)
- Java Compiler Tree API (TreeVisitor)
- Java Beans Introspector
- Java XML StreamReader
- Spring BeanDefinitionVisitor
- ASM 字节码框架 (ClassVisitor)
注意事项
- 元素接口稳定性
- 访问者状态管理
- 循环依赖处理
- 访问者性能优化
- 双分派技术实现
- 元素层次结构设计
- 访问者组合技术
- 安全控制(敏感操作)