访问者模式是设计模式中较为复杂的模式之一,而就我来看理解该模式就需要费点劲。像责任链、备忘录、中介模式这种顾名思义就能知道了解该模式是怎么做的不一样,访问者模式的设计不太容易理解,普通意义上的访问者就像记者拿着记录仪在街上采访行人一样,记者就是访问者模式中的访问者,而行人就是元素。在访问者模式中,访问者更像被访问对象在遇到访问者之后做出的反应,比如这里记者提出的问题,而访问者就是被采访的人。下面就具体的来看看访问者模式。
定义:表示作用于某个对象结构中的个元素的操作,它可以在不改变个元素的类的前提下定义作用于这些类的新操作。
定义解释:某一个对象结构中有很多个类型相同的元素(比如一个列表,或者几个相同类型的对象),现在需要对这些元素新增新的操作,一般实现是需要在抽象类中新增抽象接口,然后由各个实现实现自己各自的新操作。这破坏了类的对修改关闭的原则。访问者模式,可以在不修改类的实现的前提下,为元素新增新的操作。
类图:
模式角色:
1、抽象访问者:访问者抽象接口,通过visit(Element)方法访问Element(数据结构),完成对Element的操作行为。
2、具体访问者: 访问者的具体实现类。
3、元素:也就是被访问者,通过accept(Visitor)方法接受Visitor的访问。
4、具体元素:元素的具体实现类。
5、对象结构:拥有一组元素的组合对象。ObjectStructure本身也可以作为被访问者。
例子:
男人和女人对于不同问题的看法问题是访问者模式的合适例子,比如对于成功的看法,男人觉得成功是获得一种社会认同感,女人觉的成功就是家庭幸福。男人觉得失败就是社会不认同没有钱,女人觉得失败是婚姻不幸福,女儿不幸福。对于事业,男人认为高于家庭,女人认为低于家庭等。这里男人和女人就是元素,而成功、失败、事业就是访问者,元素对于每个访问者都需要有自己的实现,甚至于新增访问者,比如新增对于金钱的看法,男人认为那是能力的表现,女人认为那是购买力。
设计:这里有两种人,男人和女人,而且一般不会发生变化,同时有很多个问题,成功 失败 事业 金钱等甚至更多。这种元素的个数不发生变化,而问题的个数不断发生变化的情况就是访问者模式需要解决的问题。
我们将人定义为抽象元素类,男人和女人就是具体的元素,都必须实现人的visit方法(就某个问题访问了),我们定义问题的抽象类problem并提供accept抽象方法,并且有success fail career money等实现类,这里problem就是抽象访问者类,而success fail career money就是具体的访问者类,具体的访问者类必须实现accept方法,表示每种元素对于该问题的答案。对象结构就是一个由于男人和女人两个元素的数组。
实现:
//============================================================================
// Name : visitor.cpp
// Author : tester
// Version :
// Copyright : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================
#include <iostream>
#include <vector>
using namespace std;
class Person;
class Man;
class Woman;
//visitor基类
class Problem
{
public:
~Problem(){};
virtual void visit(Man* person) = 0;
virtual void visit(Woman* person) = 0;
};
class Success : public Problem
{
public:
void visit(Man* person)
{
cout << "男人成功:获得社会认同感." << endl;
}
void visit(Woman* person)
{
cout << "女人成功:家庭幸福就是成功." << endl;
}
};
class Fail : public Problem
{
public:
void visit(Man* person)
{
cout << "男人失败:社会认同度低,不能赚钱." << endl;
}
void visit(Woman* person)
{
cout << "女人失败:婚姻失败,女儿不幸福." << endl;
}
};
class Career : public Problem
{
public:
void visit(Man* person)
{
cout << "男人事业感:高于家庭,高于其他." << endl;
}
void visit(Woman* person)
{
cout << "女人事业感:低于家庭,低于保养." << endl;
}
};
class Money : public Problem
{
public:
void visit(Man* person)
{
cout << "男人有钱:那是能力的表现,提升魅力值." << endl;
}
void visit(Woman* person)
{
cout << "女人有钱:那是身份的表芯,提升购买力." << endl;
}
};
//元素基类
class Person
{
public:
virtual ~Person(){};
virtual void accept(Problem* problem) = 0;
};
//元素具体类
class Man : public Person
{
public:
void accept(Problem* problem)
{
problem->visit(this);
}
};
//元素具体类
class Woman : public Person
{
public:
void accept(Problem* problem)
{
problem->visit(this);
}
};
class ObjectStruct
{
public:
void add_person(Person* person)
{
m_person_vec.push_back(person);
}
void answer(Problem* problem)
{
for(unsigned idx=0; idx<m_person_vec.size(); ++idx)
{
m_person_vec[idx]->accept(problem);
}
}
private:
vector<Person*> m_person_vec;
};
int main() {
cout << "!!!Hello World!!!" << endl; // prints !!!Hello World!!!
ObjectStruct objstu;
Person* man = new Man;
objstu.add_person(man);
Person* woman = new Woman;
objstu.add_person(woman);
Problem* problem = new Success;
objstu.answer(problem);
problem = new Fail;
objstu.answer(problem);
problem = new Career;
objstu.answer(problem);
problem = new Money;
objstu.answer(problem);
return 0;
}
结果:
!!!Hello World!!!
男人成功:获得社会认同感.
女人成功:家庭幸福就是成功.
男人失败:社会认同度低,不能赚钱.
女人失败:婚姻失败,女儿不幸福.
男人事业感:高于家庭,高于其他.
女人事业感:低于家庭,低于保养.
男人有钱:那是能力的表现,提升魅力值.
女人有钱:那是身份的表芯,提升购买力.
总结:
访问者模式提供了一种元素个数不变,但是在不改变类的实现的情况下,动态的为对象提供操作的方法,我们知道装饰着模式也能为对象动态的添加行为,和装饰者模式不同的是,装饰着模式在原有对象上新增了新的属性,世界上改变了原有对象,而访问者模式是在元素类新增了新的处理行为,也就是让行为变宽了,并没有本身改变对象本身。另外,访问者模式是结构型模式,而装饰模式是行为模式。