在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。
在软件构建过程中,由于需求的改变,某些类层次结构中需要增加新的行为,如果这些操作在基类中进行更改,那么派生类也要做出响应的变更,给变更工作带来繁重的负担。访问器模式,就是把原来基类和派生类的实现放在访问器类中,各个派生类访问这些访问器的操作方法。而访问器中的操作方法接受派生类的实体,对派生类的属性进行操作。这样要变更操作方法只需要变更具体的访问器,而不需要变更基类和派生类。
以文件流和网络流为例, 其访问器模式 ,流的实现结构如下:
/*********建立稳定的实体类和抽象访问器类************/
class IVisitor{
public:
virtual void visitFileStream(FileStream & )=0;
virtual void visitNetWorkStream(NetWorkStream &)=0;
}
class IStream{
public:
void acceptVisitor(IVisitor&) = 0;
}
class FileStream : public IStream{
public:
File file_;
void acceptVisitor(IVisitor& v){ v.visitFileStream(*this);}
}
class NetWorkStream : public IStream{
public:
string url_;
void acceptVisitor(IVisitor& v){ v.visitNetWorkStream(*this);}
}
假如:流的功能有 读和写 , 那么 读操作和写操作不再 流对象中实现,而是把这些操作变成一个类的对象,这个类的对象派生自访问器类
/*****给流添加读写操作,(用伪码演示)*****/
class ReadStreamVisitor :IVisitor{
string output_;
public:
virtual void visitFileStream(FileStream &fs){
File file = fs.file_;
....
output_=file.getcontent();
}
virtual void visitNetWorkStream(NetWorkStream & ns){
NetwoarkRequest NetwoarkRequest(ns.url_);
...
output_=NetwoarkRequest.getcontent();
}
}
class WriteStreamVisitor :IVisitor{
public:
string inputstr_; //输入参数可以用Visitor存
public:
virtual void visitFileStream(FileStream &fs){
File file = fs.file_;
....
file.write(inputstr_);
}
virtual void visitNetWorkStream(NetWorkStream & ns){
NetwoarkRequest NetwoarkRequest(ns.url_);
...
NetwoarkRequest.write(inputstr_);
}
}
调用示例:
void main(){
//读取网络内容
NetWorkStream ns;
ns.url_ = "www.baidu.com";
ReadStreamVisitor readVisitor;
ns.accept(readVisitor);
cout<<"网络读取的内容有:" <<readVisitor.output_;
//写文件内容
File file("C:/test.txt");
FileStream fs(file);
WriteStreamVisitor writeVisitor;
writeVisitor.inputstr_ = "写入一些文本"'
fs.accept(WriteStreamVisitor );
}
如果需求要求变更写操作方法,那么只需要变动WriteStreamVisitor中的方法而不需要变动其他类。如果需求要求新增一个流方法,只需要新增一个Visitor派生类就可以。而不需要从基类从派生类从下至上的修改,造成业务需求的变更影响范围大。
当然,访问者模式只适合类的层次结构稳定,操作扩展变动频繁的业务场景。所谓的层次结构稳定就是实体类的关系很少根据需求而变化。例如如果新增一个 DataBaseStream,那么每个Visitor都要再实现关于DataBaseStream的操作。访问者模式的目的就是为了减少 实体和实体方法间的耦合程度,减小变更实体方法的影响性。如果层次结构不稳定,那么使用访问者模式就得不偿失.