C++编程 Visitor模式(访问者)

来源:互联网 发布:淘宝怎么看订单号 编辑:程序博客网 时间:2024/05/16 17:01

C++编程 Visitor模式(访问者)
        到底什么是"访问者"?
        说白了,就是"观察者+使用者"。我们日常使用的电子产品,其外包装就是一个"访问者"。当我们组装一个电路的时候,我们并有没有改变电路元件自身的特性,而是搭积木一样组合出了我们想要的功能,然后用一个黑色的盒子包装了起来,形成了一个产品。而用这个产品的人,看到的只是我这个"盒子"所提供的输入输出接口,他并没有直接访问里面的元件。
        这种设计模式和"抽象工厂"或者"工厂方法"有个本质的不同。工厂方法的核心内容是提供一个对象,这个对象继承了我的接口。而"访问者"模式并不提供这个对象的指针,不负责创建和销毁;而是提供一个经过组合的功能的"集合",它似乎是一个"对象",但是本身并不完成任何实际的功能,把实际对象本身隔离开了。当我需要提供某种新的功能的时候,我只需要设计一个新的"visitor"就可以了。
        再有一点,工厂方法返回的对象,他们的行为都是类似的;而不同的visitor之间要做的事情,可能千差万别,因为他可以操作不同类的对象。
        
        举一个例子,例如visitor是一个将军,他有一个厨子c2,有一个士兵c1。那么visitor可以设计成: 士兵攻击,厨子烧饭;也可以设计成: 厨子烧饭,士兵射击,士兵吃饭,厨子洗锅。这里,visitor就是将军发出的指令集合。士兵和厨子的能力范围并没有变,改变的只是一个visitor的方法集合与顺序。当然,士兵和厨子本身除了完成功能的能力之外,还要从"人"(Base)这个基类进行继承,并且实现"服从命令"(accetp)这个函数。这样的话,将军的命令就能在不同的下属之间顺序的进行了。下面是一个例子程序。
 

#include<stdio.h>
struct visitor;
struct Base{
  virtual void accept()=0;
};
struct c1:public Base{
  void accept();
  void f1(){printf("c1\n");}
};
struct c2:public Base{
  void accept();
  void f2(){printf("c2\n");}
};
struct visitor{
  void run(Base* pb){
    printf("run Base\n");
    pb->accept();
  }
};
void c1::accept(){
  f1();
}
void c2::accept(){
  f2();
}
int main(void){
  c1 o1;
  c2 o2;
  Base* p[2];
  p[0]=&o1;
  p[1]=&o2;
  visitor v;
  p[0]->accept();
  p[1]->accept();
  return 0;
}

        当然,这样的设计还有一个缺点,就是"士兵"c1和"厨子"c2本身的"接受命令"函数accept需要指定如何去完成命令。但是我们知道这个命令只应该来自将军。所以进一步改进这个程序,使得accept函数是按照将军的命令完陈任务。这样的话,指令代码就在visitor那里了,下属并不需要做任何功能上的修改。

#include<stdio.h>
struct visitor;
struct Base{
  virtual void accept(visitor* pv)=0;
};
struct c1:public Base{
  void accept(visitor* pv);
  void f1(){printf("c1\n");}
};
struct c2:public Base{
  void accept(visitor* pv);
  void f2(){printf("c2\n");}
};
struct visitor{
  void run(Base* pb){
    printf("run Base\n");
    pb->accept(this);
  }
  void run(c1* o1){o1->f1();}
  void run(c2* o2){o2->f2();}
};
void c1::accept(visitor* pv){
  pv->run(this);
}
void c2::accept(visitor* pv){
  pv->run(this);
}
int main(void){
  c1 o1;
  c2 o2;
  Base* p[2];
  p[0]=&o1;
  p[1]=&o2;
  visitor v;
  p[0]->accept(&v);
  p[1]->accept(&v);
  return 0;
}

运行结果是:
c1
c2

访问者模式的优点和缺点

访问者模式有如下的优点:

  1. 访问者模式使得增加新的操作变得很容易。如果一些操作依赖于一个复杂的结构对象的话,那么一般而言,增加新的操作会很复杂。而使用访问者模式,增加新的操作就意味着增加一个新的访问者类,因此,变得很容易。
  2. 访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个的节点类中。
  3. 访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成员类。迭代子只能访问属于同一个类型等级结构的成员对象,而不能访问属于不同等级结构的对象。访问者模式可以做到这一点。
  4. 积累状态。每一个单独的访问者对象都集中了相关的行为,从而也就可以在访问的过程中将执行操作的状态积累在自己内部,而不是分散到很多的节点对象中。这是有益于系统维护的优点。

访问者模式有如下的缺点:

  1. 增加新的节点类变得很困难。每增加一个新的节点都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作。
  2. 破坏封装。访问者模式要求访问者对象访问并调用每一个节点对象的操作,这隐含了一个对所有节点对象的要求:它们必须暴露一些自己的操作和内部状态。不然,访问者的访问就变得没有意义。由于访问者对象自己会积累访问操作所需的状态,从而使这些状态不再存储在节点对象中,这也是破坏封装的。