问者里面没有任何纯虚函数。
class EncryptionVisitor
{
public:
virtual ~EncryptionVisitor(){}
};
然后,访问者子类变成了
class StringEncryptionVisitor : public EncryptionVisitor
{
public:
StringEncryptionVisitor(const std::string& strContent) : m_strContent(strContent)
{}
virtual void VisitAes(AESEncryption* e)
{
m_strResult = e->AESEncrypt(m_strContent);
}
std::string GetResult() const { return m_strResult; }
private:
std::string m_strContent;
std::string m_strResult;
};
元素类Encryption那边就变成了:
class AESEncryption : public Encryption
{
public:
virtual void accept(EncryptionVisitor* v) override
{
StringEncryptionVisitor* sv = (StringEncryptionVisitor*)v;
sv->VisitAes(this);
}
std::string AESEncrypt(const std::string& strContent)
{
printf("do encrypt\n");
std::string strResult = strContent;
return strResult;
}
};
注意在accept函数里面有一个转型动作,把父类指针转型成子类指针。这个并不是一个好的办法,通常我们是禁止这么做的。但是在特定场合也不是说不能用,就像这里。
我们可以考虑使用C++的dynamic_cast。很多时候从accept函数角度来讲,它并不知道传进来的基类指针指向哪一个子类对象。这里可以有一些技巧,比如访问者基类里面存放一个子类标记,然后accept()内部就可以根据这个子类标记来进行相对准确的转型,再调用子类函数,比如VisitAes().
这么做有什么好处呢?
考虑之前的问题,新增一个MD5Encryption类,传统访问者必须在所有子类里面实现函数VisitMD5.那么现在就无需在访问者抽象基类里面增加VisitMD5函数了。(实际上访问者抽象基类里面没有任何纯虚函数)。我们现在只需在相应的访问者子类里面来处理就是了,换句话说就是在某些对MD5Encrytion类感兴趣的访问者子类里面处理就行了。
比如:
class FileEncryptionVisitor : public EncryptionVisitor
{
public:
FileEncryptionVisitor(const std::string& strPath) : m_strPath(strPath)
{}
void VisitAes(AESEncryption* e)
{
std::ifstream ifs(m_strPath);
std::string strContent;
ifs >> strContent;
m_strResult = e->AESEncrypt(strContent);
}
void VisitMD5(MD5Encrytpion* e)
{
std::ifstream ifs(m_strPath);
std::string strContent;
ifs >> strContent;
m_strResult = e->MD5Encrypt(strContent);
}
std::string GetResult() const { return m_strResult; }
private:
std::string m_strPath;
std::string m_strResult;
};
MD5Encryption类的accept函数如下:
void MD5Encryption::accept(EncryptionVisitor* e)
{
FileEncryptionVisitor* fv = (FileEncryptionVisitor*)v;
fv->VisitMD5(this);
}
客户调用还是一样:
Encryption* e = new MD5Encryption();
FileEncryptionVisitor* v = FileEncryptionVisitor("c:\\test.txt");
e->accept(v);
std::string result = v->GetResult();
ACyclic Visitor的思想就是把访问者抽象基类退化,没有任何接口需要子类实现。每个访问者子类都自己负责被元素类调用的函数。然后元素类(被访问者)通过一个基类到子类的转型来调用访问者具体类的函数。这么做的好处就是访问者抽象类跟被访问类之间解耦了。使得新增被访问类的代价减少了。无环访问者的结构图如下:
vc+jrDwvcD4KPHA+MS4gRW5jcnlwdGlvblZpc2l0b3Kyu9TZ0sDAtdPa1KrL2L7fzOXA4KGjyKG2+LT61q61xMrHt8POytXf19PA4NLAwLXT2tSqy9i+38zlwOChozwvcD4KPHA+Mi4gw7/Su7j2t8POytXfvt/M5cDgsqKyu9K7tqjSqtLAwLXT2sv509C1xNSqy9i+38zlwOCjrL/J0tTWu9LAwLXT2tDo0qq1xNSqy9i+38zlwOChozwvcD4KPHA+1q7HsMu1tb20q82zt8POytXfvs3P8crH0ru49r7Y1fOjrMTHw7TO3ru3t8POytXfxNijv77Nz/HKx9K7uPbPocrovtjV86GjPC9wPgo8cD48aW1nIHNyYz0="https://www.cppentry.com/upload_files/article/49/1_vlnw8__.png" alt="">
有关访问者,个人理解现在也就这么多了。访问者的使用条件相对比较苛刻一些,但是在某些场合也确实很有前途。
在使用的时候是选择传统的,还是无环访问者,就视具体情况而定了。如果元素类(被访问者)变动相对多一些,那么无环访问者模式就相对合适一些。
当然,任何面向对象的模式都是会带来一定的副作用的,这点需要牢记在心,不要滥用。