场景
软件开发过程中,我们有一组数据,同时有对应的一组操作。我们希望可以根据需求,用任意的操作对任意的数据进行操作。使用访问者模式可以快速方便的实现这一需求。
定义
访问者模式:封装一些施加于某种数据结构上的操作,一旦这些操作需要改变的话,接受这个操作的数据结构可以保持不变,反之亦然。
结构
原型模式对被克隆的目标类进行封装,提供克隆接口。在c++中,我们通过拷贝构造来实现原型模式。模式结构:
- Vistor(访问者):为对象结构中每一个 ConcreteElement 声明一个 visit() 操作,从这个操作的名称或参数类型可以清楚知道需要访问的具体元素的类型。
- ConcreteVisitor(具体访问者):实现每个由 Visitor 声明的操作。
- Element(元素):定义一个 accept() 操作,它通常以一个 Vistor 作为参数。
- ConcreteElement(具体元素):实现 accept() 操作,通过调用 Visitor 的 visit() 方法来实现对元素的访问。
- ObjectStructure(对象结构):能够枚举它的元素,同时提供一个高层的接口,以允许访问者访问它的元素。
实现
假设,小明、小刚一起切水果,水果有西瓜、哈密瓜。这是一个多对多的关系。
Vistor:
class Watermelon;
class Cantaloupe;
class People{
public:
virtual ~People(){}
virtual void cut(Watermelon*) = 0;
virtual void cut(Cantaloupe*) = 0;
};
ConcreteVisitor:
class xiaoming : public People{
public:
virtual void cut(Watermelon*) override {
std::cout << "xiaoming cut watermelon!" << std::endl;
}
virtual void cut(Cantaloupe*) override {
std::cout << "xiaoming cut cantaloupe!" << std::endl;
}
};
class xiaogang : public People{
public:
virtual void cut(Watermelon*) override {
std::cout << "xiaogang cut watermelon!" << std::endl;
}
virtual void cut(Cantaloupe*) override {
std::cout << "xiaogang cut cantaloupe!" << std::endl;
}
};
Element:
class Fruit{
public:
virtual ~Fruit(){}
virtual void Accept(People*) = 0;
};
ConcreteElement:
class Watermelon : public Fruit{
public:
virtual void Accept(People* people){
people->cut(this);
}
};
class Cantaloupe : public Fruit{
public:
virtual void Accept(People* people){
people->cut(this);
}
};
ObjectStructure:
class Kitchen{
public:
void Attach(Fruit* pFruit){m_fruit.push_back(pFruit);}
void Detach(Fruit* pFruit){m_fruit.remove(pFruit);}
void Accept(People * people){
for(auto it = m_fruit.begin(); it != m_fruit.end(); ++it){
(*it)->Accept(people);
}
}
private:
std::list<Fruit*> m_fruit;
};
Client:
int main(){
Kitchen* pKitchen = new Kitchen();
People* xiaoming = new xiaoming();
People* xiaogang = new xiaogang();
Fruit* Watermelon = new Watermelon();
Fruit* Cantaloupe = new Cantaloupe();
pKitchen->Attach(Watermelon);
pKitchen->Attach(Cantaloupe);
pKitchen->Accept(xiaoming);
pKitchen->Accept(xiaogang);
return 0;
}