欧耶!这周Jungle的作业终于做完了!作业是什么呢?就是完成一个习题册。Jungle做完之后,得让家长检查习题册并签字;第二天交到学校,组长得初步检查作业是否做完、家长是否签字,然后老师会评阅作业是否正确,并给出评分。
就是这么一个习题册,这是经了多少人的手啊!
Jungle——完成习题册上的题;
Jungle家长——检查儿子的作业,并在习题册上签字;
组长——初步检查Jungle的习题册是否完成;
老师——评阅习题册,给出评分。
同样一个对象(习题册),不同的人都去访问它,并且访问的方式不同,Jungle是为了完成作业,Jungle爸爸是为了签字,组长是为了检查Jungle是否完成,而老师是为了评分。 每一个人都扮演了访问者的角色。
什么?访问者?
类似于上述的习题册,软件设计中也需要这样的类似于习题册的对象结构,不同的对象对应不同的处理。设计模式中,访问者模式就是为了以不同的方式来操作复杂的对象结构。
访问者模式是一种较为复杂的行为型设计模式,具有访问者和被访问元素两个主要的角色。被访问的元素常常有不同的类型,不同的访问者可以对它们提供不同的访问方式。被访问元素通常不是单独存在,而是以集合的形式存在于一个对象结构中,访问者可以遍历该对象结构,以逐个访问其中的每一个元素。
访问者模式:
表示一个作用于某对象结构中的各个元素的操作。访问者模式让用户可以在不改变各元素的前提下定义作用于这些元素的新操作。
访问者模式的结构相对较复杂,角色有如下几个:
访问者模式的UML结构图如下:
从上图和前述可以看出,访问者模式中有两个层次结构:
正式由于有这两个层次结构,在增加新的访问者时,不必修改已有的代码,通过继承抽象访问者即可实现扩展,符合开闭原则,系统扩展性较好。但是在增加新的元素时,既要修改抽象访问者类(增加访问新增元素方法的声明),又要修改具体访问者(增加新的具体访问者类),不符合开闭原则。
访问者模式的示例代码如下:
#ifndef __DEMO_H__
#define __DEMO_H__
// 抽象访问者 Visitor
class Visitor
{
public:
virtual void visit(ConcreteElementA*) = 0;
virtual void visit(ConcreteElementB*) = 0;
};
// 具体访问者 ConcreteVisitor
class ConcreteVisitor :public Visitor
{
public:
// 实现一种针对特定元素的访问操作
void visit(ConcreteElementA*){
// 元素A的访问操作代码
}
void visit(ConcreteElementB*){
// 元素B的访问操作代码
}
};
// 抽象元素
class Element
{
public:
// 声明抽象方法,以一个抽象访问者的指针作为函数参数
virtual void accept(Visitor*) = 0;
};
// 具体元素
class ConcreteElement :public Element
{
public:
void accept(Visitor* visitor){
visitor->visit(this);
}
};
// 对象结构
class ObjectStructure
{
public:
// 提供接口接受访问者访问
void accept(Visitor* visitor){
// 遍历访问对象结构中的元素
for (){
elementList[i]->accept(visitor);
}
}
void addElement(){}
void removeElement(){}
private:
lsitelementList;
};
#endif
Jungle作为一名顾客,去超市购物,加入购物车的商品包括两种苹果和两本书,结账时收银员需要计算各个商品的的价格。本例Jungle采用访问者模式来模拟该过程。
本例中,客户Jungle和收银员都会去访问商品,但关心的地方不同:Jungle关心的是苹果和书的单价、品牌等,收银员关注的是商品的价格。因此,客户Customer和收银员Cashier是具体访问者,而苹果Apple和书Book是具体被访问元素;而购物车则是对象结构。本例的UML图如下:
// 抽象元素
class Element
{
public:
Element(){};
virtual void accept(Visitor*) = 0;
void setPrice(int iPrice){
this->price = iPrice;
}
int getPrice(){
return this->price;
}
void setNum(int iNum){
this->num = iNum;
}
int getNum(){
return num;
}
void setName(string iName){
this->name = iName;
}
string getName(){
return this->name;
}
private:
int price;
int num;
string name;
};
// 具体元素:Apple
class Apple :public Element
{
public:
Apple();
Apple(string name, int price);
void accept(Visitor*);
};
实现:
Apple::Apple(){
setPrice(0);
setNum(0);
setName("");
}
Apple::Apple(string name, int price){
setPrice(price);
setNum(0);
setName(name);
}
void Apple::accept(Visitor* visitor){
visitor->visit(this);
}
// 具体元素:Book
class Book :public Element
{
public:
Book();
Book(string name, int price);
void accept(Visitor*);
};
实现:
Book::Book(){
setPrice(0);
setNum(0);
setName("");
}
Book::Book(string iName, int iPrice){
setPrice(iPrice);
setNum(0);
setName(iName);
}
void Book::accept(Visitor* visitor){
visitor->visit(this);
}
// 抽象访问者
class Visitor
{
public:
Visitor(){};
// 声明一组访问方法
virtual void visit(Apple*) = 0;
virtual void visit(Book*) = 0;
};
// 具体访问者:顾客
class Customer :public Visitor
{
public:
Customer();
Customer(string iName);
void setNum(Apple*, int);
void setNum(Book*, int);
void visit(Apple* apple);
void visit(Book* book);
private:
string name;
};
实现:
Customer::Customer(){
this->name = "";
}
Customer::Customer(string iName){
this->name = iName;
}
void Customer::setNum(Apple* apple, int iNum){
apple->setNum(iNum);
}
void Customer::setNum(Book* book, int iNum){
book->setNum(iNum);
}
void Customer::visit(Apple* apple){
int price = apple->getPrice();
printf(" %s \t单价: \t%d 元/kg\n", apple->getName().c_str(), apple->getPrice());
}
void Customer::visit(Book* book){
int price = book->getPrice();
string name = book->getName();
printf(" 《%s》\t单价: \t%d 元/本\n", book->getName().c_str(), book->getPrice());
}
class Cashier :public Visitor
{
public:
Cashier();
void visit(Apple* apple);
void visit(Book* book);
};
实现:
Cashier::Cashier(){
}
void Cashier::visit(Apple* apple){
string name = apple->getName();
int price = apple->getPrice();
int num = apple->getNum();
int total = price*num;
printf(" %s 总价: %d 元\n", name.c_str(), total);
}
void Cashier::visit(Book* book){
int price = book->getPrice();
string name = book->getName();
int num = book->getNum();
int total = price*num;
printf(" 《%s》 总价: %d 元\n", name.c_str(), total);
}
class ShoppingCart
{
public:
ShoppingCart(){}
void addElement(Element* element){
printf(" 商品名:%s, \t数量:%d, \t加入购物车成功!\n", element->getName().c_str(), element->getNum());
elementList.push_back(element);
}
void accept(Visitor* visitor){
for (int i = 0; i < elementList.size(); i++){
elementList[i]->accept(visitor);
}
}
private:
vectorelementList;
};
#include "Element.h"
#include "Visitor.h"
#include "ShoppingCart.h"
#include
int main()
{
Apple *apple1 = new Apple("红富士苹果", 7);
Apple *apple2 = new Apple("花牛苹果", 5);
Book *book1 = new Book("红楼梦", 129);
Book *book2 = new Book("终结者", 49);
Cashier* cashier = new Cashier();
Customer* jungle = new Customer("Jungle");
jungle->setNum(apple1, 2);
jungle->setNum(apple2, 4);
jungle->setNum(book1, 1);
jungle->setNum(book2, 3);
ShoppingCart* shoppingCart = new ShoppingCart();
shoppingCart->addElement(apple1);
shoppingCart->addElement(apple2);
shoppingCart->addElement(book1);
shoppingCart->addElement(book2);
printf("\n\n");
shoppingCart->accept(jungle);
printf("\n\n");
shoppingCart->accept(cashier);
printf("\n\n");
system("pause");
return 0;
}
上述代码运行结果如下:
上述代码资源见https://github.com/FengJungle/DesignPattern
访问者模式的结构相对较复杂,在实际应用中使用频率较低。如果系统中存在一个复杂的对象结构,且不同的访问者对其具有不同的操作,那么可以考虑使用访问者模式。访问者模式的特点总结如下:
优点:
缺点: