访问者模式,通过实现访问者的接口来访问实体当中相应的部分,下面先看代码。

1 搭建架子

我们先搭建一辆车,然后通过访问者模式去访问车的各个部分。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public interface CarElement {
    void accept(CarElementVisitor visitor);
}

public class Car implements CarElement {
    private final List<CarElement> elements;

    public Car() {
        this.elements = Arrays.asList(
                new Wheel("front left"), new Wheel("front right"),
                new Wheel("back left"), new Wheel("back right"),
                new Body(), new Engine()
        );
    }

    @Override
    public void accept(CarElementVisitor visitor) {
        for (CarElement element : elements) {
            element.accept(visitor);
        }
        visitor.visit(this);
    }
}

public class Body implements CarElement {
    @Override
    public void accept(CarElementVisitor visitor) {
        visitor.visit(this);
    }
}

public class Engine implements CarElement {
    @Override
    public void accept(CarElementVisitor visitor) {
        visitor.visit(this);
    }
}

public class Wheel implements CarElement {
    private final String name;

    public Wheel(final String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

2 实现访问者

架子搭建完毕,接下来实现通过访问去访问车的不同部分,我们实现两个,一个访问者就看一眼车身,另一个访问修一下轮胎。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public interface CarElementVisitor {
    void visit(Body body);
    void visit(Car car);
    void visit(Engine engine);
    void visit(Wheel wheel);
}

public class GlanceCarVisitor implements CarElementVisitor {
    @Override
    public void visit(Body body) {
    }

    @Override
    public void visit(Car car) {
        // 看一眼车身
        System.out.println("Glance at the car.");
    }

    @Override
    public void visit(Engine engine) {
    }

    @Override
    public void visit(Wheel wheel) {
    }
}

public class RepairTireVisitor implements CarElementVisitor {
    @Override
    public void visit(Body body) {
    }

    @Override
    public void visit(Car car) {
    }

    @Override
    public void visit(Engine engine) {
    }

    @Override
    public void visit(Wheel wheel) {
        // 修一下车胎
        System.out.println("Repair tire. [" + wheel.getName() + "]");
    }
}

测试一下效果

1
2
3
4
5
6
7
8
public class VisitorDemo {
    public static void main(final String[] args) {
        Car car = new Car();

        car.accept(new GlanceCarVisitor());
        car.accept(new RepairTireVisitor());
    }
}
1
2
3
4
5
Glance at the car.
Repair tire. [front left]
Repair tire. [front right]
Repair tire. [back left]
Repair tire. [back right]

3 应用场景

在 Java ASM 的库中就使用到了访问者模式(ASM 库是一个读取 Java class 文件修改字节码的库),他通过将文件读取进入内存中,然后对访问不同部分封装成了对应的 visit 方法,使用者只需要实现该方法就可以实现对文件的修改,非常方便。

在 ASM 中是将文件先读取进入了内存中,然后做的后续操作,但是如果文件非常大(几十个 GB),并且文件可以拆成多个部分读取和处理的话,那么可以读取一部分文件,然后通过 visitor 处理读取的内容,然后写回文件,这样就可以减少内存的使用,防止大文件把内存塞满了。

参考资料

  1. Wiki: Visitor pattern
    • 案例是从 wiki 搬过来的