[10 使用C++11开发一个轻量级的AOP库] 代理模式的应用

代理模式的定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。

代理模式的主要优点有:
1 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
2 代理对象可以扩展目标对象的功能;
3 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性

其主要缺点是:
1 代理模式会造成系统设计中类的数量增加
2 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
3 增加了系统的复杂度;

类图如下:

10.1 AOP介绍

AOP(Aspect-Oriented Programming,面向方面编程)。AOP把软件系统分为了两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的是横切关注点。

10.2 AOP的简单实现

动态织入AOP一般采用动态代理的方式,在运行期对方法进行拦截,将切面动态织入到方法中。

代理模式实现方法的拦截:

#include <iostream>
#include <string>
#include <iostream>
using namespace std;

class IHello
{
public:
    IHello()
    {
    }

    virtual ~IHello()
    {
    }

    virtual void Output(const string& str)
    {
    }
};

class Hello : class IHello
{
public:
    void Output(const string& str) override
    {
        cout << str << endl;
    }
};

class HelloProxy : public IHello
{
public:
    HelloProxy(IHello* p) : m_ptr(p)
    {
    }    

    ~HelloProxy()
    {
        delete m_ptr;
        m_ptr = nullptr;
    }

    void Output(const string& str) final
    {
        cout << "Before real Output" << endl;
        m_ptr->Output(str);
        cout << "After real Output" << endl;
    }
private:
    IHello* m_ptr;    
};

void TestProxy()
{
    std::shared_ptr<IHello> hello = std::make_shared<HelloProxy>(new Hello());
    hello->Output("It is a test");
}

输出结果如下:
Before real Output
It is a test
After real Output

这里Hello::Output就是核心逻辑,HelloProxy就是一个切面,非核心逻辑在切面里实现。

代理模式实现的AOP有以下不足:

(1)不够灵活,不能自由组合多个切面。

(2)耦合性强,每个切面必须从基类继承,并实现基类的接口。

10.3 轻量级AOP框架的实现

要实现灵活组合切面,可以将切面作为模板的参数(如下代码AP)。为了降低耦合性,通过模板来做约束,每个切面对象必须有Before(Args...)或After(Args...)方法,用来处理核心逻辑执行前后的非核心逻辑。

AOP的实现如下:

/*
解释一下这段代码的功能,通过宏定义来定义一个member,来检测一个类T有没有名为member的函数。
比如HAS_MEMBER(func),经过宏展开之后,就可以用has_member_func<T,Args>的
方式来检测一个类是否有成员函数func。
*/
#define HAS_MEMBER(member)\
template<typename T, typename ... Args>struct has_member_##member\
{\
private:\
	template<typename U> static auto Check(int)->decltype(std::declval<U>().member\
(std::declval<Args>()...), std::true_type());\
	template<typename U> static std::false_type Check(...);\
public:\
	enum {value = std::is_same<decltype(Check<T>(0)), std::true_type>::value };\
};\

HAS_MEMBER(Before)
HAS_MEMBER(After)
template<typename Func, typename.. Args>
struct Aspect : NonCopyable
{
    Aspect(Func&& f) : m_func(std::forward(f))
    {
    }

    /*
      模板偏特化:
      通过typename std::enable_if<bool>::type传入一个bool值
      就能推导出这个type是不是未定义的
    */
    template<typename T>
	typename std::enable_if<has_member_Before<T, Args...>::value && has_member_After<T, Args...>::value>::type Invoke(Args&&... args, T&& aspect)
	{
		aspect.Before(std::forward<Args>(args)...);    //核心逻辑之前的切面逻辑
		m_func(std::forward<Args>(args)...);           //核心逻辑
		aspect.After(std::forward<Args>(args)...);     //核心逻辑之后的切面逻辑   
	}

    template<typename T>
	typename std::enable_if<has_member_Before<T, Args...>::value && !has_member_After<T, Args...>::value>::type Invoke(Args&&... args, T&& aspect)
	{
		aspect.Before(std::forward<Args>(args)...);    //核心逻辑之前的切面逻辑
		m_func(std::forward<Args>(args)...);           //核心逻辑
	}

template<typename T>
	typename std::enable_if<!has_member_Before<T, Args...>::value && has_member_After<T, Args...>::value>::type Invoke(Args&&... args, T&& aspect)
	{
		m_func(std::forward<Args>(args)...);           //核心逻辑
		aspect.After(std::forward<Args>(args)...);     //核心逻辑之后的切面逻辑   
	}

    template<typename Head, typename... Tail>
    void Invoke(Args&&... args, Head&& headAspect, Tail&&... tailAspect)
    {
        headAspect.Before(std::forward<Args>(args)...);
        Invoke(std::forward<Args>(args)..., std::forward<Tail>(tailAspect)...);
        headAspect.After(std::forward<Args>(args)...);
    }

private:
    Func m_func;
};
template<typename T> using identity_t = T;

// AOP的辅助函数,简化调用
template<typename... AP, typename... Args, typename Func>
void Invoke(Func&& f, Args&&... args)
{
    Aspect<Func, Args...> asp(std::forward<Func>(f));
    // 调用到Aspect的最后一个Invoke
    asp.Invoke(std::forward<Args>(args)..., identity_t<AP>()...);
}



以上代码切面中的约束:

(1)通过模板参数化切面,要求切面必须有Before或After函数

(2)Before或After函数的入参必须和核心逻辑的函数入参保持一致

应用举例,带日志和计时切面的AOP:

struct TimeElapseAspect
{
    void Before(int i)
    {
        m_lastTime = m_t.elapsed();
    }
    void After(int i)
    {
        cout << "time elapsed: " << m_t.elapsed() - m_lastTime << endl;
    }
private:
    double m_lastTime;
    Timer m_t;
};

struct LoggingAspect
{
    void Before(int i)
    {
        cout << "entering" << endl;
    }
    void After(int i)
    {
        cout << "leaving" << endl;    
    }
};

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

int main()
{
    Invoke<LoggingAspect, TimeElapseAspect>(&foo, 1);    

    return 0;
}

输出结果:
entering
start timer
real function: 1
time elapsed: 0.001
leaving

参考网址:

http://c.biancheng.net/view/1359.html

SFINAE技术初涉_qq_37925512的博客-CSDN博客

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值