bboyjing's blog

HeadFirst设计模式二十四【访问者模式】

访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构则可以保持不变。上面的术语不是很好理解,我们来看看下面的场景,就会觉得很熟悉了。

场景描述

假设有下面现在需要建造一辆汽车,简单的实现方式如下:

  1. 创建汽车部件抽象接口

    1
    2
    3
    public interface CarComponent {
    public void printMessage();
    }
  2. 创建轮胎部件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class Wheel implements CarComponent {
    @Override
    public void printMessage() {
    System.out.println("This is a wheel");
    }
    /**
    * 这时Wheel和Engine不同的方法
    */
    public void doWheel() {
    System.out.println("Checking wheel...");
    }
    }
  3. 创建引擎部件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class Engine implements CarComponent {
    @Override
    public void printMessage() {
    System.out.println("This is a engine");
    }
    /**
    * 这时Wheel和Engine不同的方法
    */
    public void doEngine() {
    System.out.println("Testing this engine...");
    }
    }
  4. 创建汽车

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public class Car {
    private List<CarComponent> components;
    public Car() {
    this.components = new ArrayList<>();
    }
    /**
    * 这里需要根据CarComponent具体的类型去做不同的事情
    */
    public void setComponent(CarComponent component) {
    components.add(component);
    if (component instanceof Wheel) {
    ((Wheel) component).doWheel();
    } else {
    ((Engine) component).doEngine();
    }
    }
    }

instanceof虽然简单,但是当零件特别多的时候,要写一堆的if/else,很不友好。下面就来学习访问者模式是如何解决这个问题的。

代码演示

访问者模式所涉及的角色如下:

  • 抽象访问者(Visitor):声明了一个或多个访问操作。
  • 具体访问者(Concrete Visitor):实现抽象访问者所声明的接口,也就是抽象访问者所声明的各个访问操作。
  • 抽象元素(Element):声明一个接受操作,接受一个访问者对象作为参数
  • 具体的元素(ConcreteElement):实现了抽象元素所规定的接受操作。
  • 结构对象(ObjectStructure):有如下职责,可以遍历结构中的所有元素;如果需要,提供一个高层次的接口让访问者对象可以访问每一个元素;如果需要,可以设计成一个复合对象或者一个聚合,如List或Set。

结合代码来理解如上几种角色:

  1. 创建抽象访问者

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    /**
    * 因为有四个元素要访问,所以定义了四个访问方法
    */
    public interface Visitor {
    void visit(final Body body);
    void visit(final Car car);
    void visit(final Engine Engine);
    void visit(final Wheel wheel);
    }
  2. 创建抽象元素接口

    1
    2
    3
    4
    5
    6
    public interface Element {
    /**
    * 接收一个观察者对象
    */
    public void accept(final Visitor visitor);
    }
  3. 创建真实汽车部件元素:Body、Engine、Wheel

    1
    2
    3
    4
    5
    6
    public class Body implements Element {
    @Override
    public void accept(Visitor visitor) {
    visitor.visit(this);
    }
    }
    1
    2
    3
    4
    5
    6
    public class Engine implements Element {
    @Override
    public void accept(Visitor visitor) {
    visitor.visit(this);
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public class Wheel implements Element {
    private String name;
    public Wheel(String name) {
    this.name = name;
    }
    public String getName() {
    return name;
    }
    @Override
    public void accept(Visitor visitor) {
    visitor.visit(this);
    }
    }
  4. 创建汽车元素

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public class Car implements Element {
    private List<Element> elements;
    public Car() {
    this.elements = new ArrayList<>();
    this.elements.add(new Wheel("front left"));
    this.elements.add(new Wheel("front right"));
    this.elements.add(new Wheel("back left"));
    this.elements.add(new Wheel("back right"));
    this.elements.add(new Engine());
    this.elements.add(new Body());
    }
    @Override
    public void accept(Visitor visitor) {
    for (Element element : elements) {
    element.accept(visitor);
    }
    visitor.visit(this);
    }
    }
  5. 创建具体的Do访问者

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public class DoVisitor implements Visitor {
    @Override
    public void visit(Body body) {
    System.out.println("Moving my body");
    }
    @Override
    public void visit(Car car) {
    System.out.println("Starting my car");
    }
    @Override
    public void visit(Engine engine) {
    System.out.println("Starting my engine");
    }
    @Override
    public void visit(Wheel wheel) {
    System.out.println("Kicking my " + wheel.getName() + " wheel");
    }
    }
  6. 创建具体的Print访问者

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public class PrintVisitor implements Visitor {
    @Override
    public void visit(Body body) {
    System.out.println("Visiting body");
    }
    @Override
    public void visit(Car car) {
    System.out.println("Visiting car");
    }
    @Override
    public void visit(Engine engine) {
    System.out.println("Visiting engine");
    }
    @Override
    public void visit(Wheel wheel) {
    System.out.println("Visiting " + wheel.getName() + " wheel");
    }
    }
  7. 测试

    1
    2
    3
    4
    5
    6
    7
    public class Test {
    public static void main(String[] args) {
    final Element car = new Car();
    car.accept(new DoVisitor());
    car.accept(new PrintVisitor());
    }
    }

这个模式的核心在于,具体的元素接收一个观察者,观察者里则定义了访问所有元素的方法。正是因为,把所有元素的访问方式都交给了访问者,所以消除了之前的if/else硬编码。本章节就到这里了,同时设计模式这个系列的学习也告一段落了。整个学习过程使用了两本书:《Head First设计模式》和《Java与模式》,向作者致敬。