您的位置:首页 > 其它

Visitor 访问者模式

2009-12-17 20:39 302 查看
     Vistior 访问者模式:表示一个作用于某对象结构的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
Visitor 访问者模式适用于:
1.一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
2.需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。Visitor 使得你可以将相关的操作集中起来定义在一个类中。当该对象结构被裉多应用共享时,用 Visitor 模式让每个应用仅包含需要用到的操作。
3.定义对象结构的类很少改变,但经常需要在此结构上定义新的操作,改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好。
Visitor 访问者模式的通用结构如下:




参与者:
Visitor:访问者,为该对象结构中 ConcreteElement 的每一个类声明一个 Visit 操作。该操作的名字和特征标识了发送 Visit 请求给该访问者的那个类。这使得访问者可以确定正被访问元素的具体的类。这样访问者就可以通过该元素的特定接口直接访问它。
ConcreteVisitor:具体访问者,实现每个由 Visitor 声明的操作,每个操作实现本算法的一部分,而该算法片断乃是对应于结构中对象的类。ConcreteVisitor 为该算法提供了上下文并存储它的局部状态。这一状态常常在遍历该结构的过程中累积结果。
Element:元素,定义一个 Accept 操作,它以一个访问者为参数。
ConcreteElement:具体元素,实现 Accept 操作,该操作以一个访问者为参数。
ObjectStructure:对象结构,能枚举它的元素。可以提供一个高层的接口以允许该访问者访问它的元素。可以是一个复合(Composite 模式)或是一个集合,如一个列表或一个无序集合。
一个使用 Visitor 模式的客户必须创建一个 ConcreteVisitor 对象,然后遍历该对象结构,并用该访问者访问每一个元素。当一个元素被访问它时,它调用对应于它的类的 Visitor 操作,如果必要,该元素将自身作为这个操作的一个参数以便该访问者访问它的状态。

单分派与双分派(单重派遣、双重派遣),访问者模式允许你不改变类即可有效地增加其上的操作,为达到这一效果使用了一种称为双分派(double-dispatch)的技术。C++ 语言支持单分派。
在单分派语言中,到底由哪一种操作将来实现一个请求取决于两个方面:该请求的名和接收者的类型,即依赖于请求和接收者的类型两个方面。也就是说如果在多个不知道类型的对象之间操作,C++ 只能在其中一个类型上激发动态绑定机制,因此程序员只能手工发现一些类型并且有效地制造自己的动态绑定行为。
双分派意味着得到执行的操作决定于请求的种类和两个接收者的类型。我们知道,多态只能通过虚函数调用来实现,所以如果想要发生双分派,必须有一个虚函数调用以确定每个未知的类型。因此,如果处理的是不同层次结构的两个类型的交互作用,则每个层次结构都必须有一个虚函数调用。通常将设立这样一种结构,使得每一个成员函数的调用导致多个虚函数调用,并且因此在该过程中确定多个类型:对于每个派遣都需要一个虚函数调用。
双重派遣代码示例如下,from Thinking in C++ Volume2:
1:  //Multiple-Dispatching


2:  #include <algorithm>


3:  #include <iostream>


4:  #include <iterator>


5:  #include <vector>


6:  #include <ctime>


7:  #include <cstdlib>


8:   


9:  using namespace std;


10:   


11:  //释放序列


12:  template<class Seq>


13:  void ReleaseSeq(Seq& c)


14:  {


15:      typename Seq::iterator i;


16:      for(i = c.begin(); i != c.end(); ++i)


17:      {


18:          delete *i;


19:          *i = 0;


20:      }


21:  }


22:   


23:  //Paper 布, Scissors 剪刀, Rock 石头


24:  class Paper;


25:  class Scissors;


26:  class Rock;


27:   


28:  enum Outcome { WIN, LOSE, DRAW };


29:   


30:  ostream& operator<<(ostream& os, const Outcome out)


31:  {


32:      switch(out)


33:      {


34:      default:


35:      case WIN:


36:          return os << "win";


37:      case LOSE:


38:          return os << "lose";


39:      case DRAW:


40:          return os << "draw";  //draw 有平局,平手的意思


41:      }


42:  }


43:   


44:  //抽象基类


45:  //compete 调用 eval 对两个具体的 Item 进行比较


46:  //eval 根据具体的 Item 动态选择具体的实现,返回比较结果


47:  class Item


48:  {


49:  public:


50:      virtual Outcome compete(const Item*) = 0;


51:      virtual Outcome eval(const Paper*) const = 0;


52:      virtual Outcome eval(const Scissors*) const= 0;


53:      virtual Outcome eval(const Rock*) const = 0;


54:      virtual ostream& print(ostream& os) const = 0;


55:      virtual ~Item() {}


56:      friend ostream& operator<<(ostream& os, const Item* it)


57:      {


58:          return it->print(os);


59:      }


60:  };


