跳转到内容
主菜单
主菜单
移至侧栏
隐藏
导航
首页
最近更改
随机页面
MediaWiki帮助
代码酷
搜索
搜索
中文(中国大陆)
外观
创建账号
登录
个人工具
创建账号
登录
未登录编辑者的页面
了解详情
贡献
讨论
编辑“︁
Java访问者模式
”︁
页面
讨论
大陆简体
阅读
编辑
编辑源代码
查看历史
工具
工具
移至侧栏
隐藏
操作
阅读
编辑
编辑源代码
查看历史
常规
链入页面
相关更改
特殊页面
页面信息
外观
移至侧栏
隐藏
您的更改会在有权核准的用户核准后向读者展示。
警告:
您没有登录。如果您进行任何编辑,您的IP地址会公开展示。如果您
登录
或
创建账号
,您的编辑会以您的用户名署名,此外还有其他益处。
反垃圾检查。
不要
加入这个!
{{DISPLAYTITLE:Java访问者模式}} '''访问者模式'''(Visitor Pattern)是[[设计模式]]中一种行为型模式,它允许你将算法与对象结构分离,从而在不修改现有对象结构的情况下定义新的操作。该模式通过将操作逻辑移动到独立的访问者类中,实现了对象结构与操作的解耦。 == 概述 == 访问者模式的核心思想是:''在对象结构中定义一个接受访问者的接口,让访问者能够处理结构中的各个元素''。这种模式特别适用于以下场景: * 对象结构包含许多不同类型的对象 * 需要对对象结构中的元素执行多种不相关的操作 * 不希望频繁修改对象结构的类定义 === 模式结构 === 访问者模式包含以下主要角色: * '''Visitor'''(访问者接口) - 声明访问操作 * '''ConcreteVisitor'''(具体访问者) - 实现访问操作 * '''Element'''(元素接口) - 定义接受访问者的方法 * '''ConcreteElement'''(具体元素) - 实现接受访问者的方法 * '''ObjectStructure'''(对象结构) - 维护元素集合,提供遍历接口 <mermaid> classDiagram class Visitor { +visitConcreteElementA(ConcreteElementA) +visitConcreteElementB(ConcreteElementB) } class ConcreteVisitor1 { +visitConcreteElementA(ConcreteElementA) +visitConcreteElementB(ConcreteElementB) } class Element { +accept(Visitor) } class ConcreteElementA { +accept(Visitor) +operationA() } class ConcreteElementB { +accept(Visitor) +operationB() } class ObjectStructure { -elements: List~Element~ +attach(Element) +detach(Element) +accept(Visitor) } Visitor <|-- ConcreteVisitor1 Element <|-- ConcreteElementA Element <|-- ConcreteElementB ConcreteElementA ..> Visitor : «uses» ConcreteElementB ..> Visitor : «uses» ObjectStructure o-- Element </mermaid> == 代码示例 == 以下是一个简单的访问者模式实现示例,展示如何计算购物车中不同类型商品的总价: <syntaxhighlight lang="java"> // 元素接口 interface ItemElement { int accept(ShoppingCartVisitor visitor); } // 具体元素类 class Book implements ItemElement { private int price; private String isbn; public Book(int cost, String isbn) { this.price = cost; this.isbn = isbn; } public int getPrice() { return price; } public String getIsbn() { return isbn; } @Override public int accept(ShoppingCartVisitor visitor) { return visitor.visit(this); } } class Fruit implements ItemElement { private int pricePerKg; private int weight; private String name; public Fruit(int priceKg, int wt, String nm) { this.pricePerKg = priceKg; this.weight = wt; this.name = nm; } public int getPricePerKg() { return pricePerKg; } public int getWeight() { return weight; } public String getName() { return name; } @Override public int accept(ShoppingCartVisitor visitor) { return visitor.visit(this); } } // 访问者接口 interface ShoppingCartVisitor { int visit(Book book); int visit(Fruit fruit); } // 具体访问者 class ShoppingCartVisitorImpl implements ShoppingCartVisitor { @Override public int visit(Book book) { int cost = book.getPrice(); // 书籍价格超过50元有5元折扣 if(cost > 50) { cost -= 5; } System.out.println("Book ISBN::" + book.getIsbn() + " cost = " + cost); return cost; } @Override public int visit(Fruit fruit) { int cost = fruit.getPricePerKg() * fruit.getWeight(); System.out.println(fruit.getName() + " cost = " + cost); return cost; } } // 对象结构 class ShoppingCart { private List<ItemElement> items = new ArrayList<>(); public void addItem(ItemElement item) { items.add(item); } public int calculateTotal(ShoppingCartVisitor visitor) { int sum = 0; for(ItemElement item : items) { sum += item.accept(visitor); } return sum; } } // 客户端代码 public class VisitorPatternDemo { public static void main(String[] args) { ShoppingCart cart = new ShoppingCart(); cart.addItem(new Book(100, "1234")); cart.addItem(new Book(40, "5678")); cart.addItem(new Fruit(10, 2, "Apple")); cart.addItem(new Fruit(5, 3, "Banana")); ShoppingCartVisitor visitor = new ShoppingCartVisitorImpl(); int total = cart.calculateTotal(visitor); System.out.println("Total Cost = " + total); } } </syntaxhighlight> '''输出结果:''' <pre> Book ISBN::1234 cost = 95 Book ISBN::5678 cost = 40 Apple cost = 20 Banana cost = 15 Total Cost = 170 </pre> == 实际应用场景 == 访问者模式在以下场景中特别有用: 1. '''编译器设计''':在抽象语法树(AST)上执行各种操作(类型检查、代码优化、代码生成等) 2. '''文档处理''':对文档结构(段落、表格、图片等)执行多种操作(拼写检查、字数统计、格式转换) 3. '''UI组件处理''':对UI组件树执行多种操作(渲染、布局计算、事件处理) 4. '''财务系统''':对不同类型的财务记录执行多种计算(税收计算、利润分析、审计) === 编译器案例 === 在编译器实现中,访问者模式可以优雅地处理抽象语法树上的各种操作: <syntaxhighlight lang="java"> // 表达式节点接口 interface ExprNode { void accept(ExprVisitor visitor); } // 具体表达式节点 class NumberNode implements ExprNode { private int value; public NumberNode(int value) { this.value = value; } public int getValue() { return value; } @Override public void accept(ExprVisitor visitor) { visitor.visit(this); } } class AddNode implements ExprNode { private ExprNode left, right; public AddNode(ExprNode l, ExprNode r) { left = l; right = r; } public ExprNode getLeft() { return left; } public ExprNode getRight() { return right; } @Override public void accept(ExprVisitor visitor) { visitor.visit(this); } } // 访问者接口 interface ExprVisitor { void visit(NumberNode node); void visit(AddNode node); } // 打印访问者 class PrintVisitor implements ExprVisitor { @Override public void visit(NumberNode node) { System.out.print(node.getValue()); } @Override public void visit(AddNode node) { node.getLeft().accept(this); System.out.print(" + "); node.getRight().accept(this); } } // 计算访问者 class CalculateVisitor implements ExprVisitor { private int result; public int getResult() { return result; } @Override public void visit(NumberNode node) { result = node.getValue(); } @Override public void visit(AddNode node) { node.getLeft().accept(this); int left = result; node.getRight().accept(this); result = left + result; } } // 使用示例 public class CompilerExample { public static void main(String[] args) { // 构建表达式树: 1 + (2 + 3) ExprNode expr = new AddNode( new NumberNode(1), new AddNode(new NumberNode(2), new NumberNode(3)) ); // 打印表达式 PrintVisitor printer = new PrintVisitor(); expr.accept(printer); // 输出: 1 + 2 + 3 // 计算表达式 CalculateVisitor calculator = new CalculateVisitor(); expr.accept(calculator); System.out.println(" = " + calculator.getResult()); // 输出: = 6 } } </syntaxhighlight> == 优缺点 == === 优点 === * '''开闭原则''':可以引入新的访问者而无需修改现有元素类 * '''单一职责原则''':将相关行为集中在一个访问者类中 * '''灵活性''':可以在运行时选择不同的访问者来执行不同的操作 * '''可扩展性''':容易添加新的操作 === 缺点 === * '''破坏封装''':访问者通常需要访问元素的内部状态 * '''元素接口变更困难''':添加新元素类型需要修改所有访问者 * '''可能违反依赖倒置原则''':具体元素类可能依赖于具体访问者 == 数学表示 == 访问者模式可以形式化表示为: <math> \begin{align} & \text{对于元素集合 } E = \{e_1, e_2, ..., e_n\} \\ & \text{和访问者集合 } V = \{v_1, v_2, ..., v_m\} \\ & \text{操作结果 } R = \sum_{i=1}^n e_i.\text{accept}(v_j) \quad \text{对于某个 } v_j \in V \end{align} </math> == 与其他模式的关系 == * 访问者模式可以看作'''命令模式'''的扩展版本,其对象可以操作多个不同类型的接收者 * 可以结合'''组合模式'''使用,对复杂对象结构进行操作 * 访问者模式经常与'''迭代器模式'''一起使用,遍历对象结构并对其元素执行操作 == 最佳实践 == 1. 当对象结构稳定但需要频繁添加新操作时,优先考虑访问者模式 2. 避免在元素接口中暴露过多内部状态,保持良好封装 3. 考虑使用访问者模式来实现"双重分发"机制 4. 对于简单的对象结构或很少变化的操作集,访问者模式可能过度设计 == 总结 == 访问者模式提供了一种优雅的方式来分离对象结构和操作逻辑,特别适合处理复杂对象结构上的多种操作。虽然它有一定的复杂性,但在适当的场景下能显著提高代码的可维护性和扩展性。理解并合理应用访问者模式,可以帮助开发者构建更加灵活和可扩展的软件系统。 [[Category:编程语言]] [[Category:Java]] [[Category:Java设计模式]]
摘要:
请注意,所有对代码酷的贡献均被视为依照知识共享署名-非商业性使用-相同方式共享发表(详情请见
代码酷:著作权
)。如果您不希望您的文字作品被随意编辑和分发传播,请不要在此提交。
您同时也向我们承诺,您提交的内容为您自己所创作,或是复制自公共领域或类似自由来源。
未经许可,请勿提交受著作权保护的作品!
取消
编辑帮助
(在新窗口中打开)