[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实践设计模式的资料。



分享给朋友:
您可能感兴趣的文章:
随机阅读: