C++11 实现优雅的 AOP 框架(动态代理模式)

4,149 阅读2分钟

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

参加该活动的第 9 篇文章

参考原文地址:(原创) C++11 改进我们的模式之改进代理模式,实现通用的 AOP 框架

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

正文

原作者文章中讲的是如何改进代理模式,具体来说是动态代理模式,动态代理模式一般实现 AOP 框架,不懂 AOP 的童鞋看这里

原作者推荐的介绍 AOP 文章洋洋洒洒写了一大堆,而且年代久远,没耐心的童鞋我建议还是看看知乎的问答

原作者前面的博文也实现了一个 AOP 框架(可以参考:(原创) C++ 轻量级 AOP 框架),但是那个 AOP 框架存在一个缺陷就是不支持切面的组合,这一点大大降低了其通用性

本次通过 C++11 改进,使 AOP 框架更完善:支持切面组合,不要求切面必须派生于某个基类,不用虚函数调用,性能更好,功能更强大

切面代理类的定义

Aspect 是切面代理类(别看名叫 Aspect 其实它是个代理类,不是切面类) 内部会调用具体的(组合)切面类的 Before/After 方法 并且负责在具体切入点织入外部传入的可执行实体

具体代码和注释如下

/// @note 切面代理类
struct Aspect : boost::noncopyable
{
    template <typename Func>
    Aspect(const Func &f) : m_func(f) ///< 构造时初始化函数对象
    {
    }

    template <typename T>
    void Invoke(T &&value)
    {
        value.Before();
        m_func(); ///< 执行封装的可调用实体,该位置是该切面类的切入点
        value.After();
    }

    template <typename Head, typename... Tail>
    void Invoke(Head &&head, Tail &&...tail) ///< 支持切面组合, Head Tail 并不要求派生于某个基类
    {
        head.Before();
        Invoke(std::forward<Tail>(tail)...);
        head.After();
    }

private:
    std::function<void()> m_func;
};

template <typename... AP>
void Invoke(const std::function<void()> &f)
{
    Aspect msp(f); ///< 构造切面(组合)的代理对象
    msp.Invoke(AP()...);
}

注意,切面类有要求,切面类中必须要有 Before/After 方法,否则编译不过,允许空实现。

下面的 AA BB CC DD 类都是切面类

具体切面类与测试类、函数的定义

代码如下

struct AA
{
    void Before()
    {
        cout << "Before from AA" << endl;
    }

    void After()
    {
        cout << "After from AA" << endl;
    }
};

struct BB
{
    void Before()
    {
        cout << "Before from BB" << endl;
    }

    void After()
    {
        cout << "After from BB" << endl;
    }
};

struct CC
{
    void Before()
    {
        cout << "Before from CC" << endl;
    }

    void After()
    {
        cout << "After from CC" << endl;
    }
};

struct DD
{
    void Before()
    {
    }

    void After()
    {
    }
};

/// @note 测试结构体(类)
struct TT
{
    void g()
    {
        cout << "real g function" << endl;
    }

    void h(int a)
    {
        cout << "real h function: " << a << endl;
    }
};

/// @note 测试无参函数
void GT()
{
    cout << "real GT function" << endl;
}

/// @note 测试有参函数
void HT(int a)
{
    cout << "real HT function: " << a << endl;
}

测试 AOP 框架

代码与详细注释如下:

void TestAOP()
{
    TT tt;
    std::function<void()> ff = std::bind(&TT::g, &tt);
    /// @note 组合了两个切面类 AA BB
    Invoke<AA, BB>([&ff]()
                   { ff(); });   ///< 织入函数对象
    Invoke<AA, BB>([&tt]()
                   { tt.g(); }); ///< 织入对象

    int aa = 3;
    Invoke<AA, BB>(&GT);         ///< 织入无参方法
    Invoke<AA, BB>([aa]()
                   { HT(aa); }); ///< 织入带参的方法

  
    std::function<void(int)> ff1 = std::bind(&TT::h, &tt, std::placeholders::_1);
    /// @note 组合了四个切面类 AA BB CC DD
    Invoke<AA, BB, CC, DD>([&ff1, aa]()  ///< 织入带参数的成员函数和对象
                           { ff1(aa); }); 
    /// @note 组合了两个切面类 AA BB
    Invoke<AA, BB>([&tt, aa]()           ///< 织入带参数的成员函数和对象
                   { tt.h(aa); });
}

测试结果:

image.png

原作者的更新

更新:代码在 GCC 下编译没问题,但在 vs2013 下编译不过,是微软的 bug ,如果要在 vs2013 下编译通过,需要做一点修改

template <typename T>
using identity_t = T;

template <typename... AP>
void Invoke(const std::function<void()> &f)
{
    Aspect msp(f);
    msp.Invoke(identity_t<AP>()...);
}