23种设计模式(C++)之 访问者(Visitor)模式
意图
表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
适用性
- 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
- 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作“污染“这些对象的类。Visitor使得你可以将相关的操作集中起来定义在一个类中。当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。
- 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。
场景
比如一个对象结构是书店的书架,书架上存了多种多样的书本,,每一本书不仅有书本名,也有零售价格。对于一个只看价格的访问者,只需要看到价格属性。对于一个只看书名的访问者,只需要看到书名属性。
角色
- Visitor
- 为该对象结构中ConcreteElement的每一个类声明一个Visit操作。该操作的名字和特征标识了发送Visit请求给该访问者的那个类。这使得访问者可以确定正在被访问元素的具体的类。这样访问者就可以通过该元素的特定接口直接访问它。
- ConcreteVisitor
- 实现每个由Visitor声明的操作。每个操作实现本算法的一部分,而该算法片段乃是对应于结构中对象的类。ConcreteVisitor为该算法提供了上下文并存储它的局部状态。这一状态常常在遍历该接口的过程中累积结果。
- Element
- 定义一个Accept操作,它以一个访问者对象为参数。
- ConcreteElement
- 实现Accept操作,该操作以一个访问者对象为参数。
- ObjectStructure
- 能枚举它的元素。
- 可以提供一个高层的接口以允许该访问者访问它的元素。
- 可以是一个复合或者一个集合。
实例
以访问书架上的书为例:
- 创建书本接口
class Visitor;
//Element interface
class Book
{
protected:
string name;
float price;
public:
virtual string getName() = 0;
virtual float getPrice() = 0;
virtual void accept(Visitor* visitor) = 0;
};
- 创建访问者接口
class WenXueBook;
class WuXiaBook;
//Visitor interface
class Visitor
{
public:
virtual void visit(WenXueBook* wenxuebook) = 0;
virtual void visit(WuXiaBook* wuxiabook) = 0;
};
- 创建具体书本类
//Concrete Element 1
class WenXueBook :public Book
{
public:
WenXueBook(string name, float price)
{
this->name = name;
this->price = price;
}
string getName()
{
return this->name;
}
float getPrice()
{
return this->price;
}
void accept(Visitor* visitor)
{
visitor->visit(this);
}
};
//Concrete Element 2
class WuXiaBook :public Book
{
public:
WuXiaBook(string name, float price)
{
this->name = name;
this->price = price;
}
string getName()
{
return this->name;
}
float getPrice()
{
return this->price;
}
void accept(Visitor* visitor)
{
visitor->visit(this);
}
};
- 创建ObjectStructure
class BookStructure
{
private:
vector<Book* > book_vec;
public:
void appendBook(Book* book)
{
book_vec.push_back(book);
}
void accept(Visitor* visitor)
{
vector<Book* >::iterator it;
for (it=this->book_vec.begin(); it!=this->book_vec.end(); it++)
{
(*it)->accept(visitor);
}
}
};
- 创建访问者类
class BookNameVisitor :public Visitor
{
public:
void visit(WenXueBook* wenxuebook)
{
cout << "This is a WenXue type of book, its name is " + wenxuebook->getName() << endl;
}
void visit(WuXiaBook* wuxiabook)
{
cout<< "This is a WuXia type of book, its name is " + wuxiabook->getName() << endl;
}
};
class BookPriceVisitor :public Visitor
{
public:
void visit(WenXueBook* wenxuebook)
{
cout << "This is a WenXue type of book, its price is "<<wenxuebook->getPrice() << endl;
}
void visit(WuXiaBook* wuxiabook)
{
cout << "This is a WuXia type of book, its price is "<<wuxiabook->getPrice() << endl;
}
};
- Client
int main()
{
BookStructure* booklist = new BookStructure();
booklist->appendBook(new WenXueBook("《假如给我三天光明》", 10));
booklist->appendBook(new WuXiaBook("《神雕侠侣》", 15.5));
booklist->appendBook(new WenXueBook("《茶花女》", 12));
booklist->appendBook(new WuXiaBook("《倚天屠龙记》", 16.5));
cout << "*****************************Book name visitor************************************" << endl;
booklist->accept(new BookNameVisitor());
cout << "*****************************Book price visitor************************************" << endl;
booklist->accept(new BookPriceVisitor());
}
- Result
*****************************Book name visitor************************************
This is a WenXue type of book, its name is 《假如给我三天光明》
This is a WuXia type of book, its name is 《神雕侠侣》
This is a WenXue type of book, its name is 《茶花女》
This is a WuXia type of book, its name is 《倚天屠龙记》
*****************************Book price visitor************************************
This is a WenXue type of book, its price is 10
This is a WuXia type of book, its price is 15.5
This is a WenXue type of book, its price is 12
This is a WuXia type of book, its price is 16.5