现在的位置: 首页 > 综合 > 正文

Visitor设计模式

2013年06月28日 ⁄ 综合 ⁄ 共 3743字 ⁄ 字号 评论关闭

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

 

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

 

Visitor模式表示一个作用于某对象结构中的各元素的操作.它使我们可以在不改变各元素的类

的前提下定义作用于这些元素的新操作.

 

Visitor模式把对结点的访问封装成一个抽象基类,通过派生出不同的类生成新的访问方式.实现时,在visitor抽象基类中声明了对所有不同结点进行访问的接口函数。Visitor模式的有一个缺陷--新加入一个结点时都要添加Visitor中的对其进行访问接口函数,这样使得所有的Visitor及其派生类都要重新编译了,也就是说Visitor模式一个缺点就是添加新的结点十分困难.另外,还需要指出的是Visitor模式采用了所谓的"双重分派"的技术。

 

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

 

#ifndef VISITOR_H

#define VISITOR_H

class Visitor;

class Element

{

public:

virtual ~Element(){}

virtual void Accept(Visitor &rVisitor) = 0;

protected:

Element(){}

};

class ConcreateElementA : public Element

{

public:

virtual ~ConcreateElementA() {}

virtual void Accept(Visitor &rVisitor);

};

class ConcreateElementB : public Element

{

public:

virtual ~ConcreateElementB() {}

virtual void Accept(Visitor &rVisitor);

};

class Visitor

{

public:

virtual ~Visitor(){}

virtual void VisitConcreateElementA(ConcreateElementA *pConcreateElement

A) = 0;

virtual void VisitConcreateElementB(ConcreateElementB *pConcreateElement

B) = 0;

protected:

Visitor(){}

};

class ConcreateVisitorA : public Visitor

{

public:

virtual ~ConcreateVisitorA(){}

virtual void VisitConcreateElementA(ConcreateElementA *pConcreateElement

A);

virtual void VisitConcreateElementB(ConcreateElementB *pConcreateElement

B);

};

class ConcreateVisitorB : public Visitor

{

public:

virtual ~ConcreateVisitorB(){}

virtual void VisitConcreateElementA(ConcreateElementA *pConcreateElement

A);

virtual void VisitConcreateElementB(ConcreateElementB *pConcreateElement

B);

};

#endif

 

 

#include "Visitor.h"

#include <iostream>

void ConcreateElementA::Accept(Visitor &rVisitor)

{

rVisitor.VisitConcreateElementA(this);

}

void ConcreateElementB::Accept(Visitor &rVisitor)

{

rVisitor.VisitConcreateElementB(this);

}

void ConcreateVisitorA::VisitConcreateElementA(ConcreateElementA *pConcreat

eElementA)

{

std::cout << "VisitConcreateElementA By ConcreateVisitorA/n";

}

void ConcreateVisitorA::VisitConcreateElementB(ConcreateElementB *pConcreat

eElementA)

{

std::cout << "VisitConcreateElementB By ConcreateVisitorA/n";

}

void ConcreateVisitorB::VisitConcreateElementA(ConcreateElementA *pConcreat

eElementA)

{

std::cout << "VisitConcreateElementA By ConcreateVisitorB/n";

}

void ConcreateVisitorB::VisitConcreateElementB(ConcreateElementB *pConcreat

eElementA)

{

td::cout << "VisitConcreateElementB By ConcreateVisitorB/n";

}

 

 

#include "Visitor.h"

int main()

{

Visitor *pVisitorA = new ConcreateVisitorA();

Element *pElement = new ConcreateElementA();

pElement->Accept(*pVisitorA);

delete pElement;

delete pVisitorA;

return 0;

}

 

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

 

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

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

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

 

 

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

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

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

抱歉!评论已关闭.