设计模式笔记-Visitor访问者模式

这个模式我也是有很多疑问,不论是从该模式的原理,还是网上很多人的实现上,都有很多我个人觉得不合理的地方,后面会讲!首先还是介绍一下该模式吧!

软件系统开发过程中,需求增加是常有的事,如果不想修改原有的设计,就得在原来的类中添加新的方法。是的,这时候“设计模式”会装逼的跟你说这违反了开闭原则!解决方法是Visitor模式:把要增加的操作封装到一个类visitor中,被更改的类element提供一个接口,在这个接口中调用visitor类中的一些操作,这样element类就不用改变,但同时增加了visitor中的操作。先上图说话吧,文字太费劲!


首先咱说这个模式的UML图,visitor和element是相互组合对方指针的,它两个是直接相互依赖的,自己模式的事,和用户代码没关系啊,如果要client代码来帮忙,那你这设计有问题吧。所以我觉得它没体现visitor模式的精髓,个人愚见,欢迎批评指正。

阻碍我理解visitor模式的,或者说我觉得它的精髓是:如果在架构设计的时候你觉得用户肯定要添加或更改需求,那么Element类事先要留一个Accept接口。需求变更你可能要修改别的OperationA/B方法,但至少在Element类修改关闭前,要把Accept接口加进去!如果你事先没这个接口,那要增加操作的时候visitor类倒是可以通过传递来的ConcreteElement指针访问具体的元件对象,但具体的元件对象没法获得visitor指针啊,除非你在元件类中加入一个Accept,像上图那样,这时候加不就晚了嘛,咱的目的就是不修改原来设计好的类,符合“开闭原则”嘛。

你看,这就是为什么很多人说不要因为模式而模式,如果你的项目不大,直接修改Element类得了,但你也得不到锻炼,成为架构大牛的梦想是要有的,最好的状态应该是你知道模式怎么用,但没必要的时候不用。

ObjectStructure类相当于一个对象集合,里面的Element元素可以是不同的类型。根据项目的需求情况,这一层可要可不要。另外和Composite模式一样,对集合的对象是一个遍历全部执行,还是通过传递参数不同对象区别执行,看你实际情况(很多模式都有这个现象)。

还是不止一次的推荐一下这个博客,一些原理性的东西它讲的很清楚,实现也不错。我修改为C++代码:

#include<map>
#include<string>
#include<iostream>
using std::cout;
using std::endl;
#include "element.h"

class IElement;
class Visitor {
public:
	virtual void visitConcreteElementA(IElement*)=0;
	virtual void visitConcreteElementB(IElement*)=0;
};

class  ConcreteVisitorA:public Visitor{
public:
	void visitConcreteElementA(IElement*);
	void visitConcreteElementB(IElement*);
};

class  ConcreteVisitorB :public Visitor {
public:
	void visitConcreteElementA(IElement*);
	void visitConcreteElementB(IElement*);
};
#include<string>
#include<iostream>
#include "visitor.h"
using namespace std;
void ConcreteVisitorA::visitConcreteElementA(IElement* elem)
{
	cout << elem->getName() << " visited by ConcreteVisitor A!" << endl;
}
void ConcreteVisitorA::visitConcreteElementB(IElement* elem)
{
	cout << elem->getName() << " visited by ConcreteVisitor A!" << endl;
}

void ConcreteVisitorB::visitConcreteElementA(IElement* elem)
{
	cout << elem->getName() << " visited by ConcreteVisitor B!" << endl;
}
void ConcreteVisitorB::visitConcreteElementB(IElement* elem)
{
	cout << elem->getName() << " visited by ConcreteVisitor B!" << endl;
}
#include"visitor.h"
#include<string>
#include<vector>
using std::string;
using std::vector;

class Visitor;
class IElement {
public:
	virtual void accept(Visitor *) = 0;
	virtual string getName() = 0;
protected:
	string name;
};

class ConcreteElementA :public IElement {
public:
	ConcreteElementA(string s) { name = s; }
	string getName();
	void accept(Visitor *);
};

class ConcreteElementB :public IElement {
public:
	ConcreteElementB(string s) { name = s; }
	string getName();
	void accept(Visitor *);
};

class ObjectStructure {
public:
	void add(IElement*);
	void remove(IElement*);
	void accept(Visitor*);
private:
	vector<IElement*> vectElem;
};
#include"visitor.h"
#include"element.h"
string ConcreteElementA::getName()
{
	return name;
}
void ConcreteElementA::accept(Visitor * v)
{
	v->visitConcreteElementA(this);
}

string ConcreteElementB::getName()
{
	return name;
}
void ConcreteElementB::accept(Visitor * v)
{
	v->visitConcreteElementB(this);
}

void ObjectStructure::add(IElement* e)
{
	vectElem.push_back(e);
}
void ObjectStructure::remove(IElement* e)
{
	for (vector<IElement*>::iterator it = vectElem.begin(); it != vectElem.end(); )
	{
		if (*it == e)
			it = vectElem.erase(it);
		else
			++it;
	}
}
void ObjectStructure::accept(Visitor * v)
{
	for (auto x : vectElem)
	{
		x->accept(v);
	}
}

对于像C++这种强类型语言,这种头文件(对象指针)你包含我,我包含你的情况,真是头大!用户代码如下:

int main()
{
	ConcreteElementA *elementA = new ConcreteElementA("ElementA");
	ConcreteElementB *elementB = new ConcreteElementB("ElementB");
	ConcreteElementB *elementB2 = new ConcreteElementB("ElementA2");
	ConcreteVisitorA *visitor1 = new ConcreteVisitorA();
	ConcreteVisitorB *visitor2 = new ConcreteVisitorB();

	ObjectStructure *os = new ObjectStructure();
	os->add(elementA);
	os->add(elementB);
	os->add(elementB2);
	os->remove(elementA);

	os->accept(visitor1);
	os->accept(visitor2);
	return 0;
}
在访问者模式中,每增加一个新的Element类都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作,违背了“开闭原则”的要求。系统设计时,经常把容易变化的进行封装,如果能把数据结构(element元件)和算法(visitor操作) 分开,并且数据结构不容易变,算法经常变的话,可以考虑用访问者模式,如果二者相反的话,就不行。
破坏封装。访问者模式要求访问者对象访问并调用每一个元素对象的操作,这意味着元素对象有时候必须暴露一些自己的内部操作和内部状态,如果不想提供public接口,需要把visitor设为友元类,这貌似更不合理!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值