锁定老帖子 主题:讨论AOSD:应用AOP实现业务逻辑
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2005-12-27
通常Use Case所描述的Business Flow分为四种:Basic Flow,Alternate Flow,Exception Flow和Extension Flow。 虽然Business Flow可能包含很多领域对象,由于每个use case的目标带有浓厚的领域逻辑,因而可以通过分析提炼出一个主domain对象。然后重组转换来自BA或者PM的BP设计文档,使其中的Basic Flow基于主domain对象,而把 Alternate Flow,Exception Flow和 Extension Flow基于其它的Domain Service和Domain Object(当然包括Util objects), 最后利用AOP把Alternate Flow, Exception Flow和Extension Flow 与Basic Flow在Service层组织起来。 使用AOP来组织Use Case时,与使用AOP组织技术问题(比如日志,权限检查和事务处理等)不同。 在AOP组织技术问题时,我们不关心join point的目标对象和目标方法以及入口参数。比如: public class BankServiceImpl implements BankService{ public void transfer(UserAccount src, UserAccount dist, BigDecimal amount);throws Exception{ src.subtract(amount);; dist.add(amount);; } //Other code goes here... } <bean id=" BankService" class="org. springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionAttributes"> <props> <prop key="transfer">PROPAGATION_REQUIRED</prop> </props> </property> </bean> 我们不关心参数,或者在一些方法重载的地方利用参数来识别区分我们的方法入口。 但当我们利用AOP来组织Use Case时我们关心目标对象和目标方法以及入口参数。因为AOP所要织入的方法是另一个Use Case是另一个Biz Flow。(这个在AOSD中显示讨论的不多,只有在12章12.4.中有提到) 比如我们要在转帐成功后发手机短信通知客户。那么在没有用AOP代码中我们这样写: public class BankServiceImpl implements BankService{ public void transfer(UserAccount src, UserAccount dist, BigDecimal change);throws Exception{ src.subtract(change);; dist.add(change);; SMSService.sendSMS(src, change);; SMSService.sendSMS(dist, change);; } //Other code goes here... } Public class SMSService { public static void sendSMS(UserAccount user, BigDecimal change);{ Long phone = user.getPhoneNumber();; BigDecimal balance = user.getBalance();; send(phone, change, belance); } private static void send(Long phone, change, balance);{ ... } } 事实用User Case的观点分析,发送短信通知是另一个use case,是转帐这个use case的extend flow。用AOP的方法应该如下: public class BankServiceEx { public static void notify(UserAccount src, BigDecimal change); { SMSService.sendSMS(src, change);; } //Other code goes here... } public aspect BankServiceAspect { pointcut transfer();:call(void BankService.transfer(..););; after(UserAccount src, UserAccount dist, BigDecimal change); returning : transfer(); && args(src, dist, change);{ BankServiceEx.notify(src, change);; BankServiceEx.notify(dist, change);; } } 这样我们完成了两个用例的分离,两个用例独立,可以重用和测试。比如上述短信通知用例其实可以被重用到其它情况如:存款,消费,以及银行分红等等。 不过可能面临一个情况是,两个独立用例的代码部分都可能用到某个对象, 那么在两个用例中可能重复一部分代码。虽然从概念上看,不应该重复(在使用用旧的方法实现时不会重复),但从不同use case看,这个重复是值得的。曾经考虑利用代码生成,直接获得Local Variable,这样可以减少重复,但是这个想法是错误的,不仅仅是实现上的困难,更重要在于,分离出的发送短信用例便绑定了转帐用例,依赖于转帐用例,而无法独立重用和测试。 这样,对象、方法以及方法参数构成了一个完整的pointcut,成为不同用例切片的共同入口,相当于一个占位符。这个时候就需要不同的用例实现人员协调好该入口。 以上是小弟一些看法,欢迎拍砖! 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2005-12-27
确实,用AOP来实现用例的扩展,是可行的方法。
不过要这样细力度的写Aspect,就太辛苦了。转帐要通知,定期转存要不要通知呢?实际上使用单一的Aspect可以为整个DomainModel提供Event服务。这样这种通知性质的服务。如:SMS,监控等扩展服务,都可以独立的得到处理。 |
|
返回顶楼 | |
发表时间:2005-12-27
拍砖的来了,这种AOP和普通的Java代码相比有啥优点?
public class BankServiceDelegator implements BankService { private BankService bankService; private NotifyService notifyService; public void transfer(UserAccount src, UserAccount dist, BigDecimal amount);{ bankService.transfer(src, dist, amount);; notifyService.notify(dist, amount);; } } |
|
返回顶楼 | |
发表时间:2005-12-27
Readonly 写道 拍砖的来了,这种AOP和普通的Java代码相比有啥优点?
public class BankServiceDelegator implements BankService { private BankService bankService; private NotifyService notifyService; public void transfer(UserAccount src, UserAccount dist, BigDecimal amount);{ bankService.transfer(src, dist, amount);; notifyService.notify(dist, amount);; } } 如果Aspect的pointcut同join point是一对一的关系,那没啥好比较的;但如果Aspect的pointcut针对多个join points那么优势就浮现出来了。 |
|
返回顶楼 | |
发表时间:2005-12-27
|
|
返回顶楼 | |
发表时间:2005-12-27
Readonly 写道 拍砖的来了,这种AOP和普通的Java代码相比有啥优点?
public class BankServiceDelegator implements BankService { private BankService bankService; private NotifyService notifyService; public void transfer(UserAccount src, UserAccount dist, BigDecimal amount);{ bankService.transfer(src, dist, amount);; notifyService.notify(dist, amount);; } } 是这样的,比如浦东发展银行有个服务叫短信灵通服务,在余额变化情况下通知客户。那么当客户在浦发买了某个基金分红存入该客户帐户时,以及刷卡消费时也需要SMS通知对吧。 于是你的代码就给这样写: public class BankServiceDelegator implements BankService { private BankService bankService; private NotifyService notifyService; public void transfer(UserAccount src, UserAccount dist, BigDecimal amount);{ bankService.transfer(src, dist, amount);; notifyService.notify(dist, amount);; } public void bonus(UserAccount src, BigDecimal amount);{ bankService.bonus(src, amount);; notifyService.notify(src, amount);; } public void pay(UserAccount src, UserAccount dist, BigDecimal amount);{ bankService.transfer(src, amount);; notifyService.notify(src, amount);; notifyService.notify(dist, amount);; } } 同时,刷卡消费又要算积分于是你的代码改成如下: public void pay(UserAccount src, UserAccount dist, BigDecimal amount);{ bankService.transfer(src, dist, amount);; bankService.积分(src, amount);; notifyService.notify(src, amount);; notifyService.notify(dist, amount);; } 当然你可那会说你会在bankService中提供这样一个方法写出这样: public void pay(UserAccount src, UserAccount dist, BigDecimal amount);{ this.transfer(src, dist, amount);; this.积分(src, amount);; } 然后 public class BankServiceDelegator implements BankService { private BankService bankService; private NotifyService notifyService; public void pay(UserAccount src, UserAccount dist, BigDecimal amount);{ bankService.pay(src, amount);; notifyService.notify(src, amount);; notifyService.notify(dist, amount);; } } 没有关系,总之你一定要显示调用对吧,但是如果你使用AOP来做,那么你不需要这么显示的写。 public class BankServiceDelegator implements BankService { private BankService bankService; private NotifyService notifyService; public void transfer(UserAccount src, UserAccount dist, BigDecimal amount);{ bankService.transfer(src, dist, amount);; } public void bonus(UserAccount src, BigDecimal amount);{ bankService.bonus(src, amount);; } public void pay(UserAccount src, UserAccount dist, BigDecimal amount);{ bankService.transfer(src, amount);; } } public aspect BankServiceAspect { pointcut transfer();:call(void BankService.transfer(..);); || call(void BankService.pay(..);; pointcut pay();:call(void BankService.pay(..););; pointcut bonus();:call(void BankService.bonus(..););; after(UserAccount src, UserAccount dist, BigDecimal change); returning : transfer(); && args(src, dist, change);{ BankServiceEx.notify(src, change);; BankServiceEx.notify(dist, change);; } after(UserAccount src, BigDecimal change); returning : bonus(); && args(src, change);{ BankServiceEx.notify(src, change);; } after(UserAccount src, UserAccount dist, BigDecimal change); returning : pay(); && args(src, change);{ BankServiceEx.积分(src, change);; } } 以上示例代码,呵呵! 这样,积分和通知,与转帐,分红和消费各自独立。比直接显示写简捷吧 |
|
返回顶楼 | |
发表时间:2005-12-27
wolfsquare 写道 基于事件分发机制的企业应用开发
http://www.blogjava.net/wolfsquare/archive/2005/12/05/22630.html 基于拦截器的企业应用构造http://www.blogjava.net/wolfsquare/archive/2005/12/06/22772.html wolfsquare问你一个小问题,如果发送的事件里需要saveOrder处理完成后才能产生的数据,并且不能通过Order拿到,如:帐户余额的变化,你如何处理? |
|
返回顶楼 | |
发表时间:2005-12-27
IMO
Aspect-oriented design 确实能比较好的体现商业逻辑中的Basic flow, alternate flow extension flow, exception flow.... 但Case归Case,实际映射到编码上还是有点问题。 比如上面这个BankService发送SMS的例子。 如果发送SMS成功,需要跳转到一个SMS汇总页面,也就是改变了主事件的流程,那么除了要新增 pointcut (完成SMS发送 )外,可能还需要在原来主流程的代码中,获取 pointcut 返回的消息,提示用户。除非在设计前考虑到这里的流程,否则,后期织入麻烦可能会大一点。 |
|
返回顶楼 | |
发表时间:2005-12-27
和buaawhl的讨论:
buaawhl: 这种就是看 pointcut的面积大小了。pointcut 面积越大,aspect越少,aop 越有意义。你给出的例子,只是强调了aspect的叠加 anders: 恩,你说的对,我隐约中还没有想到这个呢!所以请教各位老大了 buaawhl: 最终结果要比较代码量,你给出的aop 例子,代码量还要超OO anders: 就这个例子而言是这样的。如你所说,pointput最大,aop的能力越明显 buaawhl: yes. |
|
返回顶楼 | |
发表时间:2005-12-27
JJYAO 写道 IMO
Aspect-oriented design 确实能比较好的体现商业逻辑中的Basic flow, alternate flow extension flow, exception flow.... 但Case归Case,实际映射到编码上还是有点问题。 比如上面这个BankService发送SMS的例子。 如果发送SMS成功,需要跳转到一个SMS汇总页面,也就是改变了主事件的流程,那么除了要新增 pointcut (完成SMS发送 )外,可能还需要在原来主流程的代码中,获取 pointcut 返回的消息,提示用户。除非在设计前考虑到这里的流程,否则,后期织入麻烦可能会大一点。 SMS只是个ext flow,未必需要显示。但如果SMS发送完需要给客户一个提示。直接在界面中显示就好了!因为即便是用OO方法你也是如此。 比如: public class TransferAction extends action{ public ActionForward execute(...);{ if(service.transfer(...););{ return mapping.forward("sucess");; } } 与我用AOP做一样的。 |
|
返回顶楼 | |