访问者模式是一种行为设计模式,它允许你将对象的操作与对象的结构分离开来。通过这种方式,可以在不改变对象结构的前提下,添加新的操作。
结构
- 访问者接口(Visitor): 定义了可以访问每个元素的操作。
public interface Visitor {
void visit(ElementA elementA);
void visit(ElementB elementB);
}
- 具体访问者(Concrete Visitor): 实现了访问者接口,定义了具体的访问操作。
public class ConcreteVisitor implements Visitor {
public void visit(ElementA elementA) {
// 对ElementA的操作
}
public void visit(ElementB elementB) {
// 对ElementB的操作
}
}
- 元素接口(Element): 定义了一个
accept
方法,用于接受访问者的访问。
public interface Element {
void accept(Visitor visitor);
}
- 具体元素(Concrete Element): 实现了元素接口,实现了
accept
方法。
public class ElementA implements Element {
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
public class ElementB implements Element {
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
- 对象结构(Object Structure): 包含元素对象的集合,提供接受访问者的方法。
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);
}
}
}
优点和缺点
优点:
- 分离关注点: 访问者模式使得数据结构和作用于结构上的操作之间解耦,可以方便地增加新的操作,而无需修改现有的类。
- 可扩展性强: 可以通过增加新的具体访问者类,轻松地增加新的操作,而不需要修改元素类的结构。
- 符合开闭原则: 可以在不修改现有代码的情况下,引入新的访问者类和元素类。
缺点:
- 增加新元素困难: 如果系统中的元素类经常变化,需要频繁添加新的访问者类,可能导致系统变得复杂。
示例
以下是一个访问者模式的示例,模拟了一个图形库,包含圆形和矩形两种图形,同时提供了计算面积和周长的操作。
// 访问者接口
public interface Visitor {
void visit(Circle circle);
void visit(Rectangle rectangle);
}
// 具体访问者类
public class AreaVisitor implements Visitor {
public void visit(Circle circle) {
double area = Math.PI * circle.getRadius() * circle.getRadius();
System.out.println("Circle Area: " + area);
}
public void visit(Rectangle rectangle) {
double area = rectangle.getWidth() * rectangle.getHeight();
System.out.println("Rectangle Area: " + area);
}
}
public class PerimeterVisitor implements Visitor {
public void visit(Circle circle) {
double perimeter = 2 * Math.PI * circle.getRadius();
System.out.println("Circle Perimeter: " + perimeter);
}
public void visit(Rectangle rectangle) {
double perimeter = 2 * (rectangle.getWidth() + rectangle.getHeight());
System.out.println("Rectangle Perimeter: " + perimeter);
}
}
// 元素接口
public interface Shape {
void accept(Visitor visitor);
}
// 具体元素类
public class Circle implements Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
public double getRadius() {
return radius;
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
public class Rectangle implements Shape {
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;
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// 对象结构
public class Drawing {
private List<Shape> shapes = new ArrayList<>();
public void addShape(Shape shape) {
shapes.add(shape);
}
public void accept(Visitor visitor) {
for (Shape shape : shapes) {
shape.accept(visitor);
}
}
}
// 客户端代码
public class Main {
public static void main(String[] args) {
Drawing drawing = new Drawing();
drawing.addShape(new Circle(5));
drawing.addShape(new Rectangle(3, 4));
Visitor areaVisitor = new AreaVisitor();
Visitor perimeterVisitor = new PerimeterVisitor();
System.out.println("Calculating Areas:");
drawing.accept(areaVisitor);
System.out.println("Calculating Perimeters:");
drawing.accept(perimeterVisitor);
}
}
在这个示例中,Visitor 接口定义了两个具体访问者类 AreaVisitor 和 PerimeterVisitor 共同的访问操作。Shape 接口定义了两个具体元素类 Circle 和 Rectangle 共同的接受访问者的方法。Drawing 类是对象结构,它包含了图形对象,并且提供了接受访问者的方法。在客户端代码中,我们可以轻松地计算图形的面积和周长,而无需修改图形类的结构。这展示了访问者模式的优势,即在不改变现有类的情况下,添加新的操作。