`
sunxboy
  • 浏览: 2868820 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

domain object模型探讨(robbin原创)

阅读更多

有兴趣可看此处原文及相关讨论:总结一下最近关于domain object以及相关的讨论

======================================

 

在最近的围绕domain object的讨论中浮现出来了三种模型,(还有一些其他的旁枝,不一一分析了),经过一番讨论,各种问题逐渐清晰起来,在这里我试图做一个总结,便于大家了解和掌握。

第一种模型:只有getter/setter方法的纯数据类,所有的业务逻辑完全由business object来完成(又称TransactionScript),这种模型下的domain object被Martin Fowler称之为“贫血的domain object”。下面用举一个具体的代码来说明,代码来自Hibernate的caveatemptor,但经过我的改写:

一个实体类叫做Item,指的是一个拍卖项目
一个DAO接口类叫做ItemDao
一个DAO接口实现类叫做ItemDaoHibernateImpl
一个业务逻辑类叫做ItemManager(或者叫做ItemService)

Java代码 复制代码
  1. public   class  Item  implements  Serializable {  
  2.     private  Long id =  null ;  
  3.     private   int  version;  
  4.     private  String name;  
  5.     private  User seller;  
  6.     private  String description;  
  7.     private  MonetaryAmount initialPrice;  
  8.     private  MonetaryAmount reservePrice;  
  9.     private  Date startDate;  
  10.     private  Date endDate;  
  11.     private  Set categorizedItems =  new  HashSet();  
  12.     private  Collection bids =  new  ArrayList();  
  13.     private  Bid successfulBid;  
  14.     private  ItemState state;  
  15.     private  User approvedBy;  
  16.     private  Date approvalDatetime;  
  17.     private  Date created =  new  Date();  
  18.     //  getter/setter方法省略不写,避免篇幅太长   
  19. }  
public class Item implements Serializable {
    private Long id = null;
    private int version;
    private String name;
    private User seller;
    private String description;
    private MonetaryAmount initialPrice;
    private MonetaryAmount reservePrice;
    private Date startDate;
    private Date endDate;
    private Set categorizedItems = new HashSet();
    private Collection bids = new ArrayList();
    private Bid successfulBid;
    private ItemState state;
    private User approvedBy;
    private Date approvalDatetime;
    private Date created = new Date();
    //  getter/setter方法省略不写,避免篇幅太长
}

 

Java代码 复制代码
  1. public   interface  ItemDao {  
  2.     public  Item getItemById(Long id);  
  3.     public  Collection findAll();  
  4.     public   void  updateItem(Item item);  
  5. }  
public interface ItemDao {
    public Item getItemById(Long id);
    public Collection findAll();
    public void updateItem(Item item);
}



ItemDao定义持久化操作的接口,用于隔离持久化代码。

Java代码 复制代码
  1. public   class  ItemDaoHibernateImpl  implements  ItemDao  extends  HibernateDaoSupport {  
  2.     public  Item getItemById(Long id) {  
  3.         return  (Item) getHibernateTemplate().load(Item. class , id);  
  4.     }  
  5.     public  Collection findAll() {  
  6.         return  (List) getHibernateTemplate().find( "from Item" );  
  7.     }  
  8.     public   void  updateItem(Item item) {  
  9.         getHibernateTemplate().update(item);  
  10.     }  
  11. }  
public class ItemDaoHibernateImpl implements ItemDao extends HibernateDaoSupport {
    public Item getItemById(Long id) {
        return (Item) getHibernateTemplate().load(Item.class, id);
    }
    public Collection findAll() {
        return (List) getHibernateTemplate().find("from Item");
    }
    public void updateItem(Item item) {
        getHibernateTemplate().update(item);
    }
}


ItemDaoHibernateImpl完成具体的持久化工作,请注意,数据库资源的获取和释放是在ItemDaoHibernateImpl 里面处理的,每个DAO方法调用之前打开Session,DAO方法调用之后,关闭Session。(Session放在ThreadLocal中,保证 一次调用只打开关闭一次)

Java代码 复制代码
  1. public   class  ItemManager {  
  2.     private  ItemDao itemDao;  
  3.     public   void  setItemDao(ItemDao itemDao) {  this .itemDao = itemDao;}  
  4.     public  Bid loadItemById(Long id) {   
  5.         itemDao.loadItemById(id);  
  6.     }  
  7.     public  Collection listAllItems() {  
  8.         return   itemDao.findAll();  
  9.     }  
  10.     public  Bid placeBid(Item item, User bidder, MonetaryAmount bidAmount,  
  11.                         Bid currentMaxBid, Bid currentMinBid) throws  BusinessException {  
  12.             if  (currentMaxBid !=  null  && currentMaxBid.getAmount().compareTo(bidAmount) >  0 ) {  
  13.         throw   new  BusinessException( "Bid too low." );  
  14.     }  
  15.       
  16.     // Auction is active   
  17.     if  ( !state.equals(ItemState.ACTIVE) )  
  18.         throw   new  BusinessException( "Auction is not active yet." );  
  19.       
  20.     // Auction still valid   
  21.     if  ( item.getEndDate().before(  new  Date() ) )  
  22.         throw   new  BusinessException( "Can't place new bid, auction already ended." );  
  23.       
  24.     // Create new Bid   
  25.     Bid newBid = new  Bid(bidAmount, item, bidder);  
  26.       
  27.     // Place bid for this Item   
  28.     item.getBids().add(newBid);  
  29.     itemDao.update(item);     //  调用DAO完成持久化操作   
  30.     return  newBid;  
  31.     }  
  32. }  
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 {
            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( new Date() ) )
    	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);     //  调用DAO完成持久化操作
    return newBid;
    }
}



事务的管理是在ItemManger这一层完成的,ItemManager实现具体的业务逻辑。除了常见的和CRUD有关的简单逻辑之外,这里还有一个placeBid的逻辑,即项目的竞标。

以上是一个完整的第一种模型的示例代码。在这个示例中,placeBid,loadItemById,findAll等等业务逻辑统统放在ItemManager中实现,而Item只有getter/setter方法。

 

第二种模型,也就是Martin Fowler指的rich domain object是下面这样子的:

一个带有业务逻辑的实体类,即domain object是Item
一个DAO接口ItemDao
一个DAO实现ItemDaoHibernateImpl
一个业务逻辑对象ItemManager

Java代码 复制代码
  1. public   class  Item  implements  Serializable {  
  2.     //  所有的属性和getter/setter方法同上,省略   
  3.     public  Bid placeBid(User bidder, MonetaryAmount bidAmount,  
  4.                         Bid currentMaxBid, Bid currentMinBid)  
  5.         throws  BusinessException {  
  6.       
  7.         // Check highest bid (can also be a different Strategy (pattern))   
  8.         if  (currentMaxBid !=  null  && currentMaxBid.getAmount().compareTo(bidAmount) >  0 ) {  
  9.             throw   new  BusinessException( "Bid too low." );  
  10.         }  
  11.       
  12.         // Auction is active   
  13.         if  ( !state.equals(ItemState.ACTIVE) )  
  14.             throw   new  BusinessException( "Auction is not active yet." );  
  15.       
  16.         // Auction still valid   
  17.         if  (  this .getEndDate().before(  new  Date() ) )  
  18.             throw   new  BusinessException( "Can't place new bid, auction already ended." );  
  19.       
  20.         // Create new Bid   
  21.         Bid newBid = new  Bid(bidAmount,  this , bidder);  
  22.       
  23.         // Place bid for this Item   
  24.         this .getBids.add(newBid);   // 请注意这一句,透明的进行了持久化,但是不能在这里调用ItemDao,Item不能对ItemDao产生依赖!   
  25.       
  26.         return  newBid;  
  27.     }  
  28. }  
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的代码同上,省略。

Java代码 复制代码
  1. public   class  ItemManager {   
  2.     private  ItemDao itemDao;   
  3.     public   void  setItemDao(ItemDao itemDao) {  this .itemDao = itemDao;}   
  4.     public  Bid loadItemById(Long id) {   
  5.         itemDao.loadItemById(id);   
  6.     }   
  7.     public  Collection listAllItems() {   
  8.         return   itemDao.findAll();   
  9.     }   
  10.     public  Bid placeBid(Item item, User bidder, MonetaryAmount bidAmount,   
  11.                             Bid currentMaxBid, Bid currentMinBid) throws  BusinessException {   
  12.         item.placeBid(bidder, bidAmount, currentMaxBid, currentMinBid);  
  13.         itemDao.update(item);    // 必须显式的调用DAO,保持持久化   
  14.     }  
  15. }  
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框架之外独立存在。

 

第三种模型印象中好像是firebody或者是Archie提出的(也有可能不是,记不清楚了),简单的来说,这种模型就是把第二种模型的domain object和business object合二为一了。所以ItemManager就不需要了,在这种模型下面,只有三个类,他们分别是:

Item:包含了实体类信息,也包含了所有的业务逻辑
ItemDao:持久化DAO接口类
ItemDaoHibernateImpl:DAO接口的实现类

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

Java代码 复制代码
  1. public   class  Item  implements  Serializable {  
  2.     //  所有的属性和getter/setter方法都省略   
  3.    private   static  ItemDao itemDao;  
  4.     public   void  setItemDao(ItemDao itemDao) { this .itemDao = itemDao;}  
  5.       
  6.     public   static  Item loadItemById(Long id) {  
  7.         return  (Item) itemDao.loadItemById(id);  
  8.     }  
  9.     public   static  Collection findAll() {  
  10.         return  (List) itemDao.findAll();  
  11.     }  
  12.   
  13.     public  Bid placeBid(User bidder, MonetaryAmount bidAmount,  
  14.                     Bid currentMaxBid, Bid currentMinBid)  
  15.     throws  BusinessException {  
  16.       
  17.         // Check highest bid (can also be a different Strategy (pattern))   
  18.         if  (currentMaxBid !=  null  && currentMaxBid.getAmount().compareTo(bidAmount) >  0 ) {  
  19.             throw   new  BusinessException( "Bid too low." );  
  20.         }  
  21.           
  22.         // Auction is active   
  23.         if  ( !state.equals(ItemState.ACTIVE) )  
  24.             throw   new  BusinessException( "Auction is not active yet." );  
  25.           
  26.         // Auction still valid   
  27.         if  (  this .getEndDate().before(  new  Date() ) )  
  28.             throw   new  BusinessException( "Can't place new bid, auction already ended." );  
  29.           
  30.         // Create new Bid   
  31.         Bid newBid = new  Bid(bidAmount,  this , bidder);  
  32.           
  33.         // Place bid for this Item   
  34.         this .addBid(newBid);  
  35.         itemDao.update(this );       //  调用DAO进行显式持久化   
  36.         return  newBid;  
  37.     }  
  38. }  
public class Item implements Serializable {
    //  所有的属性和getter/setter方法都省略
   private static ItemDao itemDao;
    public void setItemDao(ItemDao itemDao) {this.itemDao = itemDao;}
    
    public static Item loadItemById(Long id) {
        return (Item) itemDao.loadItemById(id);
    }
    public static Collection 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( 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.addBid(newBid);
        itemDao.update(this);      //  调用DAO进行显式持久化
        return newBid;
    }
}



在这种模型中,所有的业务逻辑全部都在Item中,事务管理也在Item中实现。

 

 

转自:http://www.iteye.com/topic/11712

 

分享到:
评论

相关推荐

    MFC SDI制作Office2007样式的Robbin菜单.docx

    ### MFC SDI 制作 Office 2007 样式 Robbin 菜单 #### 一、概述 Microsoft Foundation Classes (MFC) 是一个由微软开发的类库,用于简化 Windows 应用程序的开发过程。本文档旨在介绍如何在 MFC 单文档界面...

    Robbin Fan—运营专业型社区的经验和反思.ppt

    Robbin Fan—运营专业型社区的经验和反思.ppt

    范凯个人网站源码robbin_site.zip

    robbin_site 是范凯的个人网站 http://robbinfan.com 网站的源码。 标签:robbin

    javaeye Robbin 论缓存技术

    在Javaeye Robbin的讨论中,他提到了缓存技术在多种场景下的应用和重要性。 **缓存的作用** 1. **高速缓冲存储**:缓存是一种位于主存储器和慢速I/O设备之间的高速存储器,其目的是减少对慢速设备的访问次数,提高...

    Git常用命令备忘 - robbin的自言自语1

    在本文中,我们将深入探讨Git的一些常用命令,这些命令对于日常开发和协作至关重要。 首先,配置Git是使用它的第一步。通过`git config`命令,我们可以设置用户信息,如用户名和邮箱,以及自定义别名以简化命令输入...

    Ribbon For DELPHI 10.1 BERLIN.7z

    《Ribbon for Delphi 10.1 Berlin:构建现代用户界面的新篇章》 在软件开发领域,Delphi作为一款强大的RAD(快速应用开发)工具,一直以来都深受开发者喜爱。而Ribbon界面设计则是在微软Office系列软件中广泛应用的...

    Struts2学习资料(强烈推荐)

    1. **MVC模式**:Struts2是基于Model-View-Controller(MVC)设计模式的,它将业务逻辑、数据模型和用户界面分离,提高了代码的可维护性和可重用性。 2. **Action类**:每个用户请求都会映射到一个Action类,这是...

    一个基于WPF+ C# 实现的Ribbon 控件库程序代码

    4. **MVVM模式**:WPF常使用的模型-视图-视图模型(MVVM)设计模式,有助于解耦视图和业务逻辑。 5. **依赖属性**:WPF中的一个重要特性,用于实现数据绑定和属性继承。 6. **命令**:理解ICommand接口和命令模式,...

    spring cloud 组件整合 eureka gateway feign hystrix,各个模的整合,供大家一起学习

    让我们深入探讨这些组件以及它们如何整合。 1. **Eureka**:Eureka是Spring Cloud中的服务注册与发现组件。它是一个基于REST的服务,用于定位服务,使服务能够相互发现。每个微服务启动时,会向Eureka Server注册...

    robbin谈管理:我敬佩的3位CEO管理者

    GE在韦尔奇任内20年实现了每年30%的高速增长,市值曾经达到全球第2,是全球最著名的CEO楷模。韦尔奇写的两本书:一本自传,一本Winning我读了很多遍,我觉得最有意思的反差是,尽管韦尔奇整个职业生涯都在GE渡过,...

    景观规划中的生态廊道宽度.pdf

    此外,Robbin的研究揭示了森林破碎化对鸟类种群的影响,Ambuel和S.A Temple则分析了南部威斯康星州森林鸟类社区和植被的变化,强调了面积依赖性的生态效应。 Liu的《乡村景观规划》(2003)探讨了乡村景观的保护和...

    Struts2技术内幕:深入解析Struts架构设计与实现原理

    资源名称:Struts2技术内幕:深入解析Struts架构设计与实现原理内容简介:本书由国内极为资深的Struts2技术专家(网名:downpour)亲自执笔,iteye兼CSDN产品总监范凯(网名:robbin)以及51CTO等技术社区鼎力推荐。...

    获取ip地址方法与示例

    这篇文章将详细探讨“获取IP地址的方法与示例”,并基于提供的标签“源码”和“工具”进行深入解析。 首先,我们需要了解什么是IP地址。IP地址(Internet Protocol Address)是互联网上每个设备的唯一标识,它允许...

Global site tag (gtag.js) - Google Analytics