第二種模型,也就是Martin Fowler指的rich domain object是下面這樣子的:
一個帶有業務邏輯的實體類,即domain object是Item
一個DAO接口ItemDao
一個DAO實現ItemDaoHibernateImpl
一個業務邏輯對象ItemManager
- public class Item implements Serializable {
-
- public Bid placeBid(User bidder, MonetaryAmount bidAmount,
- Bid currentMaxBid, Bid currentMinBid)
- throws BusinessException {
-
-
- if (currentMaxBid != null && currentMaxBid.getAmount().compareTo(bidAmount) > 0) {
- throw new BusinessException("Bid too low.");
- }
-
-
- if ( !state.equals(ItemState.ACTIVE) )
- throw new BusinessException("Auction is not active yet.");
-
-
- if ( this.getEndDate().before( new Date() ) )
- throw new BusinessException("Can't place new bid, auction already ended.");
-
-
- Bid newBid = new Bid(bidAmount, this, bidder);
-
-
- this.getBids.add(newBid);
-
- return newBid;
- }
- }
public class Item implements Serializable {
// 所有的屬性和getter/setter方法同上,省略
public Bid placeBid(User bidder, MonetaryAmount bidAmount,
Bid currentMaxBid, Bid currentMinBid)
throws BusinessException {
// Check highest bid (can also be a different Strategy (pattern))
if (currentMaxBid != null && currentMaxBid.getAmount().compareTo(bidAmount) > 0) {
throw new BusinessException("Bid too low.");
}
// Auction is active
if ( !state.equals(ItemState.ACTIVE) )
throw new BusinessException("Auction is not active yet.");
// Auction still valid
if ( this.getEndDate().before( new Date() ) )
throw new BusinessException("Can't place new bid, auction already ended.");
// Create new Bid
Bid newBid = new Bid(bidAmount, this, bidder);
// Place bid for this Item
this.getBids.add(newBid); // 請注意這一句,透明的進行了持久化,但是不能在這里調用ItemDao,Item不能對ItemDao產生依賴!
return newBid;
}
}
競標這個業務邏輯被放入到Item中來。請注意this.getBids.add(newBid); 如果沒有Hibernate或者JDO這種O/R Mapping的支持,我們是無法實現這種透明的持久化行為的。但是請注意,Item里面不能去調用ItemDAO,對ItemDAO產生依賴!
ItemDao和ItemDaoHibernateImpl的代碼同上,省略。
- public class ItemManager {
- private ItemDao itemDao;
- public void setItemDao(ItemDao itemDao) { this.itemDao = itemDao;}
- public Bid loadItemById(Long id) {
- itemDao.loadItemById(id);
- }
- public Collection listAllItems() {
- return itemDao.findAll();
- }
- public Bid placeBid(Item item, User bidder, MonetaryAmount bidAmount,
- Bid currentMaxBid, Bid currentMinBid) throws BusinessException {
- item.placeBid(bidder, bidAmount, currentMaxBid, currentMinBid);
- itemDao.update(item);
- }
- }
public class ItemManager {
private ItemDao itemDao;
public void setItemDao(ItemDao itemDao) { this.itemDao = itemDao;}
public Bid loadItemById(Long id) {
itemDao.loadItemById(id);
}
public Collection listAllItems() {
return itemDao.findAll();
}
public Bid placeBid(Item item, User bidder, MonetaryAmount bidAmount,
Bid currentMaxBid, Bid currentMinBid) throws BusinessException {
item.placeBid(bidder, bidAmount, currentMaxBid, currentMinBid);
itemDao.update(item); // 必須顯式的調用DAO,保持持久化
}
}
在第二種模型中,placeBid業務邏輯是放在Item中實現的,而loadItemById和findAll業務邏輯是放在ItemManager中實現的。不過值得注意的是,即使placeBid業務邏輯放在Item中,你仍然需要在ItemManager中簡單的封裝一層,以保證對placeBid業務邏輯進行事務的管理和持久化的觸發。
這種模型是Martin Fowler所指的真正的domain model。在這種模型中,有三個業務邏輯方法:placeBid,loadItemById和findAll,現在的問題是哪個邏輯應該放在Item中,哪個邏輯應該放在ItemManager中。在我們這個例子中,placeBid放在Item中(但是ItemManager也需要對它進行簡單的封裝),loadItemById和findAll是放在ItemManager中的。
切分的原則是什么呢? Rod Johnson提出原則是“case by case”,可重用度高的,和domain object狀態密切關聯的放在Item中,可重用度低的,和domain object狀態沒有密切關聯的放在ItemManager中。
我提出的原則是:看業務方法是否顯式的依賴持久化。
Item的placeBid這個業務邏輯方法沒有顯式的對持久化ItemDao接口產生依賴,所以要放在Item中。
請注意,如果脫離了Hibernate這個持久化框架,Item這個domain object是可以進行單元測試的,他不依賴于Hibernate的持久化機制。它是一個獨立的,可移植的,完整的,自包含的域對象。
而loadItemById和findAll這兩個業務邏輯方法是必須顯式的對持久化ItemDao接口產生依賴,否則這個業務邏輯就無法完成。如果你要把這兩個方法放在Item中,那么Item就無法脫離Hibernate框架,無法在Hibernate框架之外獨立存在。