【访问者模式】

1 阅读4分钟

定义

访问者模式(Visitor Pattern)是一种行为型设计模式,它允许一个或者多个操作应用到一组对象上,解耦操作和对象本身。访问者模式可以在不改变对象结构的情况下,为对象结构中的每个元素添加新的操作

以下是访问者模式的主要特点和定义:

主要角色

  1. Visitor(访问者)

    • 定义对每个元素的访问操作。
    • 通常包含多个访问方法,每个方法对应一个具体的元素类型。
  2. Element(元素)

    • 定义一个接受访问者的接口。
  3. ConcreteElement(具体元素)

    • 实现元素接口,提供接受访问者的具体实现。
  4. ObjectStructure(对象结构)

    • 可以是一个组合结构或者集合,用于存储元素对象,并提供遍历元素的方法,以便访问者可以访问每个元素。

业务

假设我们有一个图形绘制系统,其中有不同类型的图形(圆形、矩形、三角形),我们希望能够对这些图形进行不同的操作,如计算面积和绘制图形。我们可以使用访问者模式来实现这个功能。

classDiagram
    class Shape {
        +accept(Visitor) : void
    }
    class Circle {
        -double radius
        +Circle(double)
        +getRadius() : double
        +accept(Visitor) : void
    }
    class Rectangle {
        -double width
        -double height
        +Rectangle(double, double)
        +getWidth() : double
        +getHeight() : double
        +accept(Visitor) : void
    }
    class Triangle {
        -double side1
        -double side2
        -double side3
        +Triangle(double, double, double)
        +getSide1() : double
        +getSide2() : double
        +getSide3() : double
        +accept(Visitor) : void
    }
    class Visitor {
        +visit(Circle) : void
        +visit(Rectangle) : void
        +visit(Triangle) : void
    }
    class AreaVisitor {
        +visit(Circle) : void
        +visit(Rectangle) : void
        +visit(Triangle) : void
    }
    class DrawVisitor {
        +visit(Circle) : void
        +visit(Rectangle) : void
        +visit(Triangle) : void
    }
    Shape <|-- Circle
    Shape <|-- Rectangle
    Shape <|-- Triangle
    Visitor <|-- AreaVisitor
    Visitor <|-- DrawVisitor

一、定义元素接口(Element)

interface Shape {
    void accept(Visitor visitor);
}

二、定义具体元素类(ConcreteElement)

  1. 圆形:
class Circle implements Shape {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    public double getRadius() {
        return radius;
    }
}
  1. 矩形:
class Rectangle implements Shape {
    private double width;
    private double height;

    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    public double getWidth() {
        return width;
    }

    public double getHeight() {
        return height;
    }
}
  1. 三角形:
class Triangle implements Shape {
    private double side1;
    private double side2;
    private double side3;

    public Triangle(double side1, double side2, double side3) {
        this.side1 = side1;
        this.side2 = side2;
        this.side3 = side3;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    public double getSide1() {
        return side1;
    }

    public double getSide2() {
        return side2;
    }

    public double getSide3() {
        return side3;
    }
}

三、定义访问者接口(Visitor)

interface Visitor {
    void visit(Circle circle);
    void visit(Rectangle rectangle);
    void visit(Triangle triangle);
}

四、定义具体访问者类(ConcreteVisitor)

  1. 计算面积的访问者:
class AreaVisitor implements Visitor {
    @Override
    public void visit(Circle circle) {
        double area = Math.PI * circle.getRadius() * circle.getRadius();
        System.out.println("Area of circle: " + area);
    }

    @Override
    public void visit(Rectangle rectangle) {
        double area = rectangle.getWidth() * rectangle.getHeight();
        System.out.println("Area of rectangle: " + area);
    }

    @Override
    public void visit(Triangle triangle) {
        double s = (triangle.getSide1() + triangle.getSide2() + triangle.getSide3) / 2;
        double area = Math.sqrt(s * (s - triangle.getSide1()) * (s - triangle.getSide2()) * (s - triangle.getSide3));
        System.out.println("Area of triangle: " + area);
    }
}
  1. 绘制图形的访问者:
class DrawVisitor implements Visitor {
    @Override
    public void visit(Circle circle) {
        System.out.println("Drawing circle with radius " + circle.getRadius());
    }

    @Override
    public void visit(Rectangle rectangle) {
        System.out.println("Drawing rectangle with width " + rectangle.getWidth() + " and height " + rectangle.getHeight());
    }

    @Override
    public void visit(Triangle triangle) {
        System.out.println("Drawing triangle with sides " + triangle.getSide1() + ", " + triangle.getSide2() + ", " + triangle.getSide3());
    }
}

五、使用访问者模式

public class VisitorPatternExample {
    public static void main(String[] args) {
        Shape circle = new Circle(5);
        Shape rectangle = new Rectangle(4, 6);
        Shape triangle = new Triangle(3, 4, 5);

        Visitor areaVisitor = new AreaVisitor();
        Visitor drawVisitor = new DrawVisitor();

        circle.accept(areaVisitor);
        circle.accept(drawVisitor);

        rectangle.accept(areaVisitor);
        rectangle.accept(drawVisitor);

        triangle.accept(areaVisitor);
        triangle.accept(drawVisitor);
    }
}

在这个例子中,Shape是元素接口,CircleRectangleTriangle是具体元素类。Visitor是访问者接口,AreaVisitorDrawVisitor是具体访问者类。每个具体元素类实现了accept方法,该方法接受一个访问者对象,并调用访问者对象的相应方法。每个具体访问者类实现了访问者接口中的方法,对不同类型的元素进行特定的操作。通过这种方式,我们可以在不改变元素类的情况下,为元素类添加新的操作,实现了访问者模式的设计。

总结

使用场景

  1. 当一个对象结构包含很多不同类型的对象,并且需要对这些对象执行一些依赖于对象具体类型的操作时。

    • 例如,一个图形编辑软件中,有不同类型的图形元素(圆形、矩形、三角形等),需要对这些图形元素进行不同的操作,如绘制、计算面积等。可以使用访问者模式,为每种操作定义一个访问者,然后让图形元素接受这些访问者的访问。
  2. 当需要对一个对象结构进行一些复杂的操作,而这些操作不适合放在对象结构的类中时。

    • 访问者模式可以将这些复杂的操作封装在访问者类中,使得对象结构的类更加简洁。

优点

  1. 增加新的操作很容易。

    • 只需要添加一个新的访问者类,而不需要修改对象结构的类。
  2. 分离了对象结构和操作。

    • 使得操作的添加和修改更加灵活,不会影响到对象结构的代码。

缺点

  1. 增加新的元素类型比较困难。

    • 需要修改每个访问者类,添加对新元素类型的处理方法。
  2. 破坏了对象的封装性。

    • 访问者需要访问对象的内部状态,这可能会破坏对象的封装性。