访问者模式(Visitor Pattern)是一种行为型设计模式,它允许你在不修改对象结构的情况下向对象集合中添加新的操作。这一模式通过将操作分离到单独的访问者类中来实现,这些访问者类包含了对各种对象类型进行操作的逻辑。
结构
Visitor接口
定义一个或多个访问操作,每个操作对应一个对象类型,这些操作是抽象的,具体的实现由具体的访问者类提供。
java
package org.example.visitor;
public interface Visitor {
void visit(Circle circle);
void visit(Rectangle rectangle);
}
ConcreteVisitor 类实现 Vistor 接口
实现Visitor接口中定义的操作,对每个具体对象类型执行特定的行为。
java
package org.example.visitor;
public class AreaVisitor implements Visitor{
private double totalArea = 0;
@Override
public void visit(Circle circle) {
totalArea += Math.PI * circle.getRadius() * circle.getRadius();
}
@Override
public void visit(Rectangle rectangle) {
totalArea += rectangle.getWidth() * rectangle.getHeight();
}
public double getTotalArea() {
return totalArea;
}
}
Element接口
定义一个Accept操作,该操作接受一个访问者对象作为参数,Element接口是抽象的,具体的实现由具体的元素类提供。
java
package org.example.visitor;
public interface Element {
void accept(Visitor visitor);
}
ConcreteElement 类实现 Element 接口
实现Element接口中的Accept操作,当Accept操作被调用时,它调用访问者的相应方法,并将自身作为参数传递。
java
package org.example.visitor;
public class Rectangle implements Element{
private double width;
private double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
public double getWidth() {
return width;
}
public double getHeight() {
return height;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
java
package org.example.visitor;
public class Circle implements Element{
private double radius;
public Circle(double radius) {
this.radius = radius;
}
public double getRadius() {
return radius;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
ObjectStructure
是一个包含元素集合的类,它可以迭代这些元素并接受访问者的访问,ObjectStructure可以是复合对象、集合、列表或其他任何包含多个元素的复杂结构。
java
package org.example.visitor;
import java.util.ArrayList;
import java.util.List;
public class ObjectStructure {
private List<Element> elements = new ArrayList<>();
public void addElement(Element element) {
elements.add(element);
}
public void accept(Visitor visitor) {
for (Element element : elements) {
element.accept(visitor);
}
}
}
测试
java
package org.example.visitor;
public class VisitorMain {
public static void main(String[] args) {
ObjectStructure obj = new ObjectStructure();
obj.addElement(new Circle(10));
obj.addElement(new Rectangle(10, 20));
AreaVisitor visitor = new AreaVisitor();
obj.accept(visitor);
System.out.println("Total area:" + visitor.getTotalArea());
}
}
工作原理
- 客户端创建ObjectStructure实例,并填充它以包含需要操作的元素。
- 客户端创建一个或多个ConcreteVisitor实例,这些实例包含了对元素执行的具体操作。
- 客户端通过调用ObjectStructure的Accept方法,将ConcreteVisitor传递给元素集合。
- 每个元素调用其Accept方法,并接受ConcreteVisitor的访问。
- ConcreteVisitor对每个元素执行相应的操作(这些操作在ConcreteVisitor中定义)。
优点
- 增加新的操作容易:无需修改现有的类结构,只需增加新的ConcreteVisitor类即可。
- 符合开闭原则:可以在不修改现有代码的情况下扩展系统的功能。
- 操作与对象分离:操作被封装在访问者类中,与对象结构分离,提高了系统的灵活性和可维护性。
- 复杂对象结构上的操作:访问者模式使得对复杂对象结构(如树形结构、图形结构等)上的操作变得更加容易和直观。
缺点
- 增加新的元素类困难:每当增加新的元素类时,都需要在每个ConcreteVisitor类中增加相应的操作方法,这违反了开闭原则。
- 破坏封装性:访问者模式要求元素类公开其内部状态和结构给访问者,这可能会破坏对象的封装性。
- 具体元素对访问者公布细节:元素类需要知道访问者将对其执行哪些操作,这可能会导致元素类与访问者类之间的紧密耦合。
- 运行时开销:由于需要遍历整个对象结构并调用访问者的方法,访问者模式可能会带来一定的运行时开销。
应用场景
访问者模式适用于以下场景:
- 对象结构相对稳定:当对象结构(如树形结构、图形结构等)相对稳定,而需要对其中的元素进行多种不同的操作时。
- 操作易于变化:当系统中的操作经常需要变化或增加时,使用访问者模式可以避免频繁地修改对象结构。
- 跨多个类的操作:当需要对多个不同类的对象执行相同的操作时,可以使用访问者模式将这些操作封装在访问者类中。
- 累积与分离操作:当需要对对象结构中的元素进行累积(如求和、统计等)或分离(如过滤、分类等)操作时,访问者模式提供了一种优雅的实现方式。