Domain Model设计模式Word文档下载推荐.docx
《Domain Model设计模式Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《Domain Model设计模式Word文档下载推荐.docx(12页珍藏版)》请在冰豆网上搜索。
Long
id=
null;
int
version;
String
name;
Userseller;
description;
MonetaryAmountinitialPrice;
MonetaryAmountreservePrice;
Date
startDate;
endDate;
Set
categorizedItems=
new
HashSet();
Collection
bids=
ArrayList();
BidsuccessfulBid;
ItemStatestate;
UserapprovedBy;
approvalDatetime;
created=
Date();
//
getter/setter方法省略不写,避免篇幅太长
}
interface
ItemDao
ItemgetItemById(Long
id);
findAll();
void
updateItem(Itemitem);
ItemDao定义持久化操作的接口,用于隔离持久化代码。
ItemDaoHibernateImpl
extends
HibernateDaoSupport
id)
return
(Item)
getHibernateTemplate().load(Item.class,id);
}
findAll()
(List)
getHibernateTemplate().find("
fromItem"
);
updateItem(Itemitem)
getHibernateTemplate().update(item);
ItemDaoHibernateImpl完成具体的持久化工作,请注意,数据库资源的获取和释放是在ItemDaoHibernateImpl里面处理的,每个DAO方法调用之前打开Session,DAO方法调用之后,关闭Session。
(Session放在ThreadLocal中,保证一次调用只打开关闭一次)
ItemManager
ItemDaoitemDao;
setItemDao(ItemDaoitemDao)
this.itemDao
=itemDao;
BidloadItemById(Long
itemDao.loadItemById(id);
listAllItems()
itemDao.findAll();
BidplaceBid(Itemitem,Userbidder,MonetaryAmountbidAmount,
BidcurrentMaxBid,BidcurrentMinBid)
throws
BusinessException
if
(currentMaxBid!
=
null
&
currentMaxBid.getAmount().compareTo(bidAmount)
>
0)
throw
BusinessException("
Bidtoolow."
//Auctionisactive
(
!
state.equals(ItemState.ACTIVE)
)
Auctionisnotactiveyet."
//Auctionstillvalid
item.getEndDate().before(
Date()
Can'
tplacenewbid,auctionalreadyended."
//CreatenewBid
BidnewBid=
Bid(bidAmount,item,bidder);
//PlacebidforthisItem
item.getBids().add(newBid);
itemDao.update(item);
调用DAO完成持久化操作
newBid;
事务的管理是在ItemManger这一层完成的,ItemManager实现具体的业务逻辑。
除了常见的和CRUD有关的简单逻辑之外,这里还有一个placeBid的逻辑,即项目的竞标。
以上是一个完整的第一种模型的示例代码。
在这个示例中,placeBid,loadItemById,findAll等等业务逻辑统统放在ItemManager中实现,而Item只有getter/setter方法。
第二种模型,也就是MartinFowler指的richdomainobject是下面这样子的:
一个带有业务逻辑的实体类,即domainobject是Item
一个DAO接口ItemDao
一个DAO实现ItemDaoHibernateImpl
一个业务逻辑对象ItemManager
所有的属性和getter/setter方法同上,省略
BidplaceBid(Userbidder,MonetaryAmountbidAmount,
//Checkhighestbid(canalsobeadifferentStrategy(pattern))
this.getEndDate().before(
Bid(bidAmount,this,bidder);
this.getBids.add(newBid);
//请注意这一句,透明的进行了持久化,但是不能在这里调用ItemDao,Item不能对ItemDao产生依赖!
竞标这个业务逻辑被放入到Item中来。
请注意this.getBids.add(newBid);
如果没有Hibernate或者JDO这种O/RMapping的支持,我们是无法实现这种透明的持久化行为的。
但是请注意,Item里面不能去调用ItemDAO,对ItemDAO产生依赖!
ItemDao和ItemDaoHibernateImpl的代码同上,省略。
item.placeBid(bidder,bidAmount,currentMaxBid,currentMinBid);
//必须显式的调用DAO,保持持久化
在第二种模型中,placeBid业务逻辑是放在Item中实现的,而loadItemById和findAll业务逻辑是放在ItemManager中实现的。
不过值得注意的是,即使placeBid业务逻辑放在Item中,你仍然需要在ItemManager中简单的封装一层,以保证对placeBid业务逻辑进行事务的管理和持久化的触发。
这种模型是MartinFowler所指的真正的domainmodel。
在这种模型中,有三个业务逻辑方法:
placeBid,loadItemById和findAll,现在的问题是哪个逻辑应该放在Item中,哪个逻辑应该放在ItemManager中。
在我们这个例子中,placeBid放在Item中(但是ItemManager也需要对它进行简单的封装),loadItemById和findAll是放在ItemManager中的。
切分的原则是什么呢?
RodJohnson提出原则是“casebycase”,可重用度高的,和domainobject状态密切关联的放在Item中,可重用度低的,和domainobject状态没有密切关联的放在ItemManager中。
我提出的原则是:
看业务方法是否显式的依赖持久化。
Item的placeBid这个业务逻辑方法没有显式的对持久化ItemDao接口产生依赖,所以要放在Item中。
请注意,如果脱离了Hibernate这个持久化框架,Item这个domainobject是可以进行单元测试的,他不依赖于Hibernate的持久化机制。
它是一个独立的,可移植的,完整的,自包含的域对象。
而loadItemById和findAll这两个业务逻辑方法是必须显式的对持久化ItemDao接口产生依赖,否则这个业务逻辑就无法完成。
如果你要把这两个方法放在Item中,那么Item就无法脱离Hibernate框架,无法在Hibernate框架之外独立存在。
第三种模型印象中好像是firebody或者是Archie提出的(也有可能不是,记不清楚了),简单的来说,这种模型就是把第二种模型的domainobject和businessobject合二为一了。
所以ItemManager就不需要了,在这种模型下面,只有三个类,他们分别是:
Item:
包含了实体类信息,也包含了所有的业务逻辑
ItemDao:
持久化DAO接口类
ItemDaoHibernateImpl:
DAO接口的实现类
由于ItemDao和ItemDaoHibernateImpl和上面完全相同,就省略了。
所有的属性和getter/setter方法都省略
static
{this.itemDao
ItemloadItemById(Long
itemDao.loadItemById(id);
itemDao.findAll();
this.addBid(newBid);
itemDao.update(this);
调用DAO进行显式持久化
在这种模型中,所有的业务逻辑全部都在Item中,事务管理也在Item中实现。
在上面三种模型之外,还有很多这三种模型的变种,例如partech的模型就是把第二种模型中的DAO和Manager三个类合并为一个类后形成的模型;
例如frain....(id很长记不住)的模型就是把第三种模型的三个类完全合并为一个单类后形成的模型;
例如Archie是把第三种模型的Item又分出来一些纯数据类(可能是,不确定)形成的一个模型。
但是不管怎么变,基本模型归纳起来就是上面的三种模型,下面分别简单评价一下:
第一种模型绝大多数人都反对,因此反对理由我也不多讲了。
但遗憾的是,我观察到的实际情形是,很多使用Hibernate的公司最后都是这种模型,这里面有很大的原因是很多公司的技术水平没有达到这种层次,所以导致了这种贫血模型的出现。
从这一点来说,MartinFowler的批评声音不是太响了,而是太弱了,还需要再继续呐喊。
第二种模型就是MartinFowler一直主张的模型,实际上也是我一直在实际项目中采用这种模型。
我没有看过Martin的POEAA,之所以能够自己摸索到这种模型,也是因为从02年我已经开始思考这个问题并且寻求解决方案了,但是当时没有看到Hibernate,那时候做的一个小型项目我已经按照这种模型来做了,但是由于没有O/RMapping的支持,写到后来又不得不全部改成贫血的domainobject,项目做完以后再继续找,随后就发现了Hibernate。
当然,现在很多人一开始就是用Hibernate做项目,没有经历过我经历的那个阶段。
不过我觉得这种模型仍然不够完美,因为你还是需要一个业务逻辑层来封装所有的domainlogic,这显得非常罗嗦,并且业务逻辑对象的接口也不够稳定。
如果不考虑业务逻辑对象的重用性的话(业务逻辑对象的可重用性也不可能好),很多人干脆就去掉了xxxManager这一层,在Web层的Action代码直接调用xxxDao,同时容器事务管理配置到Action这一层上来。
Hibernate的caveatemptor就是这样架构的一个典型应用。
第三种模型是我很反对的一种模型,这种模型下面,DomainObject和DAO形成了双向依赖关系,无法脱离框架测试,并且业务逻辑层的服务也和持久层对象的状态耦合到了一起,会造成程序的高度的复杂性,很差的灵活性和糟糕的可维护性。
也许将来技术进步导致的O/RMapping管理下的domainobject发展到足够的动态持久透明化的话,这种模型才会成为一个理想的选择。
就像O/RMapping的流行使得第二种模型成为了可能(O/RMapping流行以前,我们只能用第一种模型,第二种模型那时候是不现实的)。
第二种模型中,我们可以清楚的把这4个类分为三层:
1、实体类层,即Item,带有domainlogic的domainobject
2、DAO层,即ItemDao和ItemDaoHibernateImpl,抽象持久化操作的接口和实现类
3、业务逻辑层,即ItemManager,接受容器事务控制,向Web层提供统一的服务调用
在这三层中我们大家可以看到,domainobject和DAO都是非常稳定的层,其实原因也很简单,因为domainobject是映射数据库字段的,数据库字段不会频繁变动,所以domainobject也相对稳定,而面向数据库持久化编程的DAO层也不过就是CRUD而已,不会有更多的花样,所以也很稳定。
问题就在于这个充当businessworkflowfacade的业务逻辑对象,它的变动是相当频繁的。
业务逻辑对象通常都是无状态的、受事务控制的、Singleton类,我们可以考察一下业务逻辑对象都有哪几类业务逻辑方法:
第一类:
DAO接口方法的代理,就是上面例子中的loadItemById方法和findAll方法。
ItemManager之所以要代理这种类,目的有两个:
向Web层提供统一的服务调用入口点和给持久化方法增加事务控制功能。
这两点都很容易理解,你不能既给Web层程序员提供xxxManager,也给他提供xxxDao,所以你需要用xxxManager封装xxxDao,在这里,充当了一个简单代理功能;
而事务控制也是持久化方法必须的,事务可能需要跨越多个DAO方法调用,所以必须放在业务逻辑层,而不能放在DAO层。
但是必须看到,对于一个典型的web应用来说,绝大多数的业务逻辑都是简单的CRUD逻辑,所以这种情况下,针对每个DAO方法,xxxManager都需要提供一个对应的封装方法,这不但是非常枯燥的,也是令人感觉非常不好的。
第二类:
domainlogic的方法代理。
就是上面例子中placeBid方法。
虽然Item已经有了placeBid方法,但是ItemManager仍然需要封装一下Item的placeBid,然后再提供一个简单封装之后的代理方法。
这和第一种情况类似,其原因也一样,也是为了给Web层