code_money_guji's Blog

Happy coding

Visitor Parttern

code_money_guji posted @ 2011年3月07日 23:43 in 设计模式/架构 , 2607 阅读

参考资料:

        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模式更加具有灵活性.

 

 

 

 


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter