锁定老帖子 主题:再次小结领域模型的种种观点
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2005-12-18
ajoo 写道 一个完整的annotation-free的解决方案。不喜欢factory的同学们可以看看这个方案如何:
http://docs.codehaus.org/display/YAN/All+About+Post+Instantiation 个人觉得最实用的是:不用给每个domain object type写一个bean。一个bean加一个aspect就可以包打天下: <function id="general_injection" params="domainobj"> <!-- use abean to auto-wire based on annotation. --> <abean component="$domainobj" autowire="bytype"/> </function> <injector class="DomainInjectionAspect" injection="$general_injection"/> 组件代码只要用一个annotation来标识出哪些property需要注入就万事大吉了。这要还不满意,我就没话说了。 |
|
返回顶楼 | |
发表时间:2005-12-18
引用 用一个annotation来标识出哪些property
一个annotation也不要可以嘛? 因为可以在外部进行描述或者autowire by name啊. |
|
返回顶楼 | |
发表时间:2005-12-19
nihongye 写道 引用 用一个annotation来标识出哪些property
一个annotation也不要可以嘛? 因为可以在外部进行描述或者autowire by name啊. 一个annotation不要的话当然可以,但是就基本只能manual wire。外部描述不就是manual-wire么? 这是因为无法区分哪些需要注入,哪些不需要。一般来说,象name, age, balance等等值都是由hibernate来设置,而一些service才是由容器设置。但是这个标准怎么定义有点不确定。容器通过什么标准来判断某个property不需要注入呢?annotation是一个方法,你觉得有没有其它的可行的方法呢? |
|
返回顶楼 | |
发表时间:2005-12-19
引用 容器通过什么标准来判断某个property不需要注入呢?
autowire啊,容器里bean的名称或类型跟domain object的属性一致就inject |
|
返回顶楼 | |
发表时间:2005-12-19
nihongye 写道 引用 容器通过什么标准来判断某个property不需要注入呢?
autowire啊,容器里bean的名称或类型跟domain object的属性一致就inject 也可以说是一法罢。这本来就支持的。 <function id="injection" param="obj"> <bean component="$obj" property-names="*" optional-properties="true" autowire="autodetect"/> </function> <injector class="GenericInjector" injection="$injection"/> 这就行了。不过一直并没有把它真正当作可行的一个方法,因为变数有点太大,系统不太好控制了。 |
|
返回顶楼 | |
发表时间:2005-12-19
robbin 写道 引用 这样的情况
Accounts.getAverageBalance() 比 AcountManager.getAverageBalance() 更好些, AccountManager更像是关于Account这个modul的业务脚本, 而Accounts是建立在这些业务脚本上更高一层次的OO抽象, 两个干的事差不多,但是概念不同, 当工具足够强大后,我们的domain modul就无须为事务的特点所妥协(会自动合并,设置事务边界), 事务应该散布在整个domain modul里。 经过wolfsquare的启发,我明白account的这些无状态的被动操作其实都是bank领域对象的主动业务逻辑方法。如果外沿无限扩大的话,我们总可以消除所有的无状态被动操作,让系统只剩下单一的领域对象和DAO接口。 叫Accounts确实比AccountManager来得舒服那么一点点,但是概念没有什么不同,都是用来分离bank的职责的。 现在spring的声明式事务已经强大到自动合并,设置事务边界了,事务确实可以散落在domain model里面,但是就算这样,你还是少不了Accounts。 引用 一、系统分析阶段 在这个阶段,毫无疑问,只有一个Account抽象领域模型。 二、编码阶段 在这个阶段,一个抽象的Account领域模型并不是只映射到一个Java Class上面去的,即使抛开DAO接口分离持久化细节的考虑之外,也不应该只得到一个Account Class。 因为针对Account抽象领域模型来说,有两类操作:一类是有状态的操作;另一类是无状态操作, 首先robbin的类划分有问题: 转账本身不是Account类的职责,应该是Accounts类的职责。 打个比方: 你是班里的学生,你有吃饭的撤责,班级作为一个对象是否要有吃饭的职责? 班级作为一个对象从一个角度看是一个集体对象(使学生的集合),从另外一个角度看它本身可能还有管理班级的职责。所以象计算平均账户余额这种工作是Accounts的职责,不是Account的职责。 另外采用有状态无状态的划分也是有问题的,为什么说转账无状态的?而取款是有状态的,以什么标准判断?其实有状态和无状态都是人为的认定的。在初期就以这个划分是没有道理的。 特别是一个类的静态方法却没有用到这个类的静态属性的时候,就要好好考虑一下这个方法放在这个类里面是否合适,比如 Account.Transfer(account1,account2);这个就是有问题的,一个类作了一件和它所有成员都不相关的事情?是不是很奇怪? -------------------------------------------------------------------- 需不需要service层? 我们来看一个现实生活中,报上海户口的一个例子:上海市政府的无数手续相信大家都能够理解,办事的手续有很多步骤:假设 先到部门A 再到部门B ,然后是C D E F. 那么可以看到ABCDEF 都是一个一个的对象,提供力所能及的服务,但是要办好户口,都需要这些部门参与。这里架设(其中一个部门的证明办不下来,户口就办不下来。整个回滚) 好了 这里有个问题,这些部门不是为了办户口而成立的,所以他们根本不知道自己会是办户口这个事务的一部分.所以这些部门对象不是办户口这个事务的管理者,只是参与者.谁是办户口事务的发起者? 想报户口的人? 好再问个问题:有了个新政策:如果卖了2000万的房子就给你报上海户口,那么报上海户口的事务就不再独立了.这个时候事务的起始点就发生变化了,可见"报上海户口"只能作为申请人的一个方法,也不能管理事务,它也不知道事务的真正发起者. 所以各个政府部门(domainObject)不是好的管理事务的地方。即使你成立个“户口办“这个部门也不能解决好事务这个问题[上面2000万的例子已经说明了]。 可见遇到事务这个问题 各个domainObject是无能为力的,为什么?事务本身是个过程化的概念:事务本身就是让一系列操作捆绑,要么都成功要么都失败。所以采用对象的方式无法解决好事务的问题。 很多人主张: 这个时候就需要"service"出场了"service" 就是采用面向过程的方式,将面向对象的那些对象组织起来,完成一件事情,它的目的就是做一件事情,它自己明确的知道一件事情的起点,终点. 重用率很低的的过程. 真正的对象告诉你,我能做什么,person :work eat sleep . 很显然 "service"告诉你:要做一件事,需要哪些步骤,从哪里开始,到哪里结束. 但是 "service"是可以慢慢向对象转化的,例如: 我成立个中介公司,专门负责和政府打交道,可以委托办理上海户口,北京户口,牛一点::美国护照,一站式服务!这个时候他就需要有员工(filed)和自己提供的一组和客户交互的接口(method) 但这个时候他就不能自己定义它一定是事务的起始点了,终止点了,他为了承接更多的业务,它就必须参与到更多的业务中去.你来我们公司可以只办护照,也可以专门为旅游公司的出国游业务服务的.它这个时候也就丧失了自己定义从哪里开始,到哪里结束的能力. 再来看看当我们submit一个页面,到下一个页面展示的过程是一个面向对象的过程么? 非也! 让我们看看web开发人员的视点来看问题: 客户提交了一个页面,里面有很多数据,我(action)把它们整理好,并且翻译成机器很看懂得方式,了解到用户给了我数据ABCD 他想做 evenA ,这个时候事情起点和终点都很清楚了为什么还需要service ? 直接调 domain就可以了! 为了复用?能复用的就应该提炼成对象,成立专门的部门,或中介组织了!(讷就是 domian了,就像上面说到的户口中介一样) -------------------------------------------------------------------- 这样过程还是action-->domain-->ado 事务还是在Action里面解决,就是规定事务的起点和终点而已.成立一个service不过是把action里面的几行代码拿到service里面 毫无意义! |
|
返回顶楼 | |
发表时间:2005-12-19
虽然你写了这么多,可是好像没有理解robbin的意思。
不知道以前讨论的一个帖子你看过没有,具体ID记不起了,大意就是o6z说任何事物都可使用名词法来分类,我想这个名词也就对应着“对象”这个概念。 所以,无论service怎么跑,都是跑不出领域对象的框的,始终存在一个更大的对象,把看起来没有状态的操作包起来。 Action里面解决事务也不是不可以,基本上也就是不那么OO而已,强耦合而已,比起service包办,更难向更OO的形式发展而已。。。 |
|
返回顶楼 | |
发表时间:2005-12-19
wolfsquare 写道 无论service怎么跑,都是跑不出领域对象的框的,始终存在一个更大的对象,把看起来没有状态的操作包起来。
注意你处理的应用程序肯定是有个范围的。 所谓“无论service怎么跑,都是跑不出领域对象的框的”太空了。能不能举个例子? |
|
返回顶楼 | |
发表时间:2005-12-20
我得做法:干脆不要DAO,在domain object 中注入一个EntityManager搞定增删改,以及简单的查找;把复杂的查询看做业务逻辑,放在service层去.
最近在一个小试验性项目中,在action的execute方法上通过annotation声明事务,用一个interceptor处理事务,基本上是action+domain object,当然还是会定制少量的service. 不过我还是认为,对于大项目,service还是必须的. |
|
返回顶楼 | |
发表时间:2005-12-21
可能是我表达得不够清楚,我的观点是:一切都是对象,
不存在不能被领域对象包含的方法。对外部调用者来说,只需要知道存在这个一个领域对象,就能进行相关操作,比起引入其他中间层次还要知道xxxservice,xxxdao来说,“知道即可用”是非常贴近人类思维的,也就是更OO的意思。 虽然理论上可以这样,但是要知道在领域对象业务概念还不是很确定的情况下,数据和操作是不断变化的,在这种情况下我认为贫血或者失血对象与service层分离是有必要的,该层存在后,可以是单独拿出来或是与domain object结合或是拿Service给domain object包一层都没有太大区别。单独存在这样一个业务层次对修改和扩充都比较有利,剩下的事情就仅仅是选择暴露该层给其他人与否的问题。 |
|
返回顶楼 | |