同步自http://www.blogjava.net/AndersLin/archive/2006/06/15/53079.html
(下面是發在javaeye上的文章,因為覺的還有點意思,轉到blog來,關於Domain和AOSD已經有了一些新的想法)
應用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,成為不同用例切片的共同入口,相當於一個預留位置。這個時候就需要不同的用例實現人員協調好該入口。