锁定老帖子 主题:再次小结领域模型的种种观点
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2005-12-16
To downpour:
doLogin放到service这种方法的褒贬我没什么看法 但是 user.logon() 这种模式更能被我的所在team所理解,无他仅此而已 同时 domain object也是主要活跃于service层的,和构架没有什么关系。 你的关于层次架构方面的观点我无异议,我也是这样做的 cheers. |
|
返回顶楼 | |
发表时间:2005-12-16
to: robbin
引用 三、充血模型
充血模型和第二种模型差不多,所不同的就是如何划分业务逻辑,即认为,绝大多业务逻辑都应该被放在domain object里面(包括持久化逻辑),而Service层应该是很薄的一层,仅仅封装事务和少量逻辑,不和DAO层打交道。 请注意你的这句话:"不和DAO层打交道". 让后请看http://jroller.com/page/habuma?entry=spring_2_0_vs_the中, public class CustomerServiceImpl implements CustomerService { public CustomerServiceImpl(); {} public void copyAndTweakCustomer(Integer id); { Customer original = dao.load(id);; Customer duplicate = new Customer();; duplicate.setId(original.getId(); + 1);; duplicate.setName(original.getName(););; original.setName("Michael Walls");; original.save();; duplicate.save();; } private CustomerDao dao; public void setDao(CustomerDao dao); { this.dao = dao; } } 这里还有这么一句: 引用 You'll notice that the service object is also injected with a CustomerDao. This is so that it can load a previously-persisted Customer. This illustrates an important point: Just because the domain object is now primarily responsible for its own persistence, that does not mean that service objects can't also access the DAO to load previously-persisted instances. 似乎service 还和DAO层打交道. |
|
返回顶楼 | |
发表时间:2005-12-16
Readonly 写道 robbin 写道 好吧,那么我是web程序员,我从页面传一个id过来,要先load一下该Account,显示account的信息。那么请问:你提供给我什么API?是
account.getAccountById(id); 还是 accountService.getAccountById(id); 还是 accountDao.getAccountById(id); ? new Account(id); 俄没接触过对象数据库. 假如我用的是对象数据库,无疑new Account(id);是适合的. o\r maping o\o maping |
|
返回顶楼 | |
发表时间:2005-12-17
Partech,看到这么一些代码:
public class AccountService { private AccountDAO dao; public void createAccount(Account ac);{ dao.save(ac);; } public void removeAccount(Account ac);{ dao.remove(ac);; } public void updateAccount(Account ac);{ dao.update(ac);; } } 我不觉得它有多可爱,我横看竖看,前看后看,左看右看,上看下看,可还是看不出这个Service除了为获得事务外,他还做了什么东西。 如果他有一些逻辑方法里面包含一些有意义的业务逻辑,那我无话可说,但是我所看到很大一个比例的方法都是这样类似的delegate的代码。 很多人还喜欢先写Service的Interface ,再来写这些delegate代码,看着更是感到可爱。 你认为我的理解有什么不对吧? 我很乐意你能够指出我的错误... |
|
返回顶楼 | |
发表时间:2005-12-17
一个完整的annotation-free的解决方案。不喜欢factory的同学们可以看看这个方案如何:
http://docs.codehaus.org/display/YAN/All+About+Post+Instantiation |
|
返回顶楼 | |
发表时间:2005-12-17
downpour 写道 我是比较同意robbin的观点的,举个登录的例子:
EmployeeService的实现: /* (non-Javadoc); * @see com.bearingpoint.gdc.service.EmployeeService#doLogin(java.lang.String, java.lang.String); */ public Employee doLogin(String name, String password); throws ObjectNotFoundException, PasswordNotMatchException { Employee employee = null; List result = employeeDAO.getEmployeeByName(name);; if(result.size(); == 0); throw new ObjectNotFoundException();; else { employee = (Employee);result.get(0);; if(!employee.getPassword();.equals(password);); throw new PasswordNotMatchException();; } return employee; } DAO的实现: /* (non-Javadoc); * @see com.bearingpoint.gdc.dao.EmployeeDAO#getEmployeeByName(java.lang.String); */ public List getEmployeeByName(String name); { String querySentence = "FROM Employee employee WHERE employee.name = :name"; Query query = getSession();.createQuery(querySentence);; query.setParameter("name", name);; return query.list();; } Action更简单: /* (non-Javadoc); * @see com.opensymphony.xwork.Action#execute(); */ public String execute(); throws Exception { employee = employeeService.doLogin(employee.getName();, employee.getPassword(););; //put the employee into session ServletActionContext.getContext();.getSession();.put("currentEmployee", employee);; return SUCCESS; } 结构层次很清晰,DAO就负责数据库操作,Service层处理逻辑。万一你的业务逻辑发生扩展和变化,也只要修改和扩展你的Service实现,与其他层次无关。同时,Service层总是接受事务的控制。 按照firebody的观点,莫非我的doLogin的逻辑要写在我的Employee类里面?然后new Employee(String name, String password) throw xxxException?这太恶心了。 我仅仅说了1,你就用你自己的思路往前说出了3,4,5.... 。而这些并不是我所说的,按照你的思路下的产物也不一定是我所说的思路的结果。 另外指出一点: employee.login() 这个方法为什么要放在Employee身上? Rich Domain Model != All is Entity Model . 希望你能够理解我的意思,SecurityService 一样会存在,也一样会在他身上存在验证用户权限的方法 , 还有另外: 看到你的这行代码: if(!employee.getPassword();.equals(password);); throw new PasswordNotMatchException();; 不如改成这个吧: employee.authenticate(password); 贫血模型用多了,看来很多代码也会变味的。 赫赫。 |
|
返回顶楼 | |
发表时间:2005-12-17
firebody 写道 Partech,看到这么一些代码:
public class AccountService { private AccountDAO dao; public void createAccount(Account ac);{ dao.save(ac);; } public void removeAccount(Account ac);{ dao.remove(ac);; } public void updateAccount(Account ac);{ dao.update(ac);; } } 我不觉得它有多可爱,我横看竖看,前看后看,左看右看,上看下看,可还是看不出这个Service除了为获得事务外,他还做了什么东西。 如果他有一些逻辑方法里面包含一些有意义的业务逻辑,那我无话可说,但是我所看到很大一个比例的方法都是这样类似的delegate的代码。 很多人还喜欢先写Service的Interface ,再来写这些delegate代码,看着更是感到可爱。 你认为我的理解有什么不对吧? 我很乐意你能够指出我的错误... 嗯,如果你的ServiceLayer接口里传递的参数包含DomainObject那么,ServiceLayer确实少了很多职责。UI层直接修改DomainObject是否也算是业务逻辑呢?那么UI层还能叫UI层吗?业务层还能叫业务层吗? Service被看作Domain层和UI层的接口/外观,会具有适配两者的职责,通常我们不会把DomainObject直接传递到UI层,总是使用DTO,目前看看使用通用的SDO行不行,甚至异常也需要转换。如果使用Aspect来管理事务,Service里面的代码就很干净了。 对于查询操作: public class AccountService{ private IAccountRepository accountRepository; private IAccountInfoAssembler accountInfoAssembler; public AccountInfo getAccountInfo(String accountCode);{ Account account = accountRepository.findByCode(accountCode);; return accountInfoAssembler.create(account);; } } 对于更新操作: public class AccountService{ private IAccountRepository accountRepository; private IAccountInfoAssembler accountInfoAssembler; public void createAccount(String Code,String Name,...);{ IAct act = new CreateAccountAct(code,name,....);; act.run();; } } 异常转换的处理和事务控制都可以通过Aspect达成。 |
|
返回顶楼 | |
发表时间:2005-12-17
partech 写道 嗯,如果你的ServiceLayer接口里传递的参数包含DomainObject那么,ServiceLayer确实少了很多职责。 Service被看作Domain层和UI层的接口/外观,会具有适配两者的职责,通常我们不会把DomainObject直接传递到UI层,总是使用DTO,目前看看使用通用的SDO行不行,甚至异常也需要转换。如果使用Aspect来管理事务,Service里面的代码就很干净了。 对于查询操作: public class AccountService{ private IAccountRepository accountRepository; private IAccountInfoAssembler accountInfoAssembler; public getAccountInfo(String accountCode);{ Account account = accountRepository.findByCode(accountCode);; return accountInfoAssembler.Create(account);; } } 对于更新操作: public class AccountService{ private IAccountRepository accountRepository; private IAccountInfoAssembler accountInfoAssembler; public createAccount(String Code,String Name,...);{ IAct act = new CreateAccountAct(code,name,....);; act.run();; } } 异常转换的处理和事务控制都可以通过Aspect达成。 哦,你所说的DTO--->Service --->Domain Model。 Service的职责确实多了,这样的Service确实有存在的必要的。 不过流行的ognl可以很容易的消除DTO,这样你需要写的代码会更少。 不过要在项目中将ognl融合进来,还需要一定精巧的设计。 webwork可是作了很多工作的,从你提到的思路来看,其实ww的Controller将承担了你所提到的Service一部分职责。 ognl是可以很好的用在Swing UI上面的。 |
|
返回顶楼 | |
发表时间:2005-12-17
如果不需要DTO,而是直接透传DomainObject,DomainObject可以在UI层更改。
我的问题是如何界定DomainObject那些属性,可以在UI改,那些必须放到业务层更改?区分的原则是什么? 如果我可以在UI中更改DomainObject,是否我可以把所有的有关更改DomainObject的业务逻辑放到UI?同时我是否可以把所有需要检查的Rule对象也传递到UI。那么业务层中还能剩下什么?业务层还能叫业务层吗? |
|
返回顶楼 | |
发表时间:2005-12-17
firebody 写道 Partech,看到这么一些代码:
public class AccountService { private AccountDAO dao; public void createAccount(Account ac);{ dao.save(ac);; } public void removeAccount(Account ac);{ dao.remove(ac);; } public void updateAccount(Account ac);{ dao.update(ac);; } } 我不觉得它有多可爱,我横看竖看,前看后看,左看右看,上看下看,可还是看不出这个Service除了为获得事务外,他还做了什么东西。 如果他有一些逻辑方法里面包含一些有意义的业务逻辑,那我无话可说,但是我所看到很大一个比例的方法都是这样类似的delegate的代码。 很多人还喜欢先写Service的Interface ,再来写这些delegate代码,看着更是感到可爱。 你认为我的理解有什么不对吧? 我很乐意你能够指出我的错误... 这样的代码的确不怎么可爱,除了获得事务,它也的确什么都没有做。但是问题是,绝大多数的情况,逻辑方法中会包含一些有意义的逻辑。 比如说,在save之前,看看它在数据库中是否已经存在了。难道你不认为这是一种逻辑嘛? 登录的例子是我随手写的,我承认employee.authenticate(password)更加OO。而事实上,很多状态维护的代码也应该被写在domain object里面。 像Hibernate里面的父子关系的例子中,为父亲添加一个儿子,总是这么写: public void addChild(Child c); { c.setParent(this);; children.add(c);; } 像这种丝毫不依赖于持久化的代码,我绝对会放在Parent对象里面,而不是Service层。 |
|
返回顶楼 | |