61:   


62:  class Paper : public Item


63:  {


64:  public:


65:      Outcome compete(const Item* it) { return it->eval(this);}


66:      Outcome eval(const Paper*) const { return DRAW; }


67:      Outcome eval(const Scissors*) const { return WIN; }


68:      Outcome eval(const Rock*) const { return LOSE; }


69:      ostream& print(ostream& os) const


70:      {


71:          return os << "Paper   ";


72:      }


73:  };


74:   


75:  class Scissors : public Item


76:  {


77:  public:


78:      Outcome compete(const Item* it) { return it->eval(this);}


79:      Outcome eval(const Paper*) const { return LOSE; }


80:      Outcome eval(const Scissors*) const { return DRAW; }


81:      Outcome eval(const Rock*) const { return WIN; }


82:      ostream& print(ostream& os) const


83:      {


84:          return os << "Scissors";


85:      }


86:  };


87:   


88:  class Rock : public Item {


89:  public:


90:      Outcome compete(const Item* it) { return it->eval(this);}


91:      Outcome eval(const Paper*) const { return WIN; }


92:      Outcome eval(const Scissors*) const { return LOSE; }


93:      Outcome eval(const Rock*) const { return DRAW; }


94:      ostream& print(ostream& os) const


95:      {


96:          return os << "Rock    ";


97:      }


98:  };


99:   


100:  //函数对象, 或仿函数(functor)


101:  struct ItemGen


102:  {


103:      Item* operator()()


104:      {


105:          switch(rand() % 3)


106:          {


107:            default:


108:            case 0: return new Scissors;


109:            case 1: return new Paper;


110:            case 2: return new Rock;


111:          }


112:      }


113:  };


114:   


115:  //函数对象


116:  //Outcome 将 Compete 返回和不同结果进行分类


117:  struct Compete


118:  {


119:      //operator 有两个 Item, 但并不知道其确切类型


120:      //并调用 virtual Outcome compete() 进行双重派遣过程


121:   


122:      //虚拟机制决定了 a 的类型,因此它激发了在函数 compete() 内部的


123:      //a 的具体类型的产生. 在保留该类型的基础之上, 函数 compete()


124:      //调用 eval() 执行第 2 次派遣. 将其自身(this 指针)作为一个参数


125:      //传递给函数 eval(),从而产生一个对重载的 eval() 函数的调用,因此


126:      //保存了第 1 次派遣的类型信息.在完成第 2 次派遣时,两个 Item 对象


127:      //的确切类型就都知道了


128:      Outcome operator()(Item* a, Item* b)


129:      {


130:          cout << a << "/t" << b << "/t";


131:          return a->compete(b);


132:      }


133:  };


134:   


135:  int main()


136:  {


137:      srand(time(0)); // Seed the random number generator


138:      const int sz = 10;


139:      vector<Item*> v(sz*2);


140:   


141:      //vector 大小为 20, 随机生成其各个元素值


142:      generate(v.begin(), v.end(), ItemGen());


143:   


144:      //对 (v[0], v[9]) 与 (v[10],v[19]) 之间的元素依次进行 Compete() 操作


145:      //并将结果使用 ostream_iterator<Outcome>(cout, "/n") 输出


146:      transform(v.begin(), v.begin() + sz,        //first source


147:          v.begin() + sz,                            //second source


148:          ostream_iterator<Outcome>(cout, "/n"),  //result


149:          Compete());                                //unary function object


150:   


151:      ReleaseSeq(v);


152:   


153:      return EXIT_SUCCESS;


154:  }


 

Accept 是一个 double-dispatch 操作。它的含义决定于两个类型:Visitor 的类型和 Element 的类型。因为 Accept()是多态的操作,需要具体的 Element 类型的子类才可以决定到底调用哪一个 Accept() 实现。Accept(Visitor* v)有一个参数 Visitor*,要决定实际传进来的 Visitor 的实际类别才可以决定具体调用哪个 VisitConcrete() 实现。

双分派使得访问者可以对每一个类元的素请求不同的操作。这是 Visitor 模式的关键所在,得到执行的操作不仅决定于 Visitor 的类型还决定于它访问的 Element 的类型。可以不将操作静态地绑定在 Element 接口中,而将其安放在一个 Visitor 中,并使用 Accept 在运行时进行绑定。扩展 Element 接口就等于定义一个新的 Visitor 子类而不是多个新的 Element 子类。

Visitor 访问者模式代码示例:

1:  //Visitor.h


2:  #pragma once


3:   


4:  #include <list>


5:   


6:  class Visitor;


7:   


8:  class Element


9:  {


10:  public:


11:      virtual ~Element(){}


12:      virtual void Accept(Visitor& rVisitor) = 0;


13:  protected:


14:      Element(){}


15:  };


