一、什么是访问者模式?
访问者模式是一种行为型设计模式,它允许你在不修改现有代码的情况下添加新的行为,通过将算法与对象结构分离,来实现对数据结构中的元素进行新的操作。访问者模式的核心思想是,将数据结构与算法分离开来,使得数据结构可以保持不变,而算法可以根据需要自由地变化。
访问者模式适用于数据结构相对稳定,但其中操作或算法需要频繁修改的情况,例如编译器的代码生成、文档解析器等。
二、访问者模式的角色
在访问者模式中,主要有以下角色:
- 抽象访问者(Visitor):定义一个访问者可以访问哪些元素,并为每种元素都提供一个访问方法,从而对元素进行不同的操作。
- 具体访问者(ConcreteVisitor):实现抽象访问者中定义的访问方法,完成对元素的具体操作。
- 抽象元素(Element):定义一个接受访问者访问的接口,从而使得访问者可以访问元素。
- 具体元素(ConcreteElement):实现抽象元素中定义的接口,即实现接受访问者的访问方法。
- 对象结构(Object Structure):由多个不同类型的元素组成的复杂对象,提供让访问者访问各个元素的接口。
- 客户端(Client):通过访问者来访问对象结构中的元素,完成不同的操作。
三、访问者模式的实现
下面给出一个简单的访问者模式的 Java 示例。假设有一个图形类 Shape,它有三种类型:圆形、矩形和三角形。现在需要对这三种图形进行不同的操作,例如计算面积、计算周长等,同时又不希望在 Shape 类中添加过多的方法。这时候,可以使用访问者模式。
定义抽象访问者 Visitor
代码语言:javascript复制public interface Visitor {
void visit(Circle circle);
void visit(Rectangle rectangle);
void visit(Triangle triangle);
}
定义具体访问者 AreaVisitor 和 PerimeterVisitor
代码语言:javascript复制// Circle、Rectangle、Triangle 分别为具体元素
public class PerimeterVisitor implements Visitor {
@Override
public void visit(Circle circle) {
System.out.println("计算圆形周长:" 2 * Math.PI * circle.getRadius());
}
@Override
public void visit(Rectangle rectangle) {
System.out.println("计算矩形周长:" 2 * (rectangle.getWidth() rectangle.getHeight()));
}
@Override
public void visit(Triangle triangle) {
double a = triangle.getA();
double b = triangle.getB();
double c = triangle.getC();
System.out.println("计算三角形周长:" (a b c));
}
}
// Element 接口,定义 accept 方法,接受 Visitor 访问
public interface Element {
void accept(Visitor visitor);
}
// Circle、Rectangle、Triangle 分别为具体元素,实现 accept 方法
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);
}
}
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);
}
}
public class Triangle implements Element {
private double a;
private double b;
private double c;
public Triangle(double a, double b, double c) {
this.a = a;
this.b = b;
this.c = c;
}
public double getA() {
return a;
}
public double getB() {
return b;
}
public double getC() {
return c;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// ObjectStructure 对象结构,存储多个 Element
public class ObjectStructure {
private List<Element> elements = new ArrayList<>();
public void addElement(Element element) {
elements.add(element);
}
public void removeElement(Element element) {
elements.remove(element);
}
public void accept(Visitor visitor) {
for (Element element : elements) {
element.accept(visitor);
}
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Circle circle = new Circle(5);
Rectangle rectangle = new Rectangle(3, 4);
Triangle triangle = new Triangle(3, 4, 5);
ObjectStructure objectStructure = new ObjectStructure();
objectStructure.addElement(circle);
objectStructure.addElement(rectangle);
objectStructure.addElement(triangle);
AreaVisitor areaVisitor = new AreaVisitor();
objectStructure.accept(areaVisitor);
PerimeterVisitor perimeterVisitor = new PerimeterVisitor();
objectStructure.accept(perimeterVisitor);
}
}
在示例代码中,Visitor 接口定义了访问三种元素的 visit 方法,具体访问者 AreaVisitor 和 PerimeterVisitor 实现了 visit 方法。Element 接口定义了 accept 方法,具体元素 Circle、Rectangle、Triangle 实现了 accept 方法,接受 Visitor 访问。ObjectStructure 对象结构存储多个 Element,提供添加、删除、接受 Visitor 访问等方法。客户端 Client 构造具体元素和 ObjectStructure 对象结构,并使用具体访问者 AreaVisitor 和 PerimeterVisitor 访问元素。
访问者模式的优缺点如下:
优点:
- 可以在不修改元素类的情况下添加新的操作,符合开闭原则;
- 将相关行为集中在访问者中,分离了行为和元素,提高了系统的可扩展性和灵活性;
- 访问者模式可以对元素进行一些复杂的操作,尤其是在集合类元素的处理上,可以避免迭代器造成的性能问题。
缺点:
- 增加新的元素类需要修改访问者接口,违反了开闭原则;
- 具体元素对访问者公布细节,破坏了封装性;
- 访问者模式中元素的增加和删除比较困难。