访问者模式(c++实现)
访问者模式
模式定义
访问者模式(Visitor),表示一个作用于某对象结构中的各个元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
模式动机
- 访问者模式适用于数据结构相对稳定的系统
- 它把数据结构和作用于数据结构之上的操作之间的耦合解脱开,使得操作集合可以相对自由的演化。
- 访问者模式的目的是要把处理从数据结构分离出来。如果这样的系统有比较稳定的数据结构,又有已与变化的算法的话,使用访问者模式就是比较合适的,因为访问者模式使得算法操作的增加变得更容易。反之亦然。
UML类图
分析:该模式相对来说比较复杂,要详细拆解一下定义和类图才能拨云见日。
首先我们再看一下定义:访问者模式表示一个作用于某对象结构(ObjectStruct)中的各个元素(Person)的操作(Action)。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
此具体实现中Action及其子类代表可变的行为,Person及其子类代表稳定不变的数据结构。
一句话的定义其实已经把该模式对应的UML类图给我们勾勒出来了。定义的第一句话用那么长的修饰语最后就定义了两个黑体字操作,所以说访问者模式就是操作作为访问者的模式。接下来我们对应代码好好分析一下我们理出来关键信息,我们找出了如下关键字并逐一分析:
- 对象结构
- 元素
- 操作
对象结构:
用来保存各个元素,可以把它理解为一个数据结构的管理类。
元素:
元素就是我们定义的一个个的Action实例,它用来访问对象结构里面的元素。这里面有一个双分派的技术,就是数据结构Person对象的accept方法接收到一个方法Action对象,然后Action的对应的方法(PerformanceMan
,PerformanceWoman
)再把this
指针传送进去,这样就完成了Action在ObjectStruct中的对对象容器的遍历访问,其实Person对象也相当于把Action注册到了自己的类内。
操作:
Action对象,是可变的。需要增加新的操作的时候只需要创建一个新的继承Action的类即可。然后通过ObjectStruct::Display(Action);
执行。
- ObjectStruct::Attach(Person);
- ObjectStruct::Display(Action);
- ObjectStruct::Display(Action) { for(auto Person : LIst
) Person.Accept(Action)}; - Person::Accept(Action)
- Acction::getConcretePerson(Person)
这5个步骤很清晰的写出了整个模式的关键步骤。
操作基类
class Action
{
public:
Action();
~Action();
virtual void PerformanceMan(Man* man){};
virtual void PerformanceWoman(Woman* woman){};
};
再来贴一个具体的实现类:
#include "success.h"
#include <iostream>
Success::Success()
:m_Type("成功")
{
}
void Success::PerformanceMan(Man* man)
{
std::cout << man->Type() << this->m_Type << "时候的表现" << std::endl;
}
void Success::PerformanceWoman(Woman* woman)
{
std::cout << woman->Type() << this->m_Type << "时候的表现" << std::endl;
}
我们可以看到两个虚函数PerformanceMan
和PerformanceWoman
就是用来访问具体的Person(数据结构)
对象的,所以说该模式适合在数据结构固定的场景使用,不然每增加一个Person子类我们都要增加Action
的虚方法,用来访问新增加的Person子类,显然不符合开放封闭原则,而且Action的每一个子类都要增加相应的实现。
源码实现
- ObjectStruct.h
#include "person.h"
#include "action.h"
#include <vector>
#include <list>
class ObjectStruct
{
public:
ObjectStruct();
void Display(Action* action);
void Attach(Person* person);
void Detach(Person* person);
std::list<Person* > m_Person;
};
- ObjectStruct.cpp
#include "objectstruct.h"
ObjectStruct::ObjectStruct()
{
}
void ObjectStruct::Display(Action* action)
{
for(auto person : m_Person)
{
person->Accept(action);
}
}
void ObjectStruct::Attach(Person *person)
{
m_Person.push_back(person);
}
void ObjectStruct::Detach(Person *person)
{
m_Person.remove(person);
}
-
Action.h
#include "man.h" #include "woman.h" class Action { public: Action(); ~Action(); virtual void PerformanceMan(Man* man){}; virtual void PerformanceWoman(Woman* woman){}; };
-
person.h
class Action;
class Person
{
public:
Person();
virtual void Accept(Action* action);
};
- success.h
#include "action.h"
class Success : public Action
{
public:
Success();
void PerformanceMan(Man* woman) override;
void PerformanceWoman(Woman* woman) override;
private:
std::string m_Type;
};
- success.cpp
#include "success.h"
#include <iostream>
Success::Success()
:m_Type("成功")
{
}
void Success::PerformanceMan(Man* man)
{
std::cout << man->Type() << this->m_Type << "时候的表现" << std::endl;
}
void Success::PerformanceWoman(Woman* woman)
{
std::cout << woman->Type() << this->m_Type << "时候的表现" << std::endl;
}
- man.h
#include "person.h"
#include <string>
class Man : public Person
{
public:
Man();
void Accept(Action* action) override;
std::string Type();
private:
std::string m_Type;
};
- man.cpp
#include "man.h"
#include "action.h"
Man::Man()
: m_Type("男人")
{
}
void Man::Accept(Action *action)
{
action->PerformanceMan(this);
}
std::string Man::Type()
{
return m_Type;
}
- main.cpp
#include <iostream>
#include "objectstruct.h"
#include "man.h"
#include "woman.h"
#include "success.h"
#include "failed.h"
#include "married.h"
#include "breakup.h"
using namespace std;
int main()
{
ObjectStruct obj;
Man* man = new Man();
obj.Attach(man);
Woman*woman = new Woman();
obj.Attach(woman);
Success* success = new Success();
obj.Display(success);
Failed* failed = new Failed();
obj.Display(failed);
Married* married = new Married();
obj.Display(married);
BreakUp* breakUp = new BreakUp();
obj.Display(breakUp);
return 0;
}
-
其他相似的子类的详细代码就不列出了
-
运行结果
男人成功时候的表现
女人成功时候的表现
男人失败时候的表现
女人失败时候的表现
男人婚后时候的表现
女人婚后时候的表现
男人分手时候的表现
女人分手时候的表现
优点
访问者模式的优点
- 访问者模式的有点就是增加新的操作很容易,因为增加新的操作就意味着增加一个新的访问者。访问者模式将有关的行为集中到一个访问者对象中。
缺点
模式的缺点
- 使增加新的数据结构变得困难。