论坛首页 Java企业应用论坛

讨论AOSD:应用AOP实现业务逻辑

浏览 31285 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2005-12-27  
应用Domain开发的系统,通常把逻辑放在Domain Service层中,而Domain Service做两个工作:1. 和表现层通信,表现为把表现层的平面数据(VO)转换为相关联的Domain对象,把Domain对象计算的结果转换成平面数据(VO)返回给表现层;2.根据Use Case完成商业逻辑的调度。以下主要讨论Use Case的内容。
    通常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,成为不同用例切片的共同入口,相当于一个占位符。这个时候就需要不同的用例实现人员协调好该入口。

以上是小弟一些看法,欢迎拍砖!
   发表时间:2005-12-27  
确实,用AOP来实现用例的扩展,是可行的方法。
不过要这样细力度的写Aspect,就太辛苦了。转帐要通知,定期转存要不要通知呢?实际上使用单一的Aspect可以为整个DomainModel提供Event服务。这样这种通知性质的服务。如:SMS,监控等扩展服务,都可以独立的得到处理。
0 请登录后投票
   发表时间: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);;
  }
}
0 请登录后投票
   发表时间: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那么优势就浮现出来了。
0 请登录后投票
   发表时间:2005-12-27  
基于事件分发机制的企业应用开发
http://www.blogjava.net/wolfsquare/archive/2005/12/05/22630.html
基于拦截器的企业应用构造http://www.blogjava.net/wolfsquare/archive/2005/12/06/22772.html
0 请登录后投票
   发表时间: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);;
	}
}

以上示例代码,呵呵!
这样,积分和通知,与转帐,分红和消费各自独立。比直接显示写简捷吧
0 请登录后投票
   发表时间:2005-12-27  
wolfsquare 写道


wolfsquare问你一个小问题,如果发送的事件里需要saveOrder处理完成后才能产生的数据,并且不能通过Order拿到,如:帐户余额的变化,你如何处理?
0 请登录后投票
   发表时间:2005-12-27  
IMO
Aspect-oriented design 确实能比较好的体现商业逻辑中的Basic flow, alternate flow extension flow, exception flow.... 但Case归Case,实际映射到编码上还是有点问题。

比如上面这个BankService发送SMS的例子。 如果发送SMS成功,需要跳转到一个SMS汇总页面,也就是改变了主事件的流程,那么除了要新增 pointcut (完成SMS发送 )外,可能还需要在原来主流程的代码中,获取 pointcut 返回的消息,提示用户。除非在设计前考虑到这里的流程,否则,后期织入麻烦可能会大一点。
0 请登录后投票
   发表时间:2005-12-27  
和buaawhl的讨论:
buaawhl: 这种就是看 pointcut的面积大小了。pointcut 面积越大,aspect越少,aop 越有意义。你给出的例子,只是强调了aspect的叠加

anders: 恩,你说的对,我隐约中还没有想到这个呢!所以请教各位老大了

buaawhl: 最终结果要比较代码量,你给出的aop 例子,代码量还要超OO

anders: 就这个例子而言是这样的。如你所说,pointput最大,aop的能力越明显

buaawhl: yes.
0 请登录后投票
   发表时间: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做一样的。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics