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