声明: 这种模式产生的原因是主要解决:稳定的数据结构和易变的操作耦合问题。
在Java中, 一个鲜明的例子: 大家都知道, Collection好象是个黑色大染缸, 本来有各种鲜明类型特征的对象一旦放入后, 再取出时, 这些类型信息就消失了(仅仅知道父类型而无法确定具体的子类型). 那么我们势必要用If-Else或Switch这种提交结合instanceof来判断.
访问者模式适用于数据结构(数据结构多采用Composite模式封装)相对稳定的系统,它把数据结构和作用于数据结构上的操作之间的耦合解脱开,使得操作集合可以相对自由地演化。访问者模式实际上是分离了集合中的元素和对这些元素进行的操作。不过使用访问者模式是有前提的, 尽量确保Visitor很少变化, 当数据结构变化时,他的visitor接口及其实现都要改变.
抽象访问者(Visitor)角色: 定义接口, 声明一个或多个访问操作.
具体访问者(ConcreteVisitor)角色: 实现抽象访问者所声明的接口, 也就是抽象访问者所声明的各个访问操作. 类似一个回调委托.
抽象被访问者(Acceptor)角色: 声明一个接受操作, 接受一个访问者对象作为一个参数.
具体被访问者(ConcreteAcceptor)角色: 实现抽象被访问者所规定的接受操作. 一般是容器集合类的元素节点.
对象结构(ObjectStructure)角色: 可以遍历结构中的所有元素, 提供一个接口让访问者对象都可以访问每一个元素.
例子1:
public interface Acceptor {
void accept(Visitor visitor);
}
public interface Visitor {
void visit(String str);
void visit(int i);
void visit(BigInteger integer);
}
public class Strings implements Acceptor {
private String value;
public Strings(String value) {
this.value = value;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(value);
}
}
public class Integers implements Acceptor {
private int value;
public Integers(int value) {
this.value = value;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(value);
}
}
public class BigIntegers implements Acceptor {
private BigInteger value;
public BigIntegers(BigInteger value) {
this.value = value;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(value);
}
}
public class DefaultVisitor implements Visitor {
@Override
public void visit(String str) {
System.out.println("String -> " + str);
}
@Override
public void visit(int i) {
System.out.println("int -> " + i);
}
@Override
public void visit(BigInteger integer) {
System.out.println("BigInteger -> " + integer.toString());
}
}
public class Main {
public static void main(String[] args) {
ArrayList<Acceptor> list = new ArrayList<>();
list.add(new Strings("66666"));
list.add(new Integers(3234));
list.add(new BigIntegers(new BigInteger("232342324")));
DefaultVisitor visitor = new DefaultVisitor();
for (Acceptor acceptor : list) {
acceptor.accept(visitor);
}
}
}
例子2:
public interface Acceptor {
void accept(Visitor visitor);
String getType();
}
public interface Visitor {
void visit(Strings str);
void visit(Integers i);
void visit(BigIntegers integer);
}
public class Strings implements Acceptor {
private String value;
public Strings(String value) {
this.value = value;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
@Override
public String getType() {
return "Strings";
}
public String getString() {
return value;
}
}
public class Integers implements Acceptor {
private int value;
public Integers(int value) {
this.value = value;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
@Override
public String getType() {
return "Integers";
}
public int getInteger() {
return value;
}
}
public class BigIntegers implements Acceptor {
private BigInteger value;
public BigIntegers(BigInteger value) {
this.value = value;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
@Override
public String getType() {
return "BigIntegers";
}
public BigInteger getBigInteger() {
return value;
}
}
public class VisitorAdapter implements Visitor {
@Override
public void visit(Strings str) {
// no-op
}
@Override
public void visit(Integers i) {
// empty-implementation
}
@Override
public void visit(BigIntegers integer) {
// do nothing
}
}
public class DefaultVisitor extends VisitorAdapter {
@Override
public void visit(Strings str) {
System.out.println(str.getType() + " -> " + str.getString());
}
@Override
public void visit(Integers i) {
System.out.println(i.getType() + " -> " + i.getInteger());
}
}
public class BigIntegerVisitor extends VisitorAdapter {
@Override
public void visit(BigIntegers i) {
System.out.println(i.getType() + " -> " + i.getBigInteger());
}
}
public class ObjectStructure {
private final List<Acceptor> list = new ArrayList<>();
public void add(Acceptor acceptor) {
list.add(acceptor);
}
public void remove(Acceptor acceptor) {
list.remove(acceptor);
}
public void visitAll(Visitor visitor) {
for (Acceptor acceptor : list) {
acceptor.accept(visitor);
}
}
}
public class Client {
public static void main(String[] args) {
ObjectStructure objectStructure = new ObjectStructure();
objectStructure.add(new Strings("66666"));
objectStructure.add(new Integers(3234));
objectStructure.add(new BigIntegers(new BigInteger("232342324")));
System.out.println("DefaultVisitor-----------------------");
Visitor visitor = new DefaultVisitor();
objectStructure.visitAll(visitor);
System.out.println("BigIntegerVisitor-----------------------");
visitor = new BigIntegerVisitor();
objectStructure.visitAll(visitor);
}
}
仔细比对例1和例2, 通过VisitorAdapter, 能适度减弱访问者的缺陷, 如果没有它, 增加一个被访问者节点, 将影响所有具体访问者.
引用陈词(http://www.cnblogs.com/zhenyulu/articles/79719.html)
面向对象的设计原则中最重要的便是所谓的”开一闭”原则。一个软件系统的设计应当尽量做到对扩展开放,对修改关闭。达到这个原则的途径就是遵循”对变化的封装”的原则。
很多系统可以按照算法和数据结构分开,也就是说一些对象含有算法,而另一些对象含有数据,接受算法的操作。如果这样的系统有比较稳定的数据结构,又有易于变化的算法的话,使用访问者模式就是比较合适的,因为访问者模式使得算法操作的增加变得容易。
反过来,如果这样一个系统的数据结构对象易于变化,经常要有新的数据对象增加进来的话,就不适合使用访问者模式。因为在访问者模式中增加新的节点很困难,要涉及到在抽象访问者和所有的具体访问者中增加新的方法。
之所以备注这篇笔记, 是在研究ANTLR的时候, 突然看明白了visitor模式, 然后我上网查考了很多这方面的博客(已经不记得有多少了), 将有感触的文字拷贝了过来. 除了两个例子, 无原创, 也不指着这个盈利或出名, 如果作者不幸看到, 恰巧又不太高兴, 请直接找我吧, 感谢您的文字!