访问者模式(visitor pattern): 当你想要为一个对象的组合增加新的能力, 且封装并不重要时, 就使用访问者模式。
访问模式详解
类图:
角色说明:
抽象访问者角色(Visitor):声明一个或者多个方法操作,形成所有的具体访问者角色必须实现的接口。
具体访问者角色(ConcreteVisitor):实现抽象访问者所声明的接口。
抽象节点角色(Node):声明一个接受操作,接受一个访问者对象作为参数。
具体节点角色(ConcreteNode):实现了抽象节点所规定的接受操作。
结构对象角色(ObjectStructure):此角色可以遍历结构中所有的元素;可以提供一个高层次的接口让访问者对象可以访问每一个元素;也可以设计一个复合对象或者一个集合。
代码演示,抽象访问者类:
public interface Visitor {
/**
* 对应于节点A的访问操作
*/
public void visit(NodeA node);
/**
* 对应于节点B的访问操作
*/
public void visit(NodeB node);
}
具体访问者类A:
public class VisitorA implements Visitor {
/**
* 对应于NodeA的访问操作
*/
@Override
public void visit(NodeA node) {
System.out.println("访问者A访问:" + node.operationA());
}
/**
* 对应于NodeB的访问操作
*/
@Override
public void visit(NodeB node) {
System.out.println("访问者A访问:" + node.operationB());
}
}
具体访问者类B:
public class VisitorB implements Visitor {
/**
* 对应于NodeA的访问操作
*/
@Override
public void visit(NodeA node) {
System.out.println("访问者B访问:" + node.operationA());
}
/**
* 对应于NodeB的访问操作
*/
@Override
public void visit(NodeB node) {
System.out.println("访问者B访问:" + node.operationB());
}
}
抽象节点类:
public abstract class Node {
/**
* 接受访问的操作
*/
public abstract void accept(Visitor visitor);
}
具体节点类A:
public class NodeA extends Node {
/**
* 接受访问的操作
*/
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
/**
* NodeA特有的方法
*/
public String operationA() {
return "NodeA";
}
}
具体节点类B:
public class NodeB extends Node {
/**
* 接受访问的操作方法
*/
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
/**
* NodeB特有的方法
*/
public String operationB() {
return "NodeB";
}
}
结构对象类:
public class ObjectStructure {
private List<Node> nodes = new ArrayList<Node>();
/**
* 执行方法操作
*/
public void action(Visitor visitor) {
for (Node node : nodes) {
node.accept(visitor);
}
}
/**
* 添加一个新元素
*/
public void add(Node node) {
nodes.add(node);
}
}
客户端测试类:
public class Client {
public static void main(String[] args) {
// 创建一个结构对象
ObjectStructure os = new ObjectStructure();
// 给结构增加节点
os.add(new NodeA());
os.add(new NodeB());
// 创建一个访问者
Visitor visitor = new VisitorA();
os.action(visitor);
}
}
运行结果:
适用场景
现在中国经济发展起来了,小轿车已经可以进入家家户户了。现在市场上的汽车可以说是琳琅满目,我们买车的时候,通常是要先去试车的。试车有可以分为两种访问形式,一种只是参观;另一种就是实操。
结合访问者模式,参观和实操可以都看成是具体访问者角色(ConcreteVisitor),车身、车轮和引擎都可以看成具体节点角色(ConcreteNode),而以下的Car类就可以看成是结构对象角色(ObjectStructure)。
类图:
代码演示,抽象车元素访问者类:
/**
* 抽象访问者
* @author Layne
*
*/
public interface CarElementVisitor {
void visit(Body body);
void visit(Car car);
void visit(Engine engine);
void visit(Wheel wheel);
}
参观汽车的访问者类:
/**
* 参观汽车的访问者
* @author Layne
*
*/
public class CarElementPrintVisitor implements CarElementVisitor {
public void visit(Body body) {
System.out.println("参观车身");
}
public void visit(Car car) {
System.out.println("参观汽车");
}
public void visit(Engine engine) {
System.out.println("参观引擎");
}
public void visit(Wheel wheel) {
System.out.println("参观"+wheel.getName());
}
}
实操汽车的访问者类:
/**
* 实操汽车的访问者
* @author Layne
*
*/
public class CarElementDoVisitor implements CarElementVisitor {
public void visit(Body body) {
System.out.println("驾驶汽车");
}
public void visit(Car car) {
System.out.println("开始实操汽车");
}
public void visit(Wheel wheel) {
System.out.println("踢一下"+wheel.getName());
}
public void visit(Engine engine) {
System.out.println("开启汽车引擎");
}
}
抽象汽车元素类:
public interface CarElement {
/**
* 接受操作方法
* @param visitor 访问者
*/
void accept(CarElementVisitor visitor);
}
汽车元素车身类:
public class Body implements CarElement {
/**
* 接受访问者参观汽车
*/
public void accept(CarElementVisitor visitor) {
visitor.visit(this);
}
}
汽车元素车轮类:
public class Wheel implements CarElement {
private String name;
public Wheel(String name) {
this.name = name;
}
public String getName() {
return name;
}
/**
* 接受访问者参观车轮
*/
public void accept(CarElementVisitor visitor) {
visitor.visit(this);
}
}
汽车元素引擎类:
public class Engine implements CarElement {
/**
* 接受访问者参观引擎
*/
public void accept(CarElementVisitor visitor) {
visitor.visit(this);
}
}
结构对象汽车类:
public class Car {
private List<CarElement> elements = new ArrayList<>();
public void add(CarElement carElement) {
elements.add(carElement);
}
public void action(CarElementVisitor visitor) {
//开始访问汽车
visitor.visit(this);
//访问汽车的各部分
for (CarElement elem : elements) {
elem.accept(visitor);
}
}
}
客户端测试类:
public class Client {
public static void main(final String[] args) {
Car car = new Car();
// System.out.println("参观者开始参观汽车:");
// car.add(new Body());
// car.add(new Wheel("四个车轮"));
// car.add(new Engine());
// car.action(new CarElementPrintVisitor());
System.out.println("参观者开始实操汽车:");
car.add(new Wheel("四个车轮"));
car.add(new Engine());
car.add(new Body());
car.action(new CarElementDoVisitor());
}
}
运行结果:
访问者模式优点:
- 好的扩展性,能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
- 通过访问者可以定义整个对象结构通用的功能,从而提高复用程度。
- 通过访问者可以分离无关的行为,把相关的行为封装在一起,构成一个访问者,这样每一个访问者的功能都比较单一。
缺点:
- 访问者模式不适用于对象结构中类经常变化的情况,如果对象结构发生改变,访问者的接口和访问者的实现都要发生相应的修改,成本太高。
- 访问者模式通常需要对象结构开放内部数据给访问者和结构对象角色(ObjectStructrue),这就破坏了对象的封装性。