<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆-14  評(píng)論-25  文章-1  trackbacks-0
    總結(jié)一下最近關(guān)于domain object以及相關(guān)的討論
    ?
    在最近的圍繞domain object的討論中浮現(xiàn)出來(lái)了三種模型,(還有一些其他的旁枝,不一一分析了),經(jīng)過(guò)一番討論,各種問(wèn)題逐漸清晰起來(lái),在這里我試圖做一個(gè)總結(jié),便于大家了解和掌握。

    第一種模型:只有g(shù)etter/setter方法的純數(shù)據(jù)類,所有的業(yè)務(wù)邏輯完全由business object來(lái)完成(又稱TransactionScript),這種模型下的domain object被Martin Fowler稱之為“貧血的domain object”。下面用舉一個(gè)具體的代碼來(lái)說(shuō)明,代碼來(lái)自Hibernate的caveatemptor,但經(jīng)過(guò)我的改寫:

    一個(gè)實(shí)體類叫做Item,指的是一個(gè)拍賣項(xiàng)目
    一個(gè)DAO接口類叫做ItemDao
    一個(gè)DAO接口實(shí)現(xiàn)類叫做ItemDaoHibernateImpl
    一個(gè)業(yè)務(wù)邏輯類叫做ItemManager(或者叫做ItemService)

    java代碼:?

    public class Item implementsSerializable{
    ? ? privateLong id = null;
    ? ? privateint version;
    ? ? privateString name;
    ? ? private User seller;
    ? ? privateString description;
    ? ? private MonetaryAmount initialPrice;
    ? ? private MonetaryAmount reservePrice;
    ? ? privateDate startDate;
    ? ? privateDate endDate;
    ? ? privateSet categorizedItems = newHashSet();
    ? ? privateCollection bids = newArrayList();
    ? ? private Bid successfulBid;
    ? ? private ItemState state;
    ? ? private User approvedBy;
    ? ? privateDate approvalDatetime;
    ? ? privateDate created = newDate();
    ? ? //? getter/setter方法省略不寫,避免篇幅太長(zhǎng)
    }



    java代碼:?

    public interface ItemDao {
    ? ? public Item getItemById(Long id);
    ? ? publicCollection findAll();
    ? ? publicvoid updateItem(Item item);
    }



    ItemDao定義持久化操作的接口,用于隔離持久化代碼。

    java代碼:?

    public class ItemDaoHibernateImpl implements ItemDao extends HibernateDaoSupport {
    ? ? public Item getItemById(Long id){
    ? ? ? ? return(Item) getHibernateTemplate().load(Item.class, id);
    ? ? }
    ? ? publicCollection findAll(){
    ? ? ? ? return(List) getHibernateTemplate().find("from Item");
    ? ? }
    ? ? publicvoid updateItem(Item item){
    ? ? ? ? getHibernateTemplate().update(item);
    ? ? }
    }


    ItemDaoHibernateImpl完成具體的持久化工作,請(qǐng)注意,數(shù)據(jù)庫(kù)資源的獲取和釋放是在ItemDaoHibernateImpl 里面處理的,每個(gè)DAO方法調(diào)用之前打開(kāi)Session,DAO方法調(diào)用之后,關(guān)閉Session。(Session放在ThreadLocal中,保證一次調(diào)用只打開(kāi)關(guān)閉一次)

    java代碼:?

    public class ItemManager {
    ? ? private ItemDao itemDao;
    ? ? publicvoid setItemDao(ItemDao itemDao){ this.itemDao = itemDao;}
    ? ? public Bid loadItemById(Long id){
    ? ? ? ? itemDao.loadItemById(id);
    ? ? }
    ? ? publicCollection listAllItems(){
    ? ? ? ? return? itemDao.findAll();
    ? ? }
    ? ? public Bid placeBid(Item item, User bidder, MonetaryAmount bidAmount,
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? Bid currentMaxBid, Bid currentMinBid)throws BusinessException {
    ? ? ? ? ? ? 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( item.getEndDate().before(newDate()))
    ? ? ? ? ? ? throw new BusinessException("Can't place new bid, auction already ended.");
    ? ?
    ? ? // Create new Bid
    ? ? Bid newBid = new Bid(bidAmount, item, bidder);
    ? ?
    ? ? // Place bid for this Item
    ? ? item.getBids().add(newBid);
    ? ? itemDao.update(item);? ? ?//? 調(diào)用DAO完成持久化操作
    ? ? return newBid;
    ? ? }
    }



    事務(wù)的管理是在ItemManger這一層完成的,ItemManager實(shí)現(xiàn)具體的業(yè)務(wù)邏輯。除了常見(jiàn)的和CRUD有關(guān)的簡(jiǎn)單邏輯之外,這里還有一個(gè)placeBid的邏輯,即項(xiàng)目的競(jìng)標(biāo)。

    以上是一個(gè)完整的第一種模型的示例代碼。在這個(gè)示例中,placeBid,loadItemById,findAll等等業(yè)務(wù)邏輯統(tǒng)統(tǒng)放在ItemManager中實(shí)現(xiàn),而Item只有g(shù)etter/setter方法。

    ?

    ?

    第二種模型,也就是Martin Fowler指的rich domain object是下面這樣子的:

    一個(gè)帶有業(yè)務(wù)邏輯的實(shí)體類,即domain object是Item
    一個(gè)DAO接口ItemDao
    一個(gè)DAO實(shí)現(xiàn)ItemDaoHibernateImpl
    一個(gè)業(yè)務(wù)邏輯對(duì)象ItemManager

    java代碼:?

    public class Item implementsSerializable{
    ? ? //? 所有的屬性和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(newDate()))
    ? ? ? ? ? ? ? ? ? ? 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);? // 請(qǐng)注意這一句,透明的進(jìn)行了持久化,但是不能在這里調(diào)用ItemDao,Item不能對(duì)ItemDao產(chǎn)生依賴!
    ? ?
    ? ? ? ? ? ? return newBid;
    ? ? }
    }



    競(jìng)標(biāo)這個(gè)業(yè)務(wù)邏輯被放入到Item中來(lái)。請(qǐng)注意this.getBids.add(newBid); 如果沒(méi)有Hibernate或者JDO這種O/R Mapping的支持,我們是無(wú)法實(shí)現(xiàn)這種透明的持久化行為的。但是請(qǐng)注意,Item里面不能去調(diào)用ItemDAO,對(duì)ItemDAO產(chǎn)生依賴!

    ItemDao和ItemDaoHibernateImpl的代碼同上,省略。

    java代碼:?

    public class ItemManager {
    ? ? private ItemDao itemDao;
    ? ? publicvoid setItemDao(ItemDao itemDao){ this.itemDao = itemDao;}
    ? ? public Bid loadItemById(Long id){
    ? ? ? ? itemDao.loadItemById(id);
    ? ? }
    ? ? publicCollection 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);? ? // 必須顯式的調(diào)用DAO,保持持久化
    ? ? }
    }



    在第二種模型中,placeBid業(yè)務(wù)邏輯是放在Item中實(shí)現(xiàn)的,而loadItemById和findAll業(yè)務(wù)邏輯是放在 ItemManager中實(shí)現(xiàn)的。不過(guò)值得注意的是,即使placeBid業(yè)務(wù)邏輯放在Item中,你仍然需要在ItemManager中簡(jiǎn)單的封裝一層,以保證對(duì)placeBid業(yè)務(wù)邏輯進(jìn)行事務(wù)的管理和持久化的觸發(fā)。

    這種模型是Martin Fowler所指的真正的domain model。在這種模型中,有三個(gè)業(yè)務(wù)邏輯方法:placeBid,loadItemById和findAll,現(xiàn)在的問(wèn)題是哪個(gè)邏輯應(yīng)該放在Item 中,哪個(gè)邏輯應(yīng)該放在ItemManager中。在我們這個(gè)例子中,placeBid放在Item中(但是ItemManager也需要對(duì)它進(jìn)行簡(jiǎn)單的封裝),loadItemById和findAll是放在ItemManager中的。

    切分的原則是什么呢? Rod Johnson提出原則是“case by case”,可重用度高的,和domain object狀態(tài)密切關(guān)聯(lián)的放在Item中,可重用度低的,和domain object狀態(tài)沒(méi)有密切關(guān)聯(lián)的放在ItemManager中。

    我提出的原則是:看業(yè)務(wù)方法是否顯式的依賴持久化。

    Item的placeBid這個(gè)業(yè)務(wù)邏輯方法沒(méi)有顯式的對(duì)持久化ItemDao接口產(chǎn)生依賴,所以要放在Item中。請(qǐng)注意,如果脫離了Hibernate這個(gè)持久化框架,Item這個(gè)domain object是可以進(jìn)行單元測(cè)試的,他不依賴于Hibernate的持久化機(jī)制。它是一個(gè)獨(dú)立的,可移植的,完整的,自包含的域?qū)ο?/span>。

    而loadItemById和findAll這兩個(gè)業(yè)務(wù)邏輯方法是必須顯式的對(duì)持久化ItemDao接口產(chǎn)生依賴,否則這個(gè)業(yè)務(wù)邏輯就無(wú)法完成。如果你要把這兩個(gè)方法放在Item中,那么Item就無(wú)法脫離Hibernate框架,無(wú)法在Hibernate框架之外獨(dú)立存在。

    ?

    ?

    第三種模型印象中好像是firebody或者是Archie提出的(也有可能不是,記不清楚了),簡(jiǎn)單的來(lái)說(shuō),這種模型就是把第二種模型的domain object和business object合二為一了。所以ItemManager就不需要了,在這種模型下面,只有三個(gè)類,他們分別是:

    Item:包含了實(shí)體類信息,也包含了所有的業(yè)務(wù)邏輯
    ItemDao:持久化DAO接口類
    ItemDaoHibernateImpl:DAO接口的實(shí)現(xiàn)類

    由于ItemDao和ItemDaoHibernateImpl和上面完全相同,就省略了。

    java代碼:?

    public class Item implementsSerializable{
    ? ? //? 所有的屬性和getter/setter方法都省略
    ? ?privatestatic ItemDao itemDao;
    ? ? publicvoid setItemDao(ItemDao itemDao){this.itemDao = itemDao;}
    ? ?
    ? ? publicstatic Item loadItemById(Long id){
    ? ? ? ? return(Item) itemDao.loadItemById(id);
    ? ? }
    ? ? publicstaticCollection findAll(){
    ? ? ? ? return(List) itemDao.findAll();
    ? ? }

    ? ? 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(newDate()))
    ? ? ? ? ? ? ? ? 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.addBid(newBid);
    ? ? ? ? itemDao.update(this);? ? ? //? 調(diào)用DAO進(jìn)行顯式持久化
    ? ? ? ? return newBid;
    ? ? }
    }



    在這種模型中,所有的業(yè)務(wù)邏輯全部都在Item中,事務(wù)管理也在Item中實(shí)現(xiàn)。

    ?

    ?

    在上面三種模型之外,還有很多這三種模型的變種,例如partech的模型就是把第二種模型中的DAO和 Manager三個(gè)類合并為一個(gè)類后形成的模型;例如frain....(id很長(zhǎng)記不住)的模型就是把第三種模型的三個(gè)類完全合并為一個(gè)單類后形成的模型;例如Archie是把第三種模型的Item又分出來(lái)一些純數(shù)據(jù)類(可能是,不確定)形成的一個(gè)模型。

    但是不管怎么變,基本模型歸納起來(lái)就是上面的三種模型,下面分別簡(jiǎn)單評(píng)價(jià)一下:

    第一種模型絕大多數(shù)人都反對(duì),因此反對(duì)理由我也不多講了。但遺憾的是,我觀察到的實(shí)際情形是,很多使用Hibernate的公司最后都是這種模型,這里面有很大的原因是很多公司的技術(shù)水平?jīng)]有達(dá)到這種層次,所以導(dǎo)致了這種貧血模型的出現(xiàn)。從這一點(diǎn)來(lái)說(shuō),Martin Fowler的批評(píng)聲音不是太響了,而是太弱了,還需要再繼續(xù)吶喊。

    第二種模型就是Martin Fowler一直主張的模型,實(shí)際上也是我一直在實(shí)際項(xiàng)目中采用這種模型。我沒(méi)有看過(guò)Martin的POEAA,之所以能夠自己摸索到這種模型,也是因?yàn)閺?2年我已經(jīng)開(kāi)始思考這個(gè)問(wèn)題并且尋求解決方案了,但是當(dāng)時(shí)沒(méi)有看到Hibernate,那時(shí)候做的一個(gè)小型項(xiàng)目我已經(jīng)按照這種模型來(lái)做了,但是由于沒(méi)有O/R Mapping的支持,寫到后來(lái)又不得不全部改成貧血的domain object,項(xiàng)目做完以后再繼續(xù)找,隨后就發(fā)現(xiàn)了Hibernate。當(dāng)然,現(xiàn)在很多人一開(kāi)始就是用Hibernate做項(xiàng)目,沒(méi)有經(jīng)歷過(guò)我經(jīng)歷的那個(gè)階段。

    不過(guò)我覺(jué)得這種模型仍然不夠完美,因?yàn)槟氵€是需要一個(gè)業(yè)務(wù)邏輯層來(lái)封裝所有的domain logic,這顯得非常羅嗦,并且業(yè)務(wù)邏輯對(duì)象的接口也不夠穩(wěn)定。如果不考慮業(yè)務(wù)邏輯對(duì)象的重用性的話(業(yè)務(wù)邏輯對(duì)象的可重用性也不可能好),很多人干脆就去掉了xxxManager這一層,在Web層的Action代碼直接調(diào)用xxxDao,同時(shí)容器事務(wù)管理配置到Action這一層上來(lái)。 Hibernate的caveatemptor就是這樣架構(gòu)的一個(gè)典型應(yīng)用。

    第三種模型是我很反對(duì)的一種模型,這種模型下面,Domain Object和DAO形成了雙向依賴關(guān)系,無(wú)法脫離框架測(cè)試,并且業(yè)務(wù)邏輯層的服務(wù)也和持久層對(duì)象的狀態(tài)耦合到了一起,會(huì)造成程序的高度的復(fù)雜性,很差的靈活性和糟糕的可維護(hù)性。也許將來(lái)技術(shù)進(jìn)步導(dǎo)致的O/R Mapping管理下的domain object發(fā)展到足夠的動(dòng)態(tài)持久透明化的話,這種模型才會(huì)成為一個(gè)理想的選擇。就像O/R Mapping的流行使得第二種模型成為了可能(O/R Mapping流行以前,我們只能用第一種模型,第二種模型那時(shí)候是不現(xiàn)實(shí)的)。

    ?

    ?

    既然大家都統(tǒng)一了觀點(diǎn),那么就有了一個(gè)很好的討論問(wèn)題的基礎(chǔ)了。Martin Fowler的Domain Model,或者說(shuō)我們的第二種模型難道是完美無(wú)缺的嗎?當(dāng)然不是,接下來(lái)我就要分析一下它的不足,以及可能的解決辦法,而這些都來(lái)源于我個(gè)人的實(shí)踐探索。

    在第二種模型中,我們可以清楚的把這4個(gè)類分為三層:

    1、實(shí)體類層,即Item,帶有domain logic的domain object
    2、DAO層,即ItemDao和ItemDaoHibernateImpl,抽象持久化操作的接口和實(shí)現(xiàn)類
    3、業(yè)務(wù)邏輯層,即ItemManager,接受容器事務(wù)控制,向Web層提供統(tǒng)一的服務(wù)調(diào)用

    在這三層中我們大家可以看到,domain object和DAO都是非常穩(wěn)定的層,其實(shí)原因也很簡(jiǎn)單,因?yàn)閐omain object是映射數(shù)據(jù)庫(kù)字段的,數(shù)據(jù)庫(kù)字段不會(huì)頻繁變動(dòng),所以domain object也相對(duì)穩(wěn)定,而面向數(shù)據(jù)庫(kù)持久化編程的DAO層也不過(guò)就是CRUD而已,不會(huì)有更多的花樣,所以也很穩(wěn)定。

    問(wèn)題就在于這個(gè)充當(dāng)business workflow facade的業(yè)務(wù)邏輯對(duì)象,它的變動(dòng)是相當(dāng)頻繁的。業(yè)務(wù)邏輯對(duì)象通常都是無(wú)狀態(tài)的、受事務(wù)控制的、Singleton類,我們可以考察一下業(yè)務(wù)邏輯對(duì)象都有哪幾類業(yè)務(wù)邏輯方法:

    第一類:DAO接口方法的代理,就是上面例子中的loadItemById方法和findAll方法。

    ItemManager之所以要代理這種類,目的有兩個(gè):向Web層提供統(tǒng)一的服務(wù)調(diào)用入口點(diǎn)和給持久化方法增加事務(wù)控制功能。這兩點(diǎn)都很容易理解,你不能既給Web層程序員提供xxxManager,也給他提供xxxDao,所以你需要用xxxManager封裝xxxDao,在這里,充當(dāng)了一個(gè)簡(jiǎn)單代理功能;而事務(wù)控制也是持久化方法必須的,事務(wù)可能需要跨越多個(gè)DAO方法調(diào)用,所以必須放在業(yè)務(wù)邏輯層,而不能放在DAO層。

    但是必須看到,對(duì)于一個(gè)典型的web應(yīng)用來(lái)說(shuō),絕大多數(shù)的業(yè)務(wù)邏輯都是簡(jiǎn)單的CRUD邏輯,所以這種情況下,針對(duì)每個(gè)DAO方法,xxxManager都需要提供一個(gè)對(duì)應(yīng)的封裝方法,這不但是非常枯燥的,也是令人感覺(jué)非常不好的。


    第二類:domain logic的方法代理。就是上面例子中placeBid方法。雖然Item已經(jīng)有了placeBid方法,但是ItemManager仍然需要封裝一下Item的placeBid,然后再提供一個(gè)簡(jiǎn)單封裝之后的代理方法。

    這和第一種情況類似,其原因也一樣,也是為了給Web層提供一個(gè)統(tǒng)一的服務(wù)調(diào)用入口點(diǎn)和給隱式的持久化動(dòng)作提供事務(wù)控制。

    同樣,和第一種情況一樣,針對(duì)每個(gè)domain logic方法,xxxManager都需要提供一個(gè)對(duì)應(yīng)的封裝方法,同樣是枯燥的,令人不爽的。


    第三類:需要多個(gè)domain object和DAO參與協(xié)作的business workflow。這種情況是業(yè)務(wù)邏輯對(duì)象真正應(yīng)該完成的職責(zé)。

    在這個(gè)簡(jiǎn)單的例子中,沒(méi)有涉及到這種情況,不過(guò)大家都可以想像的出來(lái)這種應(yīng)用場(chǎng)景,因此不必舉例說(shuō)明了。

    通過(guò)上面的分析可以看出,只有第三類業(yè)務(wù)邏輯方法才是業(yè)務(wù)邏輯對(duì)象真正應(yīng)該承擔(dān)的職責(zé),而前兩類業(yè)務(wù)邏輯方法都是“無(wú)奈之舉”,不得不為之的事情,不但枯燥,而且令人沮喪。




    分析完了業(yè)務(wù)邏輯對(duì)象,我們?cè)倩仡^看一下domain object,我們要仔細(xì)考察一下domain logic的話,會(huì)發(fā)現(xiàn)domain logic也分為兩類:

    第一類:需要持久層框架隱式的實(shí)現(xiàn)透明持久化的domain logic,例如Item的placeBid方法中的這一句:

    java代碼:?

    this.getBids().add(newBid);


    上面已經(jīng)著重提到,雖然這僅僅只是一個(gè)Java集合的添加新元素的操作,但是實(shí)際上通過(guò)事務(wù)的控制,會(huì)潛在的觸發(fā)兩條SQL:一條是insert一條記錄到bid表,一條是更新item表相應(yīng)的記錄。如果我們讓Item脫離Hibernate進(jìn)行單元測(cè)試,它就是一個(gè)單純的Java集合操作,如果我們把他加入到Hibernate框架中,他就會(huì)潛在的觸發(fā)兩條SQL,這就是隱式的依賴于持久化的domain logic
    特別請(qǐng)注意的一點(diǎn)是:在沒(méi)有Hibernate/JDO這類可以實(shí)現(xiàn)“透明的持久化”工具出現(xiàn)之前,這類domain logic是無(wú)法實(shí)現(xiàn)的。

    對(duì)于這一類domain logic,業(yè)務(wù)邏輯對(duì)象必須提供相應(yīng)的封裝方法,以實(shí)現(xiàn)事務(wù)控制。


    第二類:完全不依賴持久化的domain logic,例如readonly例子中的Topic,如下:

    java代碼:?

    class Topic {
    ? ? boolean isAllowReply(){
    ? ? ? ? Calendar dueDate = Calendar.getInstance();
    ? ? ? ? dueDate.setTime(lastUpdatedTime);
    ? ? ? ? dueDate.add(Calendar.DATE, forum.timeToLive);
    ? ?
    ? ? ? ? Date now = newDate();
    ? ? ? ? return now.after(dueDate.getTime());
    ? ? }
    }



    注意這個(gè)isAllowReply方法,他和持久化完全不發(fā)生一丁點(diǎn)關(guān)系。在實(shí)際的開(kāi)發(fā)中,我們同樣會(huì)遇到很多這種不需要持久化的業(yè)務(wù)邏輯(主要發(fā)生在日期運(yùn)算、數(shù)值運(yùn)算和枚舉運(yùn)算方面),這種domain logic不管脫離不脫離所在的框架,它的行為都是一致的。對(duì)于這種domain logic,業(yè)務(wù)邏輯層并不需要提供封裝方法,它可以適用于任何場(chǎng)合。
    posted on 2006-05-30 13:31 混沌中立 閱讀(3058) 評(píng)論(1)  編輯  收藏 所屬分類: about java & j2ee

    評(píng)論:
    # re: javaEye上面對(duì)于domain object的討論[未登錄](méi) 2014-06-18 15:20 | Felix
    清楚明了,已收藏  回復(fù)  更多評(píng)論
      
    主站蜘蛛池模板: 国产在线观看麻豆91精品免费| 亚洲人成网亚洲欧洲无码久久| 又硬又粗又长又爽免费看 | 精品国产亚洲男女在线线电影| 777成影片免费观看| 一本久久免费视频| 免费观看又污又黄在线观看| 亚洲午夜精品国产电影在线观看| 亚洲一区二区三区国产精品| 在线看片无码永久免费视频| 无码一区二区三区免费| 日日狠狠久久偷偷色综合免费| 免费播放美女一级毛片| 激情亚洲一区国产精品| 亚洲va久久久噜噜噜久久天堂| 亚洲欧洲精品成人久久奇米网| 四虎影院在线免费播放| 国产精品成人观看视频免费| 国产电影午夜成年免费视频| 国产成人精品免费视频网页大全| 久久九九全国免费| 久久免费视频一区| ww在线观视频免费观看w| 亚洲av成人片在线观看| 亚洲日韩亚洲另类激情文学| 亚洲午夜电影一区二区三区| 亚洲人成小说网站色| 亚洲成电影在线观看青青| 久久亚洲AV午夜福利精品一区| 亚洲精品亚洲人成人网| 国产精品xxxx国产喷水亚洲国产精品无码久久一区 | 久久精品亚洲一区二区三区浴池| 久久久青草青青亚洲国产免观| 亚洲男人的天堂一区二区| 国产亚洲精品无码成人| 亚洲va在线va天堂va不卡下载| 亚洲人成毛片线播放| 香港特级三A毛片免费观看| 黄页免费视频播放在线播放| 香蕉免费在线视频| 亚洲毛片免费观看|