[C++之AOP]实战Aspect C++之观察者模式

Aspect C++支持“虚切面”,类似C++中的虚函数。方面可以继承,“派生方面”可以重写“基方面”的“虚切面”,类似C++中的类继承。

有了这个特性,就可以实现一些模式了,这里列出AOP中经典的观察者(Observer)模式 [注]

[注]这个说法不太妥当,观察者模式是经典的设计模式,这里的意思是说AOP经典的观察者模式实现。

它的代码如下:

aspect ObserverPattern {
    
//  管理subjects和observers的数据结构
    
//  TODO 
public :
    
//  角色接口
     struct  ISubject {};
    
struct  IObserver {
        
virtual   void  update(ISubject  * =   0 ;
    };
    
//  在派生方面中被重写
    pointcut  virtual  observers ()  =   0 ;
    pointcut 
virtual  subjects ()  =   0 ;
    
//  subjectChange()匹配所有非const方法,但限定了subjects类
    pointcut  virtual  subjectChange ()  =
        execution(
"  % ::%( "   &&   ! "  % ::%() const " )
        
&&  within(subjects ());
    
//  为每个subject/observer类增加基类,并插入通知代码
    advice observers () : baseclass(IObserver );
    advice subjects () : baseclass(ISubject );
    advice subjectChange () : after() {
        ISubject 
*  subject  =  tjp -> that ();
        updateObservers (subject );
    }
    
//  具体操作
     void  updateObservers (ISubject  *  sub ) {  }
    
void  addObserver (ISubject  *  sub , IObserver  *  ob ) {  }
    
void  remObserver (ISubject  *  sub , IObserver  *  ob ) {  }
};

其中“...” 部分是需要完成的C++实现代码,可以简单实现一个:
#ifndef __OBSERVER_PATTERN_AH__
#define  __OBSERVER_PATTERN_AH__

#include 
< map >
#include 
< set >
using   namespace  std;

aspect ObserverPattern {
    
//  管理subjects和observers的数据结构
     struct  ISubject;
    
struct  IObserver;
    map 
<  ISubject * set < IObserver *>   >  listeners;
public :
    
//  角色接口
     struct  ISubject {};
    
struct  IObserver {
        
virtual   void  update(ISubject  * =   0 ;
    };
    
//  在派生方面中被重写
    pointcut  virtual  observers ()  =   0 ;
    pointcut 
virtual  subjects ()  =   0 ;
    
//  subjectChange()匹配所有非const方法
    pointcut  virtual  subjectChange ()  =
        execution(
"  % ::%( "   &&   ! "  % ::%() const " )
        
&&  within(subjects ());
    
//  为每个subject/observer类增加基类,并插入通知代码
    advice observers () : baseclass(IObserver );
    advice subjects () : baseclass(ISubject );
    advice subjectChange () : after() {
        ISubject 
*  subject  =  tjp -> that ();
        updateObservers (subject );
    }
    
//  具体操作
     void  updateObservers (ISubject  *  sub ) {
        
const   set < IObserver *>&  observers  =  listeners[sub];
        
set < IObserver *> ::const_iterator iter  =  observers.begin();
        
for  (; iter  !=  observers.end(); iter  ++ )
        {
            (
* iter) -> update(sub);
        }
    }
    
void  addObserver (ISubject  *  sub , IObserver  *  ob ) { listeners[sub].insert(ob); }
    
void  removeObserver (ISubject  *  sub , IObserver  *  ob ) { listeners[sub].erase(ob); }
};

#endif   //  __OBSERVER_PATTERN_AH__

保存为ObserverPattern.ah供下面使用。

下面编写一个应用实例:

1、car.h

#ifndef __CAR_H__
#define  __CAR_H__

#include 
< string >
using   namespace  std;

class  Car
{
    
string  name;
    
int  x;
    
int  y;
    
enum  Direction{South, East, North, West};
    Direction direction;

    Car (
const  Car & );
    Car
&   operator   =  ( const  Car & );
public :
    Car (
const   string &  name);
    
void  turnLeft ();
    
void  turnRight ();
    
void  forward (size_t step);

    
const   string &  getName()  const ;
    
int  getX ()  const ;
    
int  getY ()  const ;
    Direction getDirection () 
const ;
};



#endif   //  __CAR_H__


2、car.cc

#include 
" car.h "
#include 
< cassert >

Car::Car (
const   string &  name_)
: name(name_), x(
0 ), y( 0 ), direction(South)
{
}

void  Car::turnLeft ()
{
    
if  (direction  ==  West)
        direction 
=  South;
    
else
        direction 
=  (Direction)( int (direction)  +   1 );
}

void  Car::turnRight ()
{
    
if  (direction  ==  South)
        direction 
=  West;
    
else
        direction 
=  (Direction)( int (direction)  -   1 );
}

void  Car::forward (size_t step)
{
    
switch  (direction)
    {
    
case  South:
        y 
+=  step;  break ;
    
case  East:
        x 
+=  step;  break ;
    
case  North:
        y 
-=  step;  break ;
    
case  West:
        x 
-=  step;  break ;
    
default :
        assert (
! " Invalid direction " );
    }
}

const   string &  Car::getName()  const
{
    
return  name;
}

int  Car::getX()  const
{
    
return  x;
}

int  Car::getY()  const
{
    
return  y;
}

Car::Direction Car::getDirection() 
const
{
    
return  direction;
}

3、dummy.h(这个用来测试Aspect C++的匹配模式会不会混乱)
#ifndef __DUMMY_H__
#define  __DUMMY_H__

class  Dummy
{
public :
    
void  test_non_const (){}
    
void  test_const ()  const  {}
};

#endif   //  __DUMMY_H__

4、main.cc
#include  " car.h "
#include 
" dummy.h "

int  main()
{
    Car car(
" No.1 " );
    car.turnLeft();
    car.forward(
3 );
    car.turnLeft();
    car.forward(
9 );
    car.turnRight();
    car.forward(
12 );

    Car car1(
" No.2 " );
    car1.forward(
7 );
    car1.turnLeft();
    car1.forward(
3 );

    car.forward(
5 );

    Dummy dummy;
    dummy.test_non_const();
    dummy.test_const();
    
    
return   0 ;
}

这个程序编译运行,没有任何输出。有时候为了监视对象的状态,可以在执行一个操作后加上一些打印状态的代码,当然这样比较繁琐;也可以在各个操作函数中加入这些代码,但修改已经写好的代码总是不太舒服。

下面先实现一个Car状态打印类:

5、car_logging.h
#ifndef __CAR_LOGGING__
#define  __CAR_LOGGING__

#include 
" car.h "
#include 
< iostream >
using   namespace  std;

class  CarLogging
{
public :
    
void  printCarInfo ( const  Car &  car)
    {
        
static   const   char *  direction_str[]  =  { " South " " East " " North " " West " };
        cout 
<<   " Car name:  "   <<  car.getName()
            
<<   " , direction:  "   <<  direction_str[ int (car.getDirection())] 
            
<<   " , x:  "   <<  car.getX() 
            
<<   " , y:  "   <<  car.getY() 
            
<<  endl;
    }
};

extern  CarLogging g_carLogging;

#endif   //  __CAR_LOGGING__

6、car_logging.cc
#include  " car_logging.h "

CarLogging g_carLogging;

7、CarLoggingObserver.ah
#ifndef __CAR_LOGGING_OBSERVER_AH__
#define  __CAR_LOGGING_OBSERVER_AH__

#include 
" ObserverPattern.ah "
#include 
" car.h "
#include 
" car_logging.h "

aspect CarLoggingObserver : 
public  ObserverPattern {
    
//  定义方面(ointcuts)
    pointcut subjects()  =   " Car " ;
    pointcut observers() 
=   " CarLogging " ;
public :
    advice observers() :
    
void  update( ObserverPattern::ISubject *  sub ) {
        printCarInfo (
* (Car * )sub);
    }

    advice construction (classes(subjects())) : after()
    {
        addObserver(tjp
-> that(),  & g_carLogging);
    }

    advice destruction (classes(subjects())) : before()
    {
        removeObserver(tjp
-> that(),  & g_carLogging);
    }
};

#endif   //  __CAR_LOGGING_OBSERVER_AH__

这个方面重写了subjects以及observers切面,并定义了observers在update被调用时执行的操作,另外还在Car的构造函数和析构函数中添加了注册和注销代码。

运行ac++生成代码,编译并运行,结果如下:

这里演示的例子依旧选择了不影响原始程序的做法,网上很多资料都把这个模式和实现代码结合起来,由于Aspect C++编译速度还是很慢,所以选择“外挂”的方式,这样不需要这些方面时,直接编译C++代码即可。

关于Aspect C++以及AOP,还有许多话题,不过不打算再继续了,AOP是个广泛的议题,局限在某一实现上只会使我们眼界变窄。

AOP被称为设计模式最佳实践者,它当之无愧。网上还有很多AOP实践设计模式的资料。

你可能感兴趣的:(设计模式,AOP,C++,c,C#)