Visitor Parttern
参考资料:
http://en.wikipedia.org/wiki/Visitor_Pattern wiki Visitor Pattern
Visitor Pattern:
模式背景: 今天在看<<java 并发编程 设计原则与模式>>中, 涉及到了Visitor模式. 在并发过程中, 当一个类中持有一个集合的时候, 如果需要遍历这个集合,
就不能单纯的考虑把整个集合返回给调用方.因为这样就给调用方提供了改变集合的操作. 试想,当你花费了大量的synchronized方法或者使用了多
么高明的lock-free算法进行性能上的优化,一旦你向外界返回了你要保护的对象,一切都将是一场"灾难". 矛盾的是, 你无法预测客户会使用什么样
的方式来进行对你集合中的元素进行操作. 困难如下:
private List<T> yourList; ..... public synchronized void doIterator(){ for(T t: yourList){ <how to do with element e ...??> } }
你如何确认对方需要做的操作时什么呢? 答案是没有. 基于面对对象的操作. 遍历的方式不变, 如果是串行的数据结构,无非就是循环. 如果是树状结构那么可能会不一样的遍历方式. 就如同蛋糕店中一样,他们给你返回了蛋糕,可是要怎么去吃,那是你自己的事情. 想到这点, 就应该清楚一件事情, 调用方的操作理应"自己知道".那么就变成了如下的形式:
private List<T> yourList;
.....
public void doIterator(Client : client){
for(T t: yourList){
client.doWithElemnt(t);
}
}
看到这里, 也许还有问题: 我怎么知道你是使用"doWithElemnt(..)"这个方式去消费呢? 换句话说,这是一种约定,java中,标准时用Interface来表示. 这样就形成了这样的一个"调用方"的结构:
当有client来遍历的时候,就调用它的visit方法,并把蛋糕给他. 事情就是这么简单!
客户的问题解决了,但是客户真正想要的是蛋糕,并不是整个"蛋糕池". 理解这一点很重要, 因为它进一步确定了我们不应该在doIterator方法中调用客户端.
而是应该在List<T>的每一个t上调用,代码进一步更改:
private List<T> yourList;
.....
public void doIterator(){
for(T t: yourList){
t.accept(visitor);
}
}
... T 中的accept方法如下:
visitor.visit(this);
如此一来, doIterator方法拜托了visitor的依赖. 那么,在doIterator方法中,有怎么样知道就是需要调用t.accept()方法呢? 一样地, Interface的标准化.
这么一来,就形成了visitor模式:
结束语: 可以使用java Reflection的反射机制使得Viditor模式更加具有灵活性.