0 引言

0.1 目的

       本文档给出设计模式之——Visitor模式的简化诠释,并给出其C++实现

0.2 说明

Project

Design Pattern ExplanationBy K_Eckel

Authorization

Free Distributed but Ownership Reserved

Date

2005-04-05Cherry blossom is Beautiful

Test Bed

MS Visual C++ 6.0

0.3 参考

       在本文档的写作中,参考了以下的资源,在此列出表示感谢:

u       书籍

[GoF 2000]GoF,Design Patterns-Elements of Reusable Object-Oriented Software Addison-Wesley 2000/9.

        [Martine 2003]Robert C.Martine, Agile Software Development Principles, Patterns, and Practices, Pearson Education, 2003.

0.4 联系作者

Author

K_Eckel

State

Candidate for Master’s Degree School of Computer Wuhan University

E_mail

frwei@whu.edu.cn  

2 Visitor模式

2.1 问题

       在面向对象系统的开发和设计过程,经常会遇到一种情况就是需求变更(Requirement Changing),经常我们做好的一个设计、实现了一个系统原型,咱们的客户又会有了新的需求。我们又因此不得不去修改已有的设计,最常见就是解决方案就是给已经设计、实现好的类添加新的方法去实现客户新的需求,这样就陷入了设计变更的梦魇:不停地打补丁,其带来的后果就是设计根本就不可能封闭、编译永远都是整个系统代码。

       Visitor模式则提供了一种解决方案:将更新(变更)封装到一个类中(访问操作),并由待更改类提供一个接收接口,则可达到效果。

2.2 模式选择

       我们通过Visitor模式解决上面的问题,其典型的结构图为:


2-1Visitor Pattern结构图

       Visitor模式在不破坏类的前提下,为类提供增加新的新操作。Visitor模式的关键双分派(Double-Dispatch的技术【注释1C++语言支持的是单分派。

       Visitor模式中Accept()操作是一个双分派的操作。具体调用哪一个具体的Accept()操作,有两个决定因素:1Element的类型。因为Accept()是多态的操作,需要具体的Element类型的子类才可以决定到底调用哪一个Accept()实现;2Visitor的类型。Accept()操作有一个参数(Visitor* vis),要决定了实际传进来的Visitor的实际类别才可以决定具体是调用哪个VisitConcrete()实现。

 

【注释1:双分派意味着执行的操作将取决于请求的种类和接收者的类型。更多资料请参考资料。

2.3 实现

2.3.1 完整代码示例(code

       Visitor模式的实现很简单,这里为了方便初学者的学习和参考,将给出完整的实现代码(所有代码采用C++实现,并在VC 6.0下测试运行)。
 

代码片断1Visitor.h

//Visitor.h

#ifndef _VISITOR_H_

#define _VISITOR_H_


class ConcreteElementA;


class ConcreteElementB;


class Element;


class Visitor

{

public:

      virtual ~Visitor();

virtual void VisitConcreteElementA(Element* elm) = 0;

      virtual void VisitConcreteElementB(Element* elm) = 0;

protected:

      Visitor();

private:

};


class ConcreteVisitorA:public Visitor

{

public:

      ConcreteVisitorA();

      virtual ~ConcreteVisitorA();

      virtual void VisitConcreteElementA(Element* elm);

      virtual void VisitConcreteElementB(Element* elm);

protected:

private:

};


class ConcreteVisitorB:public Visitor

{

public:

      ConcreteVisitorB();

      virtual ~ConcreteVisitorB();

      virtual void VisitConcreteElementA(Element* elm);

      virtual void VisitConcreteElementB(Element* elm);

protected:

private:

};
#endif //~_VISITOR_H_

代码片断2Visitor.cpp

//Visitor.cpp

#include "Visitor.h"

#include "Element.h"


#include <iostream>

using namespace std;


Visitor::Visitor()

{

}


Visitor::~Visitor()

{

}


ConcreteVisitorA::ConcreteVisitorA()

{

}


ConcreteVisitorA::~ConcreteVisitorA()

{

}


void ConcreteVisitorA::VisitConcreteElementA(Element* elm)

{

      cout<<"i will visit ConcreteElementA..."<<endl;

}


void ConcreteVisitorA::VisitConcreteElementB(Element* elm)

{

      cout<<"i will visit ConcreteElementB..."<<endl;

}


ConcreteVisitorB::ConcreteVisitorB()

{

}


ConcreteVisitorB::~ConcreteVisitorB()

{

}


void ConcreteVisitorB::VisitConcreteElementA(Element* elm)

{

      cout<<"i will visit ConcreteElementA..."<<endl;

}


void ConcreteVisitorB::VisitConcreteElementB(Element* elm)

{

      cout<<"i will visit ConcreteElementB..."<<endl;

}

代码片断3Template.cpp

//Element.h

#ifndef _ELEMENT_H_

#define _ELEMENT_H_


class Visitor;


class Element

{

public:

      virtual ~Element();

      virtual void Accept(Visitor* vis) = 0;

protected:

      Element();

private:

};


class ConcreteElementA:public Element

{

public:

      ConcreteElementA();

      ~ConcreteElementA();

      void Accept(Visitor* vis);

protected:

private:

};


class ConcreteElementB:public Element

{

public:

      ConcreteElementB();

      ~ConcreteElementB();

      void Accept(Visitor* vis);

protected:

private:

};
#endif //~_ELEMENT_H_

代码片断4Element.cpp

//Element.cpp

#include "Element.h"

#include "Visitor.h"


#include <iostream>

using namespace std;


Element::Element()

{

}


Element::~Element()

{

}


void Element::Accept(Visitor* vis)

{

}


ConcreteElementA::ConcreteElementA()

{

}


ConcreteElementA::~ConcreteElementA()

{

}


void ConcreteElementA::Accept(Visitor* vis)

{

      vis->VisitConcreteElementA(this);

      cout<<"visiting ConcreteElementA..."<<endl;

}


ConcreteElementB::ConcreteElementB()

{

}


ConcreteElementB::~ConcreteElementB()

{

}


void ConcreteElementB::Accept(Visitor* vis)

{

      cout<<"visiting ConcreteElementB..."<<endl;

      vis->VisitConcreteElementB(this);

}

代码片断5main.cpp

#include "Element.h"

#include "Visitor.h"


#include <iostream>

using namespace std;


int main(int argc,char* argv[])

{

      Visitor* vis = new ConcreteVisitorA();

      Element* elm = new ConcreteElementA();

      elm->Accept(vis);

     
return 0;

}

2.3.2 代码说明

       Visitor模式的实现过程中有以下的地方要注意:

       1Visitor类中的Visit()操作的实现。

u       这里我们可以向Element仅仅提供一个接口Visit(),而在Accept()实现中具体调用哪一个Visit()操作则通过函数重载(overload)的方式实现:我们提供Visit()的两个重载版本aVisitConcreteElementA* elmA),bVisitConcreteElementB* elmB)。

