java 域模型_基于Spring实现领域模型模式 - RUP实践者指南 - JavaEye技术网站
事務(wù)腳本、領(lǐng)域模型及表模塊是Martin
Fowler在《企業(yè)應(yīng)用架構(gòu)模式》中總結(jié)的三種領(lǐng)域邏輯組織模式。各有各的優(yōu)點(diǎn)和缺點(diǎn),這里不打算討論它們各自的適用場景,只簡單總結(jié)一下在應(yīng)用領(lǐng)域模
型模式方面的一些經(jīng)驗(yàn)與教訓(xùn)。
1.??? 背景
近幾年Struts(1或2) + Spring + Hibernate(IBatis)組合在Java
EE企業(yè)應(yīng)用開發(fā)中頻頻出現(xiàn)。這里也不例外,所采用的主要技術(shù)平臺(tái)即是SSH。本文總結(jié)的領(lǐng)域模型模式應(yīng)用也主要是在這樣一個(gè)技術(shù)背景之下。
在企業(yè)應(yīng)用的架構(gòu)設(shè)計(jì)方面,我通常組合使用多種架構(gòu)風(fēng)格和模式,它們主要是:
按技術(shù)職責(zé)分層模式
組件化模式
按通用性程度分層
基于職責(zé)分層就是一般意義上的分層,劃分也采用相對(duì)常見的方式,即表現(xiàn)層、應(yīng)用層、領(lǐng)域?qū)雍统志脤印?/p>
組件化模式也是我最近幾年非常喜歡的一種架構(gòu)模式,它可以提高系統(tǒng)的可維護(hù)性和可復(fù)用性。邏輯組件參考用例模型并主要在分析模型中初步創(chuàng)建,在設(shè)計(jì)模型中
進(jìn)一步細(xì)化。組件的粒度級(jí)別為業(yè)務(wù)組件(各種組件級(jí)別包括:分布式組件、業(yè)務(wù)組件和系統(tǒng)級(jí)組件),業(yè)務(wù)組件可以包含所有4個(gè)職責(zé)層,即表示層、應(yīng)用層、領(lǐng)
域?qū)雍统志脤?#xff0c;且持久層邏輯上包括數(shù)據(jù)表。業(yè)務(wù)組件的對(duì)外接口一般設(shè)置在應(yīng)用層級(jí)別,其他元素通常不對(duì)外暴露。對(duì)于數(shù)據(jù)庫表更不例外,一般地,屬于某個(gè)業(yè)
務(wù)組件的數(shù)據(jù)庫表禁止其他組件直接使用。建立一個(gè)良好的組件模型是很費(fèi)工夫的事情,需要在分析模型上花費(fèi)很大的精力,在設(shè)計(jì)模型中還需要進(jìn)一步優(yōu)化。但付
出總是會(huì)有回報(bào)的,系統(tǒng)可修改、可維護(hù)性、可復(fù)用性會(huì)有很大提升。當(dāng)然有時(shí)我們確實(shí)不需要這種回報(bào)。
最后一個(gè)常用的架構(gòu)模式是按組件的通用性進(jìn)行分層,這也是RUP里的分層,即特定于應(yīng)用層、通用業(yè)務(wù)層、中間件層和系統(tǒng)層。如果開發(fā)多個(gè)類似產(chǎn)品,各個(gè)產(chǎn)
品中不相同的組件位于特定應(yīng)用層,各個(gè)產(chǎn)品在業(yè)務(wù)層面可以復(fù)用的組件位于通用業(yè)務(wù)層,與業(yè)務(wù)領(lǐng)域無關(guān)的組件放在中間件層。系統(tǒng)層主要是DBMS、OS級(jí)別
的東西。這種架構(gòu)模式主要服務(wù)于系統(tǒng)化復(fù)用。
介紹完成應(yīng)用領(lǐng)域模型模式的背景之后,應(yīng)該進(jìn)入正題了。
2.??? 應(yīng)用領(lǐng)域模型
2.1.??? 主要設(shè)計(jì)元素
展現(xiàn)層主要包括:
View:主要是視圖元素,如JSP。
Command:主要是Struts2中的Action,通常Action以應(yīng)用層中的DTO為屬性來充當(dāng)PresentationModel。
應(yīng)用層主要包括:
ApplicationService:由接口和實(shí)現(xiàn)兩部分組成,主要用來委托職責(zé)給實(shí)體或協(xié)調(diào)多個(gè)實(shí)體的協(xié)作,是真正的控制類,一般為每個(gè)用例
創(chuàng)建一個(gè)應(yīng)用服務(wù)類。Struts1/2中的servlet或filter也常被稱為控制器,但在此種場景下,更準(zhǔn)確的名稱應(yīng)該是前端控制器,屬于展現(xiàn)
層。
DTO:數(shù)據(jù)傳輸對(duì)象。
Assembler:組裝器,負(fù)責(zé)將實(shí)體轉(zhuǎn)化為數(shù)據(jù)傳輸對(duì)象。
領(lǐng)域?qū)又饕?#xff1a;
Entity:處理核心領(lǐng)域邏輯。
DomainService:是不屬于單一實(shí)體且比較穩(wěn)定的領(lǐng)域邏輯的處理者
持久層主要包括:
DAO:可以由接口和實(shí)現(xiàn)兩部分組成,數(shù)據(jù)訪問對(duì)象。
組件接口元素主要包括:
ComponentFacade:組件對(duì)外接口,其實(shí)現(xiàn)形式與ApplicationService的實(shí)現(xiàn)相同。
ComponentFacadeFactory:其他組件在使用該組件時(shí),不能直接創(chuàng)建組件接口,該元素為創(chuàng)建組件接口提供了一個(gè)工廠,隱藏了組
件的內(nèi)部實(shí)現(xiàn)。
DTO:與上面DTO同。
2.2.??? 讓實(shí)體處理領(lǐng)域邏輯
實(shí)體或領(lǐng)域?qū)ο笫欠袷穷I(lǐng)域邏輯的核心處理者應(yīng)該是事務(wù)腳本和領(lǐng)域模型模式之間的本質(zhì)區(qū)別。“貧血領(lǐng)域模型”的本質(zhì)應(yīng)該更貼近事務(wù)腳本模式,它們有著類似的
優(yōu)缺點(diǎn)。
以下是任務(wù)管理組件的領(lǐng)域模型:
注:示例中藍(lán)色字體的實(shí)體不屬于任務(wù)管理組件。
讓實(shí)體處理領(lǐng)域邏輯是一件簡單的事,但讓實(shí)體處理哪些領(lǐng)域邏輯卻是一個(gè)需要思考的問題。
為實(shí)體指派職責(zé)的基本原則是“誰擁有誰負(fù)責(zé)”,即“信息專家模式”。實(shí)體應(yīng)該處理哪些它擁有相關(guān)信息和能力的領(lǐng)域職責(zé)。這與現(xiàn)實(shí)世界是一致的,如一個(gè)醫(yī)
生、一個(gè)木匠,生病了要找醫(yī)生,因?yàn)獒t(yī)生擁有治病救人的專業(yè)知識(shí),打家具要找木匠,因?yàn)槟窘秤凶黾揖叩募寄堋?/p>
在上面的領(lǐng)域模型中,創(chuàng)建、更新和刪除任務(wù)的職責(zé)應(yīng)該放到Task上,這自不必說。那么統(tǒng)計(jì)項(xiàng)目實(shí)際工時(shí)的職責(zé)應(yīng)該放到哪里呢?首先,考查Project
對(duì)象,它知道自己有哪些迭代,那么它可以用來合計(jì)各個(gè)迭代的工時(shí)。再考查迭代對(duì)象,它知道自己有哪些任務(wù),那么它可以累加所有任務(wù)的工時(shí)。依此類推
Task則要負(fù)責(zé)自己工時(shí)的計(jì)算。當(dāng)然這個(gè)過程中我們可能會(huì)遇到性能問題,這需要在性能目標(biāo)和可維護(hù)性目標(biāo)之間做出平衡。
“信息專家模式”是一個(gè)基本原則,但不是唯一的原則。關(guān)于如何分配職責(zé)是一個(gè)很大的話題這里就不再深入了。
在讓實(shí)體處理領(lǐng)域邏輯時(shí),應(yīng)用服務(wù)通常只是將職責(zé)委托給實(shí)體,或協(xié)調(diào)多個(gè)實(shí)體完成業(yè)務(wù)邏輯,應(yīng)用服務(wù)層很薄。如:
@Service("taskFacade")
@Transactional(readOnly = true, rollbackFor = Throwable.class)
public class TaskFacadeImpl implements TaskFacade {
@Transactional(readOnly = false, isolation = Isolation.SERIALIZABLE)
public long createTask(long projectId, long periodId, Integer
periodTag,????? Integer index, String name, int priority, Date
startDate, Date endDate,
int points, String description, List
resources)
throws BusinessException {
Task root = getRootTaskFromCache(projectId, periodId, periodTag);
if (root == null) {
root = EntityFactory.getEntity(Task.class);
root.save(projectId, periodId, periodTag);
addRootTaskToCache(projectId, periodId, periodTag, root.getId());
}
return root.addSubTask(index, name, priority, startDate, endDate,
points,description, resources);
}
@Transactional(readOnly = false, isolation = Isolation.SERIALIZABLE)
public void moveUpTask(long taskId) throws BusinessException {
Task task = Task.get(taskId);
if (task == null)
throw new BusinessException("任務(wù)不存在。");
task.moveUp();
}
……
}
2.3.??? 賦予實(shí)體持久化能力
實(shí)體要處理領(lǐng)域邏輯,特別是相對(duì)復(fù)雜的領(lǐng)域邏輯,沒有持久化能力是不行的。Martin Fowler推薦Domain Model與Active
Record一起使用。在它們一起使用時(shí),實(shí)體就有了持久化能力,如保存、更新和刪除自己。
那么在使用Struts2 + Spring +
Hibernate這樣的技術(shù)平臺(tái)時(shí)如何給實(shí)體賦予持久化能力呢?這里主要是在實(shí)體上加了一個(gè)靜態(tài)方法用于獲得管理該實(shí)體集合的Dao。如實(shí)體Task中
處理如下
public class Task implements Entity {
……
private static TaskDao dao() {
return IocBeans.getInstance().getBean("taskDao", TaskDao.class);
}
}
Dao是Spring的IOC管理對(duì)象,如TaskDao的具體實(shí)現(xiàn):
@Repository("taskDao")
public class HibernateTaskDao extends HibernateDao implements TaskDao {
@Autowired
public void setSessionFactoryEx(SessionFactory sessionFactory) {
super.setSessionFactory(sessionFactory);
}
public Task get(long id) {
return super.get(Task.class, id);
}
public List findAll(QueryCriteria criteria, int
firstResult,
int maxResults) {
……
}
……
}
其中,HibernateTaskDao可以通過BeanFactory根據(jù)BeanId“taskDao”獲得。這里為了獲取BeanFactory并
方便獲得相關(guān)Bean實(shí)例而創(chuàng)建了類IocBeans,其內(nèi)容如下
public final class IocBeans implements BeanFactoryAware {
private BeanFactory beanFactory;
public void setBeanFactory(BeanFactory beanFactory) throws
BeansException {
this.beanFactory = beanFactory;
}
private IocBeans() {
}
private static IocBeans instance;
public static IocBeans getInstance() {
if (instance == null)
instance = new IocBeans();
return instance;
}
@SuppressWarnings("unchecked")
public T getBean(String name, Class requiredType) {
return (T) beanFactory.getBean(name, requiredType);
}
}
實(shí)現(xiàn)了BeanFactoryAware接口的類可以在容器初始化時(shí),將BeanFactory實(shí)例注入進(jìn)來,從而獲得對(duì)BeanFactory的操控能
力。為了使這個(gè)類可以順利的實(shí)例化,還需要在Spring的配置文件中加入如下內(nèi)容:
factory-method="getInstance" />
經(jīng)過以上努力實(shí)體就擁有了持久化能力,與Active
Record不同的是這里將數(shù)據(jù)訪問邏輯移入Dao中。Dao中通常只有添加、刪除和查找方法,而沒有更新方法。使用Hibernate讓我們很幸運(yùn),我
們不需要在Dao中加入更新方法,因?yàn)樵谖覀兏淖儗?shí)體對(duì)象的屬性時(shí),Hibernate會(huì)幫我們自動(dòng)完成更新。下面是Task對(duì)象的幾個(gè)領(lǐng)域方法:
public long save(long projectId, long periodId, Integer periodTag)
throws BusinessException {
return save(projectId, periodId, periodTag, "任務(wù)虛根",
TaskDetail.PRIORITY_3,
null, null, 0, null, null);
}
protected long save(long projectId, long periodId, Integer periodTag,
String name, int priority, Date startDate, Date endDate, int
points,
String description, List resources)
throws BusinessException {
Assert.notNull(name);
setProjectId(projectId);
setPeriodId(periodId);
setPeriodTag(periodTag);
setName(name);
setPriority(priority);
set_startDate(startDate);
set_endDate(endDate);
setPoints(points);
setDescription(description);
setPercentageCompleted(0.0);
setStatus(TaskDetail.STATUS_OPEN);
long id = (Long) obtainDao().save(this);
if (resources != null && resources.size() > 0)
saveOrUpdateResources(resources);
return id;
}
public void saveOrUpdateResources(List resources)
throws BusinessException {
if (resources == null || resources.size() == 0) {
setResources(null);
IndividualTask.deleteAll(getId());
return;
}
Map resourcesMap = new HashMap
String>();
Set ids = new HashSet();
for (ResourceDetail resource : resources) {
IndividualTask itask = IndividualTask.find(getId(), resource
.getResourceId());
if (itask == null) {
itask = EntityFactory.getEntity(IndividualTask.class);
itask.save(this, resource.getResourceId(),
resource.getPercent(),
resource.isPrincipal());
} else {
itask.update(resource.getPercent(), resource.isPrincipal());
}
ids.add(itask.getId());
StringBuilder sbRes = new StringBuilder();
sbRes.append(itask.getPersonId());
sbRes.append("|");
sbRes.append(resource.getPercent());
sbRes.append("|");
sbRes.append(resource.isPrincipal() ? "Y" : "N");
resourcesMap.put(itask.getId(), sbRes.toString());
}
StringBuilder sb = new StringBuilder();
int i = 1, length = resourcesMap.keySet().size();
for (Long key : resourcesMap.keySet()) {
sb.append(resourcesMap.get(key));
if (i++ < length)
sb.append("_");
}
setResources(sb.toString());
IndividualTask.deleteAllExcept(getId(), new
ArrayList(ids));
}
public void moveTo(long parentId, Integer index) throws
BusinessException {
Assert.isTrue(index == null || index >= 0);
if (getParent() == null)
return;
if (getParent().getId().longValue() == parentId) {
int count = getParent().getChildCount();
if (count == 1)
return;
if (index == null && getIndex() == count - 1)
return;
if (index != null && index.intValue() == getIndex())
return;
}
Task parent = Task.get(parentId);
Task p = parent;
while (p != null) {
if (this.equals(p))
return;
p = p.getParent();
}
if (parent == null)
throw new BusinessException("父任務(wù)不存在。");
if (parent.getProjectId() != getProjectId())
throw new BusinessException("任務(wù)不在同一個(gè)項(xiàng)目中,不能移動(dòng)。");
this.off();
if (!getParent().equals(parent)) {
this.setParent(parent);
if (this.getPeriodId() != parent.getPeriodId()) {
this.changePeriodTo(parent.getPeriodId(),
parent.getPeriodTag());
}
}
Task child = parent.getFirstChild();
if (child == null) {
parent.setFirstChild(this);
this.setNextSibling(null);
return;
}
int i = 0;
Task lastChild = null;
while (child != null) {
if (index != null && index.intValue() == i) {
if (index == 0)
parent.setFirstChild(this);
Task prev = child.getPrevSibling();
if (prev != null)
prev.setNextSibling(this);
this.setNextSibling(child);
return;
}
i++;
if (child.getNextSibling() == null)
lastChild = child;
child = child.getNextSibling();
}
this.setNextSibling(null);
lastChild.setNextSibling(this);
}
2.4.??? 隱藏?cái)?shù)據(jù)訪問對(duì)象
這里使用數(shù)據(jù)訪問對(duì)象Dao分離數(shù)據(jù)訪問邏輯,并管理實(shí)體集合。在職責(zé)上,它同DDD中的Repository是同義的。這里叫Dao而沒有叫
Repository的主要原因是在實(shí)際項(xiàng)目中需要向很多人解釋這個(gè)并不算新的新概念是很麻煩的,所以就依然使用了Dao這個(gè)名稱。
無論是服務(wù)還是實(shí)體都需要使用其他實(shí)體或?qū)嶓w的集合來完成任務(wù),如在服務(wù)中的方法:
@Transactional(readOnly = false, isolation = Isolation.SERIALIZABLE)
public void moveUpTask(long taskId) throws BusinessException {
Task task = Task.get(taskId);
if (task == null)
throw new BusinessException("任務(wù)不存在。");
task.moveUp();
}
這里,要向上移動(dòng)任務(wù),必須先找到任務(wù),再調(diào)用任務(wù)的移動(dòng)方法。無論是查找單一任務(wù)實(shí)例還是集合,都最終需要使用Dao來完成。如果在這里直接使用Dao
來獲得實(shí)體,就必須先獲得Dao,這樣的代碼將會(huì)在很多地方出現(xiàn),即麻煩又難看,所以將這類對(duì)集合操作或查找實(shí)體的操作封裝到實(shí)體當(dāng)中。只是在邏輯上,這
樣的方法不屬于實(shí)體的任何一個(gè)實(shí)例,而是屬于類,所以全部采用靜態(tài)方法,如:
public class Task implements Entity {
……
public static Task get(long id) {
return dao().get(id);
}
public static Task findRoot(long projectId, long periodId, Integer
periodTag) {
return dao().findRoot(projectId, periodId, periodTag);
}
public static List findAll(QueryCriteria criteria, int
firstResult, int maxResults) {
return dao().findAll(criteria, firstResult, maxResults);
}
public static long count(QueryCriteria criteria) {
return dao().count(criteria);
}
private static TaskDao dao() {
return IocBeans.getInstance().getBean("taskDao", TaskDao.class);
}
}
所有這些靜態(tài)方法只是簡單的將職責(zé)委派給Dao來處理,這樣Dao就被隱藏在實(shí)體的背后,服務(wù)類看不到它,其他實(shí)體也看不到它,只有本實(shí)體類可以使用它。
這樣可以很大程度地簡化的領(lǐng)域模型實(shí)現(xiàn)。
2.5.??? 私有化實(shí)體屬性寫方法
對(duì)實(shí)體進(jìn)行有效地封裝可以提高內(nèi)聚性、降低耦合性。比較理想的情況下,我們追求最小暴露,即屬性和方法能私有就不設(shè)置為公共。這是一件很難在團(tuán)隊(duì)中貫徹執(zhí)
行事情,因?yàn)檫@會(huì)增加很多成本。這是一件我喜歡做但從不強(qiáng)求的工作,除了方法之外,對(duì)于屬性的寫方法我總會(huì)將其可見性設(shè)置為protected,如:
public class Comment implements Entity {
private Long id;
private Long taskId;
private Long personId;
private String comment;
private Date addedDate;
protected CommentDao obtainDao() {
return dao();
}
public Long getId() {
return id;
}
protected void setId(Long id) {
this.id = id;
}
public Long getTaskId() {
return taskId;
}
protected void setTaskId(Long taskId) {
this.taskId = taskId;
}
public Long getPersonId() {
return personId;
}
protected void setPersonId(Long personId) {
this.personId = personId;
}
public String getComment() {
return comment;
}
protected void setComment(String comment) {
this.comment = comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public Date getAddedDate() {
return addedDate;
}
public void setAddedDate(Date addedDate) {
this.addedDate = addedDate;
}
……
}
對(duì)屬性的寫方法的限制訪問可以很大程度上降低來自服務(wù)類的對(duì)實(shí)體對(duì)象的誤操作,當(dāng)然也需要付出比較高的成本。如在保存時(shí),就需要這樣寫了
public void save(long taskId, long personId, String comment) {
setTaskId(taskId);
setPersonId(personId);
setComment(comment);
setAddedDate(DateUtils.today());
obtainDao().save(this);
}
2.6.??? 訪問其他組件
實(shí)體常常會(huì)訪問其他組件,這時(shí)就建立了實(shí)體同其他組件的耦合關(guān)系。但如果被引用的組件接口是相對(duì)穩(wěn)定的,這一般也沒什么關(guān)系,如
public class Person implements Entity {
……
public Long save(String username, String password, String realName,
String email, String telephone, String mobileTelephone)
throws BusinessException {
Assert.notNull(realName, "Person的realName必須填寫。");
Assert.notNull(username, "Person的username必須填寫。");
if (!StringUtils.hasText(realName))
throw new BusinessException("用戶姓名必須包括有效字符。");
if (!StringUtils.hasText(username))
throw new BusinessException("用戶登錄名必須包括有效字符。");
SecurityExFacade securityExFacade =
SecurityExFacadeFactory.getInstance()
.createSecurityExFacade();
long userId = securityExFacade.createUser(username, password);
setUserId(userId);
setUsername(username);
setRealName(realName);
setEmail(email);
setTelephone(telephone);
setMobileTelephone(mobileTelephone);
// 檢查email是否已經(jīng)存在?如果存在禁止新建人員。
if (obtainDao().exists(email))
throw new BusinessException("電子郵件地址已經(jīng)存在。");
return (Long) obtainDao().save(this);
}
}
這里,在創(chuàng)建人員時(shí),調(diào)用安全管理組件的接口來創(chuàng)建一個(gè)系統(tǒng)用戶。安全組件接口相對(duì)穩(wěn)定,不會(huì)有太大的變化,因此實(shí)體在處理領(lǐng)域邏輯時(shí),直接使用接口就可
以。但有的時(shí)候,被調(diào)用者并不是很穩(wěn)定、或根本不知道被調(diào)用者是誰,這時(shí)就應(yīng)該使用發(fā)布訂閱模式或者此種情境下的領(lǐng)域事件模式。還是那么幸
運(yùn),Spring為我們提供了這個(gè)模式的支持。使用這種模式我們可以對(duì)調(diào)用者和被調(diào)用者進(jìn)行解耦。例如,在此處可以將黑色字體的代碼修改為:
UserCreatedEvent event = new UserCreatedEvent(username, password);
AppContext.getInstance().publishEvent(event);
long userId = event.getId();
在兩個(gè)組件的連接處編寫事件監(jiān)聽器,
public class UserEventListener implements ApplicationListener {
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof UserCreatedEvent) {
SecurityExFacade securityExFacade =
SecurityExFacadeFactory.getInstance()
.createSecurityExFacade();
UserCreatedEvent e = (UserCreatedEvent) event;
long userId = securityExFacade.createUser(e.getUsername(), e
.getPassword());
e.setId(userId);
}
}
}
然后,在Spring的配置文件中加入如下內(nèi)容,
這樣就可以以事件模式工作了,注意這里的事件模式是同步的。
領(lǐng)域事件模式可以用來解除耦合,但反對(duì)到處使用,如實(shí)體調(diào)用Dao或?qū)嶓w調(diào)用組件內(nèi)的實(shí)體。
2.7.??? 使用數(shù)據(jù)傳輸對(duì)象
幾年來我一直在將實(shí)體轉(zhuǎn)化為DTO還是不轉(zhuǎn)DTO之間徘徊。不轉(zhuǎn)DTO好處是非常用明顯的,服務(wù)可以直接將查詢結(jié)果拋給表現(xiàn)層,這樣既減少代碼量,又降低
了數(shù)據(jù)復(fù)制過程中新建對(duì)象的開銷。但問題也是存在的,首先,組件間調(diào)用不轉(zhuǎn)DTO是不行的,因?yàn)槿绻晦D(zhuǎn)相當(dāng)于將組件內(nèi)部的細(xì)節(jié)給公開了,實(shí)體可以公開被
訪問,實(shí)體上的方法也可以被訪問,這樣的風(fēng)險(xiǎn)很大,也不利于組件間的解耦。那么組件內(nèi)呢?其實(shí)實(shí)體也同樣暴露給了表現(xiàn)層,雖然是組件內(nèi),不同人開發(fā)不同層
時(shí),這種問題也很嚴(yán)重。另外,實(shí)體也可能被表現(xiàn)層開發(fā)人員作為表現(xiàn)層模型的一部分,即在action中聲明一個(gè)屬性,這個(gè)屬性就是實(shí)體對(duì)象,表現(xiàn)層用它收
集頁面提交的數(shù)據(jù)或向頁面展示數(shù)據(jù)。這時(shí)各自需求不同很難協(xié)調(diào)。所以,最后決定除非小項(xiàng)目,否則一定引入DTO對(duì)象。實(shí)體在轉(zhuǎn)換成DTO時(shí)使用
Assembler,如
public final class CommentDetailAssembler {
private CommentDetailAssembler() {
}
public static CommentDetail toDetail(Comment comment) {
if (comment == null)
return null;
String personName = null;
Assert.notNull(comment.getPersonId());
PersonDetail person = OrgUtils.getPerson(comment.getPersonId());
if (person != null)
personName = person.getRealName();
return new CommentDetail(comment.getId(), comment.getTaskId(),
comment
.getPersonId(), personName, comment.getComment(), comment
.getAddedDate());
}
public static List toDetails(List
comments) {
Assert.notNull(comments);
List cts = new
ArrayList(comments.size());
for (Comment ct : comments) {
cts.add(toDetail(ct));
}
return cts;
}
}
以上是在使用領(lǐng)域模型過程的一些簡單總結(jié),還有很多想說的,今天就寫這么多了。
總結(jié)
以上是生活随笔為你收集整理的java 域模型_基于Spring实现领域模型模式 - RUP实践者指南 - JavaEye技术网站的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 美国总统,国务卿给做“广告”,黑莓手机想
- 下一篇: 【OA】致远oa预写字段数值异常