设计模式介绍
一、访问者模式
1. 访问者模式定义
表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
2. 访问者模式本质
访问者模式本质:预留通路,回调实现
3. 访问者模式结构和说明
(1) 结构
(2) 调用顺序
4. 访问者模式适用情况
-
如果想对一个对象结构实施一些依赖于对象结构中具体类的操作,可以使用访问者模式。
-
如果想对一个对象结构中的各个元素进行很多不同的而且不相关的操作,为了避免这些操作使类变得杂乱,可以使用访问者模式。把这些操作分散到不同的访问者对象中去,每个访问者对象实现同一类功能。
-
如果对象结构很少变动,但是需要经常给对象结构中的元素对象定义新的操作,可以使用访问者模式。
5. 访问者模式优缺点
(1) 优点
- 好的扩展性
能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。 - 好的复用性
可以通过访问者来定义整个对象结构通用的功能,从而提高复用程度。 - 分离无关行为
可以通过访问者来分离无关的行为,把相关的行为封装在一起,构成一个访问者,这样每一个访问者的功能都比较单一。
(2) 缺点
- 对象结构变化很困难
不适用于对象结构中的类经常变化的情况,因为对象结构发生了改变,访问者的接口和访问者的实现都要发生相应的改变,代价太高。 - 破坏封装
访问者模式通常需要对象结构开放内部数据给访问者和ObjectStructrue,这破坏了对象的封装性。
6. 相关模式
- 访问者模式和组合模式
这两个模式可以组合使用。
如同前面示例的那样,通过访问者模式给组合对象预留下扩展功能的接口,使得为组合模式的对象结构添加功能非常容易。 - 访问者模式和装饰模式
这两个模式从表面上看功能有些相似,都能够实现在不修改原对象结构的情况下修改原对象的功能。但是装饰模式更多的是实现对已有功能的加强、修改或者完全全新实现:而访问者模式更多的是实现为对象结构添加新的功能。 - 访问者模式和解释器模式
这两个模式可以组合使用。
解释器模式在构建抽象语法树的时候,是使用组合模式来构建的,也就是说解释器模式解释并执行的抽象语法树是一个组合对象结构,这个组合对象结构是很少变动的,但是可能经常需要为解释器增加新的功能,实现对同一对象结构的不同解释和执行的功能,这正是访问者模式的优势所在,因此在使用解释器模式的时候通常会组合访问者模式来使用。
访问者模式示例代码
#include <iostream>
#include <string>
#include <functional>
#include <map>
using namespace std;
#include <vector>
class Element;
class CPU;
class VideoCard;
class MainBoard;
class Visitor {
public:
Visitor(std::string name) {
visitorName = name;
}
virtual void visitCPU( CPU* cpu ) {};
virtual void visitVideoCard( VideoCard* videoCard ) {};
virtual void visitMainBoard( MainBoard* mainBoard ) {};
std::string getName() {
return this->visitorName;
};
private:
std::string visitorName;
};
class Element {
public:
Element( std::string name ) {
eleName = name;
}
virtual void accept( Visitor* visitor ) {};
virtual std::string getName() {
return this->eleName;
}
private:
std::string eleName;
};
class CPU : public Element {
public:
CPU(std::string name) : Element(name) {}
void accept(Visitor* visitor) {
visitor->visitCPU(this);
}
};
class VideoCard : public Element {
public:
VideoCard(std::string name) : Element(name) {}
void accept(Visitor* visitor) {
visitor->visitVideoCard(this);
}
};
class MainBoard : public Element {
public:
MainBoard(std::string name) : Element(name) {}
void accept(Visitor* visitor) {
visitor->visitMainBoard(this);
}
};
class CircuitDetector : public Visitor {
public:
CircuitDetector(std::string name) : Visitor(name) {}
// checking cpu
void visitCPU( CPU* cpu ) {
std::cout << Visitor::getName() << " is checking CPU's circuits.(" << cpu->getName()<<")" << std::endl;
}
// checking videoCard
void visitVideoCard( VideoCard* videoCard ) {
std::cout << Visitor::getName() << " is checking VideoCard's circuits.(" << videoCard->getName()<<")" << std::endl;
}
// checking mainboard
void visitMainBoard( MainBoard* mainboard ) {
std::cout << Visitor::getName() << " is checking MainBoard's circuits.(" << mainboard->getName() <<")" << std::endl;
}
};
class FunctionDetector : public Visitor {
public:
FunctionDetector(std::string name) : Visitor(name) {}
virtual void visitCPU( CPU* cpu ) {
std::cout << Visitor::getName() << " is check CPU's function.(" << cpu->getName() << ")"<< std::endl;
}
// checking videoCard
void visitVideoCard( VideoCard* videoCard ) {
std::cout << Visitor::getName() << " is checking VideoCard's function.(" << videoCard->getName()<< ")" << std::endl;
}
// checking mainboard
void visitMainBoard( MainBoard* mainboard ) {
std::cout << Visitor::getName() << " is checking MainBoard's function.(" << mainboard->getName() << ")"<< std::endl;
}
};
class Computer {
public:
Computer(CPU* cpu,
VideoCard* videocard,
MainBoard* mainboard) {
elementList.push_back(cpu);
elementList.push_back(videocard);
elementList.push_back(mainboard);
};
void Accept(Visitor* visitor) {
for( std::vector<Element*>::iterator i = elementList.begin(); i != elementList.end(); i++ )
{
(*i)->accept(visitor);
}
};
private:
std::vector<Element*> elementList;
};
int main(){
CPU* cpu = new CPU("Intel CPU");
VideoCard* videocard = new VideoCard("XXX video card");
MainBoard* mainboard = new MainBoard("HUAWEI mainboard");
Computer* myComputer = new Computer(cpu, videocard, mainboard);
CircuitDetector* Dan = new CircuitDetector("CircuitDetector Dan");
FunctionDetector* Tom = new FunctionDetector("FunctionDetector Tom");
std::cout << "\nStep 1: Dan is checking computer's circuits." << std::endl;
myComputer->Accept(Dan);
std::cout << "\nStep 2: Tom is checking computer's functions." << std::endl;
myComputer->Accept(Tom);
return 0;
}
基于可变参数模板实现
GOF《设计模式》一书中也明确指出了Visitor模式需要注意的问题:定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要付出很大的代价。如果对象结构类经常改变,那么还是在这些类中定义这些操作较好。
也就是说,在访问者模式中被访问者应该是一个稳定的继承体系,如果这个继承体系经常变化,就会导致经常修改Visitor基类,因为在Visitor基类中定义了需要访问的对象类型,每增加一种被访问类型就要增加一个对应的纯虚函数,在上例中,如果需要增加一个新的被访问者ConcreteElement3,则需要在Visitor基类中增加一个纯虚函数:
virtual void Visit(ConcreteElement3* element) = 0;
根据面向接口编程的原则,我们应该依赖于接口而不应依赖于实现,因为接口是稳定的,不会变化的。而访问者模式的接口不太稳定,这会导致整个系统的不稳定,存在很大的隐患。要解决这个问题,最根本的方法是定义一个稳定的Visitor接口层,即不会因为增加新的被访问者而修改接口层,能否定义一个稳定的Visitor接口层呢?答案是肯定的,通过C++11进行改进,我们就可以实现这个目标。
通过可变参数模板就可以实现一个稳定的接口层,利用可变参数模板可以支持任意个数的参数的特点,可以让访问者接口层访问任意个数的被访问者,这样就不需要每增加一个新的被访问者就修改接口层,从而使接口层保持稳定。
#include <iostream>
#include <string>
#include <vector>
using namespace std;
/*--------Template--------*/
template <typename... Types>
class Visitor_;
template <typename T, typename... Types>
class Visitor_<T,Types...> : Visitor_<Types...>{
public:
using Visitor_<Types...>::Visit;//避免覆盖父类的同名方法
virtual void Visit(T&) = 0;
};
template <typename T>
class Visitor_<T>{
public:
virtual void Visit(T&) = 0;
};
class Element;
class CPU;
class VideoCard;
class MainBoard;
class Element;
using VisitorBase=Visitor_<CPU,VideoCard,MainBoard> ;
/*------------------*/
class Visitor : public VisitorBase {
public:
Visitor(std::string&& name) {
visitorName = std::forward<string>(name);
}
std::string getName() {
return this->visitorName;
};
private:
std::string visitorName;
};
class Element {
public:
Element( std::string&& name ) {
eleName = std::forward<string>(name);
}
virtual void accept( Visitor& visitor ) {};
virtual std::string getName() {
return this->eleName;
}
private:
std::string eleName;
};
/*----------- Elements -------------*/
class CPU : public Element {
public:
CPU(std::string name) : Element(std::move(name)) {}
void accept(Visitor& visitor) override {
visitor.Visit(*this);
}
};
class VideoCard : public Element {
public:
VideoCard(std::string name) : Element(std::move(name)) {}
void accept(Visitor& visitor) override {
visitor.Visit(*this);
}
};
class MainBoard : public Element {
public:
MainBoard(std::string name) : Element(std::move(name)) {}
void accept(Visitor& visitor) override {
visitor.Visit(*this);
}
};
/*----------- ConcreteVisitor -------------*/
class CircuitDetector : public Visitor{
public:
CircuitDetector(std::string name) : Visitor(std::move(name)) {}
// checking cpu
void Visit(CPU& cpu ) override {
std::cout << Visitor::getName() << " is checking CPU's circuits.(" << cpu.getName()<<")" << std::endl;
}
// checking videoCard
void Visit( VideoCard& videoCard ) override {
std::cout << Visitor::getName() << " is checking VideoCard's circuits.(" << videoCard.getName()<<")" << std::endl;
}
// checking mainboard
void Visit( MainBoard& mainboard ) override {
std::cout << Visitor::getName() << " is checking MainBoard's circuits.(" << mainboard.getName() <<")" << std::endl;
}
};
class FunctionDetector : public Visitor {
public:
FunctionDetector(std::string name) : Visitor(std::move(name)) {}
void Visit( CPU& cpu ) override {
std::cout << Visitor::getName() << " is check CPU's function.(" << cpu.getName() << ")"<< std::endl;
}
// checking videoCard
void Visit( VideoCard& videoCard ) override {
std::cout << Visitor::getName() << " is checking VideoCard's function.(" << videoCard.getName()<< ")" << std::endl;
}
// checking mainboard
void Visit( MainBoard& mainboard ) override {
std::cout << Visitor::getName() << " is checking MainBoard's function.(" << mainboard.getName() << ")"<< std::endl;
}
};
/*------------------------*/
class Computer {
public:
Computer(CPU* cpu,
VideoCard* videocard,
MainBoard* mainboard) {
elementList.push_back(cpu);
elementList.push_back(videocard);
elementList.push_back(mainboard);
};
void Accept(Visitor& visitor) {
for( std::vector<Element*>::iterator i = elementList.begin(); i != elementList.end(); i++ )
{
(*i)->accept(visitor);
}
};
private:
std::vector<Element*> elementList;
};
int main(){
CPU* cpu = new CPU("Intel CPU");
VideoCard* videocard = new VideoCard("XXX video card");
MainBoard* mainboard = new MainBoard("HUAWEI mainboard");
Computer* myComputer = new Computer(cpu, videocard, mainboard);
CircuitDetector* Dan = new CircuitDetector("CircuitDetector Dan");
FunctionDetector* Tom = new FunctionDetector("FunctionDetector Tom");
std::cout << "\nStep 1: Dan is checking computer's circuits." << std::endl;
myComputer->Accept(*Dan);
std::cout << "\nStep 2: Tom is checking computer's functions." << std::endl;
myComputer->Accept(*Tom);
return 0;
}