设为首页 加入收藏

TOP

再议访问者模式 - Visitor vs Acyclic Visitor(一)
2015-07-20 17:40:25 来源: 作者: 【 】 浏览:7
Tags:访问者 模式 Visitor Acyclic

访问者模式的应用条件相对苛刻一些。通常它被使用在类似于这种场景:被访问类不太会增加新的子类,但是它的方法会经常变动。

先来简单回顾一些经典访问者。

传统Visitor模式

假设我们需要创建一个Encryption类,如下:

class Encryption
{
public:
    virtual std::string Encrypt(const std::string& strContent);
};

class AESEncryption : public Encryption
{
public:
    virtual std::string Encrypt(const std::string& strContent) override
    {
        printf("do aes encryption\n");
    }
};

这个类提供加密功能。现在考虑这么一种情况,因为不同需求,经常需要更改接口函数或者新增函数,比如新增一个函数:EncryptFile().

如果接口函数变动比较频繁的话,那么访问者就很适合。我们尝试把上面的代码转变成如下的样子:

class Encryption
{
public:
    virtual void accept(EncryptionVisitor* v);
};

class AESEncryption : public Encryption
{
public:
    virtual void accept(EncryptionVisitor* v) override
    {
        v->VisitAes(this);
    }

    std::string AESEncrypt(const std::string& strContent)
    {
        printf("do encrypt\n");
        std::string strResult = strContent;
        return strResult;
    }
};

class EncryptionVisitor
{
public:
    virtual void VisitAes(AESEncryption* e) = 0;
};

class StringEncryptionVisitor : public EncryptionVisitor
{
public:
    StringEncryptionVisitor(const std::string& strContent) : m_strContent(strContent)
    {}

    virtual void VisitAes(AESEncryption* e) override
    {
        m_strResult = e->AESEncrypt(m_strContent);
    }

    std::string GetResult() const { return m_strResult; }
private:
    std::string m_strContent;
    std::string m_strResult;
};

上面总共有4个类,Encryption,AesEncryption,EncryptionVisitor和StringEncryptionVisitor。我们现在可以这么使用:

Encryption* e = new AesEncryption();
StringEncryptionVisitor* v = new StringEncryptionVisitor("hello world");
e->accept(v);
string result = v->GetResult();

这么做有啥好处呢?比如我们现在要增加一个新的函数用来从一个本地文件读取内容,加密返回。如果不使用visitor,那么就必须在Encryption类里面增加接口函数了,从而所有Encryption的子类都要实现相应的函数。现在使用访问者,就可以这么做:

class FileEncryptionVisitor : public EncryptionVisitor
{
public:
    FileEncryptionVisitor(const std::string& strPath) : m_strPath(strPath)
    {}

    virtual void VisitAes(AESEncryption* e) override
    {
        std::ifstream ifs(m_strPath);
        std::string strContent;
        ifs >> strContent;

        m_strResult = e->AESEncrypt(strContent);
    }

    std::string GetResult() const { return m_strResult; }
private:
    std::string m_strPath;
    std::string m_strResult;
};

然后调用:

Encryption* e = new AESEncryption();
FileEncryptionVisitor* v = FileEncryptionVisitor("c:\\test.txt");
e->accept(v);
std::string result = v->GetResult();

这样就成功从文件读取内容,并且加密,获取结果。

通过访问者,我们可以做到,不更改基类的接口而增加新的功能。其实,仔细看一下上面的代码,基本思路就是用一个新的访问者子类代替了原来Encryption里面的函数。每一个访问者子类都可以实现一个功能。这样如果想新增Encryption函数,只需要新增访问者子类就行了。每个访问者子类的数据成员就好像函数参数一样。

如果我们现在新增一个Encryption子类,会发生什么?

ok,这就意味这访问者类里面不得不新增一个函数,比如新增一个Encryption子类RsaEncryption,那么访问者类必须相应的增加一个函数。然后所有Visitor子类都必须实现这个函数。这是访问者模式的一个很大缺陷。

class EncryptionVisitor
{
public:
    virtual void VisitAes(AESEncryption* e) = 0;
    virtual void VisitRsa(RsaEncryption* e) = 0;
};

访问者模式的结构图如下:

\

访问者模式可以形象的用一个矩阵来表示:

\

如果现在新增一个MD5Encryption,那么EncryptionVisitor必须新增一个函数:VisitMD5(MD5Encryption* e)

同时,所有EncryptionVisitor的子类都必须实现它。

如果新增一个访问者子类,也必须实现所有访问者接口的纯虚函数。

\

OK, 访问者模式的一个比较大的问题其他也体现在上面的矩阵里面。每当新增一个元素子类(Encryption子类),就必须更新所有的访问者。就好象矩阵里面得填进去一样。

那么有没有办法提升呢?办法总是有的。

Uncle Bob(Robert C Martin),这哥们提出了一个ACyclic Visitor,无环访问者模式。看一下上面的结构图,Encryption和EncryptionVisit互相依赖了,这个像不像一个环。ACyclic Visitor的意图就是打破这个环。

ACyclic Visitor (无环访问者模式)

其实思路还是挺简单的,我们把访问者退化,也就是访

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇HDOJ 4474 Yet Another Multiple .. 下一篇HDU5015-233 Matrix(矩阵快速幂)

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

·HTTP协议深度解析: (2025-12-25 16:21:03)
·HTTP 概述 - MDN (2025-12-25 16:21:00)
·视频直播为什么要用u (2025-12-25 16:20:57)
·用 Python 进行数据 (2025-12-25 15:49:09)
·如何学习Python数据 (2025-12-25 15:49:07)