设计模式20--Visitor模式(访问者模式)---行为型模式

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

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

 

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

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

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

//Element.h
#pragma once
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:
};

//Element.cpp
#include "stdafx.h"
#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);
}

//Visitor.h
#pragma once
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:
};

//Visitor.cpp
#include "stdafx.h"
#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;
}

int main(int argc, _TCHAR* argv[])
{
	Visitor* vis = new ConcreteVisitorA();
	Element* elm = new ConcreteElementA();
	elm->Accept(vis);
	return 0;
}

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

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

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

aVisit ConcreteElementA* elmA),

bVisit ConcreteElementB* elmB)。

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

 

      有时候我们需要为 Element 提供更多的修改,这样我们就可以通过为 Element 提供一系 列的 Visitor 模式可以使得 Element 在不修改自己的同时增加新的操作,但是这也带来了至少 以下的两个显著问题:

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

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值