C++11 实现优雅的访问者模式

336 阅读2分钟

「这是我参与11月更文挑战的第 4 天,活动详情查看:2021最后一次更文挑战」。

参加该活动的第 8 篇文章

参考原文地址:(原创)C++11 改进我们的模式之改进访问者模式

我对文章的格式和错别字进行了调整,并在他的基础上,根据我自己的理解把重点部分进一步解释完善(原作者的代码注释甚少)。以下是正文。

正文

本次讲 C++11 改进我们的模式之改进访问者模式。

访问者模式是 GOF 23 个设计模式中比较复杂的模式之一,但是它的功能也很强大,非常适合稳定的继承层次(即以下代码示例中,基类 Base 和 stA 、stB 的稳定关系) 中对象的访问,可以在不修改被访问类的情况下,动态给被访问对象添加职责(改变它们的行为),这正是访问者模式强大的地方;

但是它的实现又造成了两个继承层次的强烈耦合(即下例中 stA、stB 必须派生自 Base 类,且 Visitor 基类和 Visitor 派生类中都要对具体的 stA、stB 的行为分开定义,如下结构图所示),这也是被人诟病的地方,可以说是让人爱又让人恨的模式(所以它的使用范围也比较小)。

访问者模式的结构如下: image.png

而 C++11 实现的访问者模式能一定程度上解决以上的问题(即具体到 Visitor 的派生类中才定义了 stA、stB 的具体职责(行为),Visitor 基类中是不用知道这些的)。C++11 版本的访问者模式中,定义新的具体访问者类是很容易的,扩展性很好,被访问对象类的继承层次也不用做任何修改【即不必修改下例中派生自 Base 类的 stA、stB 类的行为,直接在具体访问者类的方法中定义它们即可】。

具体代码:

/// @note 访问者基类
template <typename... Types>
struct Visitor;

/// @note 虚基类、泛化的可变参模板类
template <typename T, typename... Types>
struct Visitor<T, Types...> : Visitor<Types...>
{
    using Visitor<Types...>::Visit;
    virtual void Visit(const T &) = 0; ///< 注意!这里既有静态多态又有动态多态
};

template <typename T>
struct Visitor<T>
{
    virtual void Visit(const T &) = 0;
};

注意,上面的代码为每个类型都只定义了一个泛化的纯虚函数 Visit,简洁又清爽 。

下面看看如何使用 Visitor 访问该继承体系(stA、stB 继承自 Base)的对象。


struct stA;
struct stB;

/// @note 基类中指定了被访问(派生类)对象 —— stA 、stB
struct Base
{
    typedef Visitor<stA, stB> MytVisitor; ///< 注意!如果继承体系 stA、 stB 相对稳定的话,只是需要时不时修改它们的行为,则该(特化的访问者基类)类型不用变动
    
    virtual void Accept(MytVisitor &) = 0; ///< 传入特化的访问者基类类型的引用
};

struct stA : Base
{
    double val;
    void Accept(Base::MytVisitor &v)
    {
        v.Visit(*this); ///< 将 *this 传给访问者对象,根据其类型选择调用(具体访问者提供)的方法
    }
};

struct stB : Base
{
    int val;
    void Accept(Base::MytVisitor &v)
    {
        v.Visit(*this); ///< 将 *this 传给访问者对象,根据其类型选择调用(具体访问者提供)的方法
    }
};

/// @note Visitor 派生类中定义了 stA、stB 的具体职责(行为)而不用直接修改 stA、stB 类
struct PrintVisitor : Base::MytVisitor
{
    void Visit(const stA &a)
    {
        std::cout << "from stA: " << a.val << std::endl;
    }
    void Visit(const stB &b)
    {
        std::cout << "from stB: " << b.val << std::endl;
    }
};

测试代码:

void TestVisitor()
{
    PrintVisitor vis;
    
    stA a;
    a.val = 8.97;
    stB b;
    b.val = 8;
    
    Base *base = &a;
    base->Accept(vis);
    base = &b;
    base->Accept(vis);
}

测试结果:

from stA: 8.97

from stB: 8