「这是我参与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>(>); ///< 织入无参方法
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); });
}
测试结果:
原作者的更新
更新:代码在 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>()...);
}