16:   


17:  class ConcreteElementA : public Element


18:  {


19:  public:


20:      virtual ~ConcreteElementA() {}


21:      virtual void Accept(Visitor& rVisitor);


22:   


23:      void PrintSelf1();


24:      void PrintSelf2();


25:  };


26:   


27:  class ConcreteElementB : public Element


28:  {


29:  public:


30:      virtual ~ConcreteElementB() {}


31:      virtual void Accept(Visitor& rVisitor);


32:   


33:      void PrintSelf1();


34:      void PrintSelf2();


35:  };


36:   


37:  //Visitor


38:  class Visitor


39:  {


40:  public:


41:      virtual ~Visitor(){}


42:      virtual void VisitConcreteElementA(ConcreteElementA *pConcreteElementA) = 0;


43:      virtual void VisitConcreteElementB(ConcreteElementB *pConcreteElementB) = 0;


44:  protected:


45:      Visitor(){}


46:  };


47:   


48:  class ConcreteVisitorA : public Visitor


49:  {


50:  public:


51:      virtual ~ConcreteVisitorA(){}


52:      virtual void VisitConcreteElementA(ConcreteElementA *pConcreteElementA);


53:      virtual void VisitConcreteElementB(ConcreteElementB *pConcreteElementB);


54:  };


55:   


56:  class ConcreteVisitorB : public Visitor


57:  {


58:  public:


59:      virtual ~ConcreteVisitorB(){}


60:      virtual void VisitConcreteElementA(ConcreteElementA *pConcreteElementA);


61:      virtual void VisitConcreteElementB(ConcreteElementB *pConcreteElementB);


62:  };


.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

1:  //Visitor.cpp


2:  #include "Visitor.h"


3:  #include <iostream>


4:   


5:  //Element


6:  void ConcreteElementA::Accept(Visitor &rVisitor)


7:  {


8:      rVisitor.VisitConcreteElementA(this);


9:  }


10:  void ConcreteElementA::PrintSelf1()


11:  {


12:      std::cout << "ConcreteElementA::PrintSelf1()" << std::endl;


13:  }


14:  void ConcreteElementA::PrintSelf2()


15:  {


16:      std::cout << "ConcreteElementA::PrintSelf2()" << std::endl;


17:  }


18:  ///


19:  void ConcreteElementB::Accept(Visitor &rVisitor)


20:  {


21:      rVisitor.VisitConcreteElementB(this);


22:  }


23:  void ConcreteElementB::PrintSelf1()


24:  {


25:      std::cout << "ConcreteElementB::PrintSelf1()" << std::endl;


26:  }


27:  void ConcreteElementB::PrintSelf2()


28:  {


29:      std::cout << "ConcreteElementB::PrintSelf2()" << std::endl;


30:  }


31:   


32:  //Visitor


33:  void ConcreteVisitorA::VisitConcreteElementA(ConcreteElementA *pConcreteElementA)


34:  {


35:      std::cout << "VisitConcreteElementA By ConcreteVisitorA" << std::endl;


36:      pConcreteElementA->PrintSelf1();


37:  }


38:   


39:  void ConcreteVisitorA::VisitConcreteElementB(ConcreteElementB *pConcreteElementB)


40:  {


41:      std::cout << "VisitConcreteElementB By ConcreteVisitorA" << std::endl;


42:      pConcreteElementB->PrintSelf2();


43:  }


44:   


45:  void ConcreteVisitorB::VisitConcreteElementA(ConcreteElementA *pConcreteElementA)


46:  {


47:      std::cout << "VisitConcreteElementA By ConcreteVisitorB" << std::endl;


48:      pConcreteElementA->PrintSelf1();


49:  }


50:   


51:  void ConcreteVisitorB::VisitConcreteElementB(ConcreteElementB *pConcreteElementB)


52:  {


53:      std::cout << "VisitConcreteElementB By ConcreteVisitorB" << std::endl;


54:      pConcreteElementB->PrintSelf2();


55:  }


56:   


.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

1:  //test.cpp


2:   


3:  #include "Visitor.h"


4:  #include <iostream>


5:   


6:  int main()


7:  {


8:      Visitor *pVisitorA = new ConcreteVisitorA();


9:      Element *pElementA1  = new ConcreteElementA();


10:      pElementA1->Accept(*pVisitorA);


11:      std::cout << std::endl;


12:   


13:      Visitor *pVisitorB = new ConcreteVisitorB();


14:      Element *pElementA2  = new ConcreteElementA();


15:      pElementA2->Accept(*pVisitorB);


16:      std::cout << std::endl;


17:   


18:      delete pElementA1;


19:      delete pVisitorA;


20:   


21:      delete pElementA2;


22:      delete pVisitorB;


23:   


24:      return EXIT_SUCCESS;


25:  }


.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: