void PstnUser::HookOn()
{
// do something corresponding to HookOn Event.
. . .
PstnSynAgent::AskForSynHookOn(GetV5IID(), GetL3Addr() );
}
void PstnUser::HookOff()
{
// do something corresponding to HookOff Event.
. . .
PstnSynAgent::AskForSynHookOff(GetV5IID(), GetL3Addr() );
}
可以看出,上面的代码片断中存在我们在前面所提到的问题,同步逻辑散布在PstnUser对象中那些需要同步状态的方法中。而PstnUser对象本身也违反了SRP,它不但要完成PstnUser本身的逻辑,还得去完成同步触发逻辑。下面我们将通过两种惯用的技术手法来达成AOP以消除上述味道,这两种方法是:Decorator模式以及Policy-Based Design。
1) 使用Decorator模式达成AOP
在使用Decorator模式时,其实是把同步逻辑作为PstnUser对象的一个额外职责,放在另外一个单独的Decorator对象中,从而消除上述味道。熟悉Decorator模式的读者肯定已经明白该如何做了,这里就不再赘述。更改后的代码如下:
// 定义一个Decorator模式所需要的接口
class IPstnUser
{
//. . .
public:
virtual void HookOn() = 0;
virtual void HookOff() = 0;
};
class PstnSynDecorator : public IPstnUser
{
public:
PstnSynDecorator(IPstnUser* user) : user_(user)
{ }
void HookOn()
{
user_->HookOn();
PstnSynAgent::AskForSynHookOn(user_->GetV5IID(),
user_->GetL3Addr());
}
void HookOff()
{
user_->HookOff();
PstnSynAgent::AskForSynHookOff(user_->GetV5IID(),
user_->GetL3Addr());
}
// . . .
private:
IPstnUser* user_;
};
class PstnUser : pubic IPstnUser
{
public:
void HookOn()
{
// do something corresponding to HookOn Event.
. . .
}
void HookOff()
{
// do something corresponding to HookOff Event.
. . .
}
// . . .
};
// 在创建对象时,需要这样进行
IPstnUser* pstnUser = new PstnUser(iid, addr) //创建普通PstnUser对象
IPstnUser* synPstnUser = new PstnSynDecorator(pstnUser) //为普通PstnUser对象增加同步功能。
这样,就达成了业务对象状态同步需求和PstnSynDecorator类直接对应,而Pstn用户端口的功能需求和PstnUser直接对应。
2) 使用Policy-Based Design达成AOP
在这种方法中,其实是把业务对象状态同步逻辑作为一个Policy,通过使用Policy-Based Design手法,把这个policy和业务对象逻辑本身组合起来,从而消除上述味道。Policy-Based Design技术在C++中是通过template和multiple-inheritance实现的。关于Policy-Based Design的详细介绍,请参见《Modern C++ Design》一书,这里也不再介绍。更改后的代码如下:
template <class SynPolicy, class UserPolicy>
class PstnUserWrapper : private SynPolicy, private UserPolicy
{
// . . .
public:
void HookOn()
{
UserPolicy::HookOn();
SynPolicy::AskForSynHookOn(GetV5IID(),GetL3Addr());
}
void HookOff()
{
UserPolicy::HookOff();
SynPolicy::AskForSynHookOff(GetV5IID(),GetL3Addr());
}
// . . .
};
class PstnUser
{
public:
void HookOn()
{
// do something corresponding to HookOn Event.
. . .
}
void HookOff()
{
// do something corresponding to HookOff Event.
. . .
}
// . . .
};
// 在创建对象时,需要这样进行
tyepdef PstnUserWrapper<PstnSynAgent, PstnUser > SynPstnUser;
SynPstnUser synPstnUser;
这种方法同样也解决了上面提到的问题,达成了需求元素和实现结构间的一一对应关系。相比起来,使用Decorator模式的方法具有更大的动态性,如果需要这种额外的动态性的话,可以选择使用Decorator模式的方法。
不过请注意,上面两种方法只是应用了AOP的思想,并不是完全意义上的AOP。在支持纯粹AOP的开发环境中,业务状态同步这种“横切”逻辑是由开发环境(编译器(扩展)或者run-time system等)根据相应的定义描述信息自动“织入”的,对象模型本身根本感觉不到这个“横切”逻辑的存在。在缺少AOP开发环境支持的情况下,我们只能通过手工的方法进行“织入”。
一点建议
如果在进行Use Case分析时能够正确运用AOP技术,我们就获取了另外一个纬度的自由。这个额外的自由度能为我们带来很多好处。首先,我们可以以更为干净、清晰的方式获取Use Case,使Use Case之间的耦合降到最低,并且还能最大程度地做到Use Case和实现结构间地一对一映射关系,这样开发团队就可以独立、并行地进行开发,大大提高开发生产力,并且有效地降低了开发小组间无谓的沟通、协调工作量。此外,这个自由度可以使我们在设计实现结构时更加模块化、更加富有表达力,从而为更加有效的迭代、重构提供了一个良好的基础。
但是,在我们进行开发时,一定要注意不要滥用AOP技术。和任何一种其他技术一样,AOP也是靠引入一定程度的代价来解决我们所面临的问题的。如果我们能够理解AOP所真正要解决的问题,在实际开发中正确的应用了这种技术,那么这种代价和所带来的好处相比是值得的。但是,如果不恰当地应用了AOP技术,那么非但不会带来任何好处,相反还会使得程序结构变得更加晦涩。希望上面的介绍能够为你在决定“是否该考虑使用AOP?”这个问题时提供一个依据。我相信如果你真得理解了上面对AOP的介绍,就不会在拿到了AOP这个“榔头”后,发现到处都是pointcut和advice之类的“钉子”了 :)
发表于 @ 2008年04月15日 16:23:00 | 评论( loading... ) | 举报| 收藏