u       C++中我们还可以通过RTTI(运行时类型识别:Runtime type identification)来实现,即我们只提供一个Visit()函数体,传入的参数为Element*型别参数 ,然后用RTTI决定具体是哪一类的ConcreteElement参数,再决定具体要对哪个具体类施加什么样的具体操作。【注释2RTTI给接口带来了简单一致性,但是付出的代价是时间(RTTI的实现)和代码的Hard编码(要进行强制转换)。

2.4 讨论

       有时候我们需要为Element提供更多的修改,这样我们就可以通过为Element提供一系列的

       Visitor模式可以使得Element在不修改自己的同时增加新的操作,但是这也带来了至少以下的两个显著问题:

1)  破坏了封装性Visitor模式要求Visitor可以从外部修改Element对象的状态,这一般通过两个方式来实现:aElement提供足够的public接口,使得Visitor可以通过调用这些接口达到修改Element状态的目的;bElement暴露更多的细节给Visitor,或者让Element提供public的实现给Visitor(当然也给了系统中其他的对象),或者将Visitor声明为Elementfriend,仅将细节暴露给Visitor。但是无论那种情况,特别是后者都将是破坏了封装性原则(实际上就是C++friend机制得到了很多的面向对象专家的诟病)。

2)  ConcreteElement扩展很困难:每增加一个Element的子类,就要修改Visitor

接口,使得可以提供给这个新增加的子类的访问机制。从上面我们可以看到,或者增加一个用于处理新增类的Visit()接口,或者重载一个处理新增类的Visit()操作,或者要修改RTTI方式实现的Visit()实现。无论那种方式都给扩展新的Element子类带来了困难。

 

【注释2:我们可以通过RTTI来实现Visit()接口的单一,示例代码省略。

Posted on 2005-07-08 20:44  k_eckel's mindview  阅读(815)  评论(0)    收藏  举报