用访问者模式进行多重派遣
访问者模式(Visitor)的目标是将继承层次结构上的操作与这个层次结构本身分开。这是一个相当古怪的动机,因为在向对象编程中所做的大部分工作是将数据和操作组合在一起形成对象,并利用多态性根据对象的确切类型自动选择操作的正确变化。
利用访问者模式将操作从类的继承层次结构中提取出来置入一个独立的外部层次结构。“主层次结构”包含一个函数visit(),该函数接受任何来自操作层次结构的对象。结果得到了两上类继承层次结构而不是一个。此外,可以看到,“主层次结构”变得很脆弱——如果要增加一个新类,也要强制改动第二个层次结构。因此,GoF认为主层次结构应该“很少地变化”。这个限制非常有限,从而更进一步降低了这种模式的可应用性。
因此(假定实际上需要这么做)两难的窘境就是,用户需要向基类中添加新的成员函数,但是由于某种原因用户不能接触到基类。那么该如何处理这种情况呢?
访问者模式允许创建一个独立的类层次结构Visitor而有效地对主类的接口进行扩展,这个独立的类层次结构将主类上的各种操作“虚化”。主类对象仅“接受”访问者,然后调用访问者的动态绑定的成员函数。因此,创建一个访问,并将其传递给主层次结构,便可以获得和虚函数一样的效果。举例如下:
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
#include <ctime>
#include <cstdlib>
//#include "../purge.h"
using namespace std;
class Gladiolus;
class Renuculus;
class Chrysanthemum;
class Visitor {
public:
virtual void visit(Gladiolus* f) = 0;
virtual void visit(Renuculus* f) = 0;
virtual void visit(Chrysanthemum* f) = 0;
virtual ~Visitor() {}
};
class Flower {
public:
virtual void accept(Visitor&) = 0;
virtual ~Flower() {}
};
class Gladiolus : public Flower {
public:
virtual void accept(Visitor& v) {
v.visit(this);
}
};
class Renuculus : public Flower {
public:
virtual void accept(Visitor& v) {
v.visit(this);
}
};
class Chrysanthemum : public Flower {
public:
virtual void accept(Visitor& v) {
v.visit(this);
}
};
// Add the ability to produce a string:
class StringVal : public Visitor {
string s;
public:
operator const string&() { return s; }
virtual void visit(Gladiolus*) {
s = "Gladiolus";
}
virtual void visit(Renuculus*) {
s = "Renuculus";
}
virtual void visit(Chrysanthemum*) {
s = "Chrysanthemum";
}
};
// Add the ability to do "Bee" activities:
class Bee : public Visitor {
public:
virtual void visit(Gladiolus*) {
cout << "Bee and Gladiolus" << endl;
}
virtual void visit(Renuculus*) {
cout << "Bee and Renuculus" << endl;
}
virtual void visit(Chrysanthemum*) {
cout << "Bee and Chrysanthemum" << endl;
}
};
struct FlowerGen {
Flower* operator()() {
switch(rand() % 3) {
default:
case 0: return new Gladiolus;
case 1: return new Renuculus;
case 2: return new Chrysanthemum;
}
}
};
int main() {
srand(time(0)); // Seed the random number generator
vector<Flower*> v(10);
generate(v.begin(), v.end(), FlowerGen());
vector<Flower*>::iterator it;
// It's almost as if I added a virtual function
// to produce a Flower string representation:
StringVal sval;
for(it = v.begin(); it != v.end(); it++) {
(*it)->accept(sval);
cout << string(sval) << endl;
}
// Perform "Bee" operation on all Flowers:
Bee bee;
for(it = v.begin(); it != v.end(); it++)
(*it)->accept(bee);
//purge(v);
}
Flower是主层次结构,Flower的各个子类通过函数accept()得到一个Visitor。Flower主层次结构除了accept()外没有别的操作,因此Flower层次结构的所有功能都将包含在Visitor层次结构中。注意,Visitor类必须要了解Flower的所有基本类型,如果添加一个Flower的新类型,整个Visitor层次结构必须重新工作。
每个Flower()中的accept()函数开始一个双重派遣。第1次派遣决定了Flower的准确类型,第2北派遣决定了Visitor的准确类型。一旦知道了他们的准确类型,就可以对这两者执行恰当的操作。
选自《C++编程思想》。