访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构则可以保持不变。上面的术语不是很好理解,我们来看看下面的场景,就会觉得很熟悉了。
场景描述
假设有下面现在需要建造一辆汽车,简单的实现方式如下:
创建汽车部件抽象接口
123public interface CarComponent {public void printMessage();}创建轮胎部件
12345678910111213public class Wheel implements CarComponent {public void printMessage() {System.out.println("This is a wheel");}/*** 这时Wheel和Engine不同的方法*/public void doWheel() {System.out.println("Checking wheel...");}}创建引擎部件
12345678910111213public class Engine implements CarComponent {public void printMessage() {System.out.println("This is a engine");}/*** 这时Wheel和Engine不同的方法*/public void doEngine() {System.out.println("Testing this engine...");}}创建汽车
12345678910111213141516171819public 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。
结合代码来理解如上几种角色:
创建抽象访问者
123456789101112/*** 因为有四个元素要访问,所以定义了四个访问方法*/public interface Visitor {void visit(final Body body);void visit(final Car car);void visit(final Engine Engine);void visit(final Wheel wheel);}创建抽象元素接口
123456public interface Element {/*** 接收一个观察者对象*/public void accept(final Visitor visitor);}创建真实汽车部件元素:Body、Engine、Wheel
123456public class Body implements Element {public void accept(Visitor visitor) {visitor.visit(this);}}123456public class Engine implements Element {public void accept(Visitor visitor) {visitor.visit(this);}}12345678910111213141516public class Wheel implements Element {private String name;public Wheel(String name) {this.name = name;}public String getName() {return name;}public void accept(Visitor visitor) {visitor.visit(this);}}创建汽车元素
123456789101112131415161718192021public 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());}public void accept(Visitor visitor) {for (Element element : elements) {element.accept(visitor);}visitor.visit(this);}}创建具体的Do访问者
123456789101112131415161718192021public class DoVisitor implements Visitor {public void visit(Body body) {System.out.println("Moving my body");}public void visit(Car car) {System.out.println("Starting my car");}public void visit(Engine engine) {System.out.println("Starting my engine");}public void visit(Wheel wheel) {System.out.println("Kicking my " + wheel.getName() + " wheel");}}创建具体的Print访问者
123456789101112131415161718192021public class PrintVisitor implements Visitor {public void visit(Body body) {System.out.println("Visiting body");}public void visit(Car car) {System.out.println("Visiting car");}public void visit(Engine engine) {System.out.println("Visiting engine");}public void visit(Wheel wheel) {System.out.println("Visiting " + wheel.getName() + " wheel");}}测试
1234567public 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与模式》,向作者致敬。