访问者(Visitor)是一种行为型模式,它表示一个作用于某对象结构中各元素的操作。它主要目的是在不修改现有对象结构的前提下,对对象添加新的操作。

譬如我们已经有了一个成熟的2D图形系统,里面包含父类Shape和它的子类Rectangle, Circle等,在这个系统中我们自然已经定义好了求面积求周长等各种操作。突然有一天,一个新的业务需求提出来了:各个类型都需要加一种优美的打印自己的方法。

由于既有代码工作良好,我们不想在各个子类分别去添加这么个方法实现,所以我们新建一个Visitor,让它去做这个事情。尽管我们不会对Shape及其子类有大的改动,但是还是需要稍微改一下,即添加accept方法,方法具体的实现交给具体的Vistor来做。

整个实现过程还是比较简单的,核心是把新加操作封装到Visitor和它具体的实现类里面,再对既有code增加一个打印方法。

  • Shape, Rectangle, Circle 是图形类,它们需要改的是增加accept方法,但这个方法参数是Viistor, 并且具体的实现使用的是visitor的beautifulPrint方法
  • Visitor, 接口,定义优美打印的方法,其中方法参数为各个Shape子类
  • PrintVisitor, 实现Visitor接口,里面的打印方法,参数是各个Shape 子类
  • VisitorDemo, 测试类
public abstract class Shape{
    public abstract void accept (Visitor visitor);

    // other old and stable codes ...
}
public class Rectangle extends Shape {
    private int length, width;

    public Rectangle(){}

    public Rectangle(int length, int width) {
        this.length = length;
        this.width = width;
    }

    public int getLength() {
        return length;
    }
 
    public int getWidth() {
        return width;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.beautifulPrint(this);
    }
}
public class Circle extends Shape {
    private int radius;

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


    public int getRadius() {
        return radius;
    }

    public void setRadius(int radius) {
        this.radius = radius;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.beautifulPrint(this);
    }
}
public interface Visitor {
    void beautifulPrint(Rectangle rectangle);
    void beautifulPrint(Circle circle);
}
public class PrintVisitor implements Visitor {
    @Override
    public void beautifulPrint(Rectangle rectangle) {
        int length = rectangle.getLength();
        int width = rectangle.getWidth();

        for (int i = 0; i <= width; i++) {
            for (int j = 0;j <= length; j++) {
                System.out.print(".  ");
            }
            System.out.println();
        }
    }

    @Override
    public void beautifulPrint(Circle circle) {
        int radius = circle.getRadius();

        for (int y = -radius; y <= radius; y++) {
            for (int x = -radius; x <= radius; x++) {
                if (x * x + y * y <= radius * radius) {
                    // 点(x,y)在圆内
                    System.out.print(".  ");
                } else {
                    System.out.print("   ");
                }
            }
            System.out.println();
        }

    }
}
public class VisitorDemo {
    public static void main(String[] args) {
        Rectangle rectangle = new Rectangle(12, 5);
        Circle circle = new Circle(5);

        Visitor printer = new PrintVisitor();
        System.out.println("\nPrint the rectangle:");
        printer.beautifulPrint(rectangle);

        System.out.println("\nPrint the circle:");
        printer.beautifulPrint(circle);
    }
}

结果如下

Print the rectangle:
.  .  .  .  .  .  .  .  .  .  .  .  .  
.  .  .  .  .  .  .  .  .  .  .  .  .  
.  .  .  .  .  .  .  .  .  .  .  .  .  
.  .  .  .  .  .  .  .  .  .  .  .  .  
.  .  .  .  .  .  .  .  .  .  .  .  .  
.  .  .  .  .  .  .  .  .  .  .  .  .  

Print the circle:
               .                 
      .  .  .  .  .  .  .        
   .  .  .  .  .  .  .  .  .     
   .  .  .  .  .  .  .  .  .     
   .  .  .  .  .  .  .  .  .     
.  .  .  .  .  .  .  .  .  .  .  
   .  .  .  .  .  .  .  .  .     
   .  .  .  .  .  .  .  .  .     
   .  .  .  .  .  .  .  .  .     
      .  .  .  .  .  .  .        
               .