贫血模型与充血模型

来源:互联网 发布:人类与人工智能 编辑:程序博客网 时间:2024/04/27 09:50
 

贫血模型与充血模型再讨论

         Martin Fowler很早以前就写过一篇文章,题目叫"贫血模型"。文章里面批判贫血的领域模型是不够优雅、不够OO的,提倡使用充血的领域模型。在Java世界里这是一直争论的话题。到底什么是贫血什么是充血呢?


  贫血模型 :是指领域对象里只有get和set方法,或者包含少量的CRUD方法,所有的业务逻辑都不包含在内而是放在Business Logic层。

      优点是系统的层次结构清楚,各层之间单向依赖,Client->(Business Facade)->Business Logic->Data Access(ADO.NET)。当然Business Logic是依赖Domain Object的。似乎现在流行的架构就是这样,当然层次还可以细分。

      该模型的缺点是不够面向对象,领域对象只是作为保存状态或者传递状态使用,所以就说只有数据没有行为的对象不是真正的对象。在Business Logic里面处理所有的业务逻辑,在POEAA(企业应用架构模式)一书中被称为Transaction Script模式。


  充血模型 :层次结构和上面的差不多,不过大多业务逻辑和持久化放在Domain Object里面,Business Logic只是简单封装部分业务逻辑以及控制事务、权限等,这样层次结构就变成Client->(Business Facade)->Business Logic->Domain Object->Data Access。
      它的优点是面向对象,Business Logic符合单一职责 ,不像在贫血模型里面那样包含所有的业务逻辑太过沉重。
      缺点是如何划分业务逻辑,什么样的逻辑应该放在Domain Object中,什么样的业务逻辑应该放在Business Logic中,这是很含糊的。即使划分好了业务逻辑,由于分散在Business Logic和Domain Object层中,不能更好的分模块开发。熟悉业务逻辑的开发人员需要渗透到Domain Logic中去,而在Domian Logic又包含了持久化,对于开发者来说这十分混乱。  其次,因为Business Logic要控制事务并且为上层提供一个统一的服务调用入口点,它就必须把在Domain Logic里实现的业务逻辑全部重新包装一遍,完全属于重复劳动。

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

比较好的文章 2

         最近一直在学习领域驱动设计(DDD )的理论知识,从网上搜集了一些个人认为比较有价值的东西,贴出来和大家分享一下:

我一直觉得不要盲目相信权威,比如不能一谈起领域驱动设计,就一定认为国外的那个Eric Evans 写的那本书中的一些概念就一定是正确的,认为领域驱动设计就一定是聚合,聚合根,实体,值对象等概念。我们要有自己的思想,要有自己判断真正的领域模型该是什么样子的勇气和追求。

1.   " 领域驱动设计 " = “问题域模型驱动领域建模 ” + “ 领域建模驱动软件实现

2.   问题域建模的过程就是业务领域分析的过程,对于企业而言就是业务架构的分析和建立过程,这里不包含任何OO 的设计成分,主要从组织、流程和业务能力三个维度来分析业务。

3.   记住很多模式没有什么用处,带着问题在模式中寻找答案才是正确的使用方式,让那种解决方案的思想融入到你的模型当中,然后彻底地忘掉那些所谓的模式名词。  

4.   好的领域建模应该具有柔性,能够伴随着用户一起成长。  

5.   这让我意识到业务建模应该回归自然:一谈起来建模技术,就离不开国外提出的OO EDA 之类的东西,其实我们的老祖宗早就有了 摸脉 的说法,现在的 SOA ESB 之类的东西是不是就像打造一个企业的 神经脉络 ,而 “OO” 是不是就像 神经元 ,它们之间的通讯就是靠生物电脉冲,这就是消息驱动。  

6.   《领域驱动设计》一书中只是强调了业务的水平分割,然而在大项目里还有垂直分割,注意垂直分割不完全等同于包的划分。目前有一种非常错误的做法,就是一上来就开始对象建模,然后再进行归类划分模块;正确的做法应该是前期以确认领域边界功能为主,后期以确认领域内的对象模型为主。关于领域的切分,《领域驱动设计》没有过多谈及,其实方法就是不断对企业业务知识的学习和分析。当你对一个业务认识不清的时候,最好的办法就是不同企业环境下去分析这个业务,那这个业务的所有发展变化就清楚了,这就像那些生物学家总是喜欢通过长期的野外考查来学习知识。这个工作做好了,项目就成功了50%

7.   领域的边界就是服务,也是对外提供服务的唯一入口。领域服务和领域对象模型是一个业务领域的2 个不同侧面。领域服务强调是从外向内看,反映了 外部对业务领域的使用功能 ;领域对象模型强调业务领域就像一个独立的具有一定自主能力的生命体,反映了 业务领域的内部运行机制 。领域对象模型的功能是不能对外暴露的,不然会造成外部对领域对象的耦合。  

8.   不要一说 面向关系设计 就是错的。因为用户的角度看,业务本来就是面向关系和过程的,这非常自然;而从设计看,业务是不同主体在相互作用。这就是为什么越靠近用户的地方面向关系和过程的设计味道越浓的原因了。  

9.   “ 类和职责 的叫法让我总感觉比较僵化,像是没有生命的死细胞一样,觉得这是一种西医模式。我更崇尚一种中医模式,强调建模是动态的、基于场景交互的,应该用更自然的还原业务本来面目的眼光去审视建模过程,也就是说 有机的业务建模 实际上就是 技术建模 的问题域建模,而 技术建模 只是 业务建模 的技术落地而已。

10. 关于建模工具,像 用例图,流程图,状态图之类 的并不是我理想的建模工具,虽然他们确实能表达一些东西。我理想的业务建模工具应该是能从角色(组织或者人)、流程、业务能力三个维度立体地、动态地分析描述业务模型,希望可以是一个动态的3D 视图和流图,并可以按不同的维度分析展开。  

11. 那对于我们做企业业务建模,终极目标应该是个什么样子呢?我认为这个终极目标一定是非常复杂的(也就是我原来常说的大项目场景),因为只有在复杂的场景下才能真正检验各种建模技术的偏颇。想想看,我们的建模目标应该向谁学习。论坛也有人说过, 自然的就是最好的 。是啊,经历过亿万年才进化出来的模型难道不值得我们学习吗,难道不是我们的目标模型吗!呵呵,答案已经呼之欲出了,就是 仿生物建模 。是的,没错,如果说利用我们的建模技术能够去构建出一个复杂的仿真人,具备人的一些特征和功能的话,那这种建模技术就是完美的。  

12. 企业的业务建模可以分为两个层面, 宏观建模 微观建模 宏观建模 是指首先要对企业做一个整体的信息化规划,对企业进行整体的的业务架构建模,其成果就是业务组件。其中的方法论可以参照 IBM CBM ,不过 IBM 好像也只是咨询,真正的落地还要靠自己对CBM 的领悟。 Evans DDD主要属于 微观建模 部分。对于 微观建模 ,我认为分为 2个方面: 结构化建模 行为建模 ,这是一体两面。我觉得 Evans DDD 总结了几个关键的要素:实体、值对象、聚合、工厂和存储,但其中还少了一个非常重要和关键的要素: 事件

13. 众所周知,人体是由很多细胞构成的,那细胞之间是如何作用的呢,其实就是 刺激 响应 。其中 刺激 就是 事件 ,所以 事件 是业务模型本来就应该具备的要素,而不是什么技术层面的东西。从 事件 角度看, 职责的本质就是事件的响应   

14. “ 结构化建模 是指建模中除了静态的实体和值对象的结构关系外,从 事件 角度看,实体或者值对象还具备一些 本能的反应 ,比如 " 手指会弯曲 " 。而 行为建模 是指通过神经中枢(消息总线)来控制不同对象的本能反应来完成一个复杂的组合,比如" 用手弹钢琴 "   

15.  “ 一上来先识别类,然后就考虑怎么分配职责 的做法是一种本末倒置的僵化的静态建模。这种方式往往让人落入一种只注重 而忽略 (业务本来的面目)的怪圈。我认为建模分为 2 个阶段,第一个是 有机的业务建模 ,第二个是 技术建模 业务建模 技术建模 的区别是: 业务建模 完全是业务语言,不含任何技术成分,比如 类、值对象和职责 的概念,在 技术建模 中才涉及那些概念。 业务建模 做好了, 技术建模 就成了自然而然的东西了。
    “ 有机的业务建模 的一个非常重要的特征就是 有机 ,就是强调业务模型中的每一个 业务主体 都是一个生命体。这些业务主体都有 生命特征 ,在一定条件下受到外界的刺激就会发生一定的行为。这些业务主体构成了整个业务模型。当然,这些业务主体的规模和层次不同:比较大的业务主体就是 领域主体 ,他有明确的领域边界;而领域主体的对外界响应其实是通过内部的 业务核心主体 的协作来完成的,有的看见的见摸得着 ———— 具有业务实体的特点,有的比较抽象 ———— 控制规则和流程,当然有的 业务核心主体 可能是个多核细胞。 有机的业务建模 有一个非常重要的工作就是 检查业务模型的生命健康状况 。需要注意的是这个业务建模可能是通过不完善的用户需求建立起来的,所以我们需要构建多种场景通过不同的刺激去看这个领域主体内部的响应中是否有 异常情况 ,如果有那就要开刀了。
     关于 技术建模 ,我认为 对内功能和对外功能要分开 ,领域对象不建议同时承担对内和对外的功能,但小项目除外。服务对外要封装和隐藏领域内部的东西,提供的是服务接口;同时服务要负责领域内部对象的行为组装。从另外一个层面看,服务是需求,领域对象模型是实现。值对象的本质就是反映业务主体的不同业务特征,可能是业务主体的临时状态(比如用户的发帖数)或者属于另一业务主体的状态值。从微观看,聚合是交互最紧密的业务对象的封装,从宏观看,聚合是一个具有明确业务边界的独立业务核心主体。所以聚合只是形式,业务模型的正确建立才是决定要素。  

16. 相互作用原则全面、深刻地揭示了事物之间的因果联系,是因果关系在逻辑上的充分展开。在客观世界的普遍联系链条中,原因和结果经常互移其位、相互转化。受原因作用的事物在发生变化的同时也反作用于原因,从而把因果性关系转变为相互作用的关系。其中每一方都作为另一方的原因并同时又作为对立面的反作用的结果表现出来。整个物质世界就是各种物质存在普遍相互作用的统一整体, 相互作用是事物的真正的终极原因 , 在它之外没有也不可能有使它运动和发展的原因。相互作用也是系统内部诸要素的关系和联系的形式。要素之间相互作用的方式构成系统存在的基础,系统中要素的相互作用是决定系统发展方向的因素。相互作用只有借助于特殊的物质载体才能实现,相互作用的内容取决于组成要素的物质层次和性质。例如,现代生物学把相互作用划分为分子的、细胞的、器官的、机体的、种的、生物圈等不同水平的形式。社会生活是最复杂的相互作用的形式。  相互作用是客观的、普遍的。具体的相互作用是整个物质世界相互作用链条的环节和部分,相互作用的普遍性和绝对性通过无限多样的具体的相互作用而体现出来。相互作用是事物的属性、结构、规律存在和发展的条件。相互作用范畴具有重要的方法论意义。认识事物意味着认识它们的相互作用,要揭示事物的本质属性,就必须研究事物之间具体的相互作用的特殊性。相互作用的实质是矛盾以及矛盾诸方面的相互依存和斗争。在诸多因素的相互作用中,必有一种起着主导的决定的作用。在实际工作中,只有认清事物之间相互作用的特点和规律性,才能认识和把握事物的本质。 “ 事件 这个词的确有技术范畴的嫌疑,但说到 相互作用 输入和输出 ,大家都会明白的。过去的 OO过于僵化、教条,偏重于静态场景,忽略了客观世界运动和相互作用的本质规律,而最近出现新技术和对 OO的反思 正在回归自然

17. “ 世界是有事物及其活动组成 或者 世界由事物及其相互作用组成 是非常朴素、直观的世界观。  

18. 建议学习 经过亿万年进化而来的客观存在的生物控制模型 ,我们都知道 人体是通过神经网络来控制不同的肌肉、骨骼来做出各种复杂的动作的 刺激和响应 是客观存在的,而 场景 只是我们对客观世界的主观认识。 刺激和响应 举个例子,人的最外层的皮肤就如同 门面 ,用来接受外界的 刺激 ,内部再由 大脑 下达指令,指挥各个 肢体 进行 响应 神经网络 是被这层皮肤覆盖加以保护。 神经元 皮肤 注册可能完成的 响应 ,而 皮肤 接受 刺激 后由大脑进行反馈而回调 神经元”,“ 神经元 再完成各个指令动作。  

19. 业务建模应该遵守客观规律。  

20. DDD 强调 充血模型 。在复杂业务中,这很有用,可以提高对象的可复用性。但是要注意, 充血模型 虽然很好,但其背后却隐藏玄机。复杂场景中,如果所有的行为都放到了对象中,那领域对象就会变得沉重无比,带来各种副作用。同时,描述状态的值对象往往不是一蹴而就,而是充满着变化,难以把握。当业务不明时," 贫血模型 " 容易把握,这也就是为什么现在 充血模型 不多,而 " 贫血模型 " 大行其道的原因。  

21. DDD 认为 "领域对象应该是有行为的 " ,大家都认可。如果说 " 贫血模型 " 造成了对象和行为的分离的话;那么 " 充血模型" 也是有问题的,因为 DDD中简单地认为把行为放到对象中就行了,殊不知这其实是关于对象和行为之间的一种静态地僵化的看法。而 DCI对这种思想进行了修正,认为对象在场景中才具有行为。对于 DCI中的对象(数据),我认为 DCI中的对象(数据)是一种裸对象,但却不是简单意义上的 " 贫血对象" DCI 中的对象和行为是一种动态关系,是依赖于场景而存在的。这也就是说对象不会无缘无故的产生行为,(呵呵,那是神经病),对象具有行为一定是有意义的。也许有人担心场景会变成" 贫血对象 " 模式下 service那样的噩梦,造成对象和行为的分离。其实不用担心,在 DCI中可以用 " 事件 " 把对象和行为联系在一起就避免了 " 贫血对象 " 的问题。呵呵,这是不是应了中国的一句老话:" 分久必合,合久必分 "   

22. 对于 DDD中的聚合,我也有不同的理解。其实 DDD提出聚合的概念是为了保证领域内对象之间的一致性问题。但是我对 DDD中对 " 聚合 " 这个概念的落地形式表示质疑,DDD 特别强调聚合根的封装性,然而这可能会导致领域内对象之间的逻辑强耦合。也许有人说领域内部的对象是高内聚的,这样做没关系。但是在实战中,领域模型内部的值对象往往存在着变数,这是我们认识客观世界的必然规律。然而这会导致领域模型的不稳定性。所以我认为及时领域内部的对象也应该注意低耦合,这个问题同样需要靠事件来解决,事件才是保证领域对象一致性的关键。  

23.  一致性 究竟是什么呢,我认为 一致性 是事物相互作用的本质内在联系,也就是在一定场景下外界刺激在沿着一定路径传递而导致一系列对象的变化。所以 外界不可以绕过根实体直接修改状态 并不能反应这一本质,因为外界刺激并不全都是先作用在根对象上面的。在我看来,这种非本质的封装反而会造成耦合,尤其是采用 直接调用 的形式。应该说, 直接调用 是造成对象耦合最大根源,因为 直接调用 是在强调对象的上下级关系,这很生硬。如果我们换一种方式,用一种平等的心态去看待对象间作用关系,用 告诉我做什么 的方式而让对象间解耦。  

24. 在思考语言范式时,我曾这样想过,面向对象,行为与属性绑得太紧,面向过程,行为与属性放得的太松。但这里不是仅仅选择 那么简单, 贫血模型 充血模型 实际上与 面向过程 面向类(对象) 的矛盾是相似的。

贫血对象 是将 行为 属性 完全放开的一种表达,而 充血对象 则又矫枉过正,把 行为与属性 绑得太紧。

类是表达共性的概念,而对象则是充满个性,而且这些个性是依赖场景的,离开场景将失去意义。所以,在 充血模型 中,用类表达对象时,实际是将 个性 统统视为 共性 ,在任何具体的场景中,对象的角色或职责都已经定义好了,这显然是不合适,因为一个对象可以多种角色参与不同的活动或场景(可能使得类继承体系非常庞大和复杂),而且在参与新的活动或者场景时,以 继承 的方式定义对象则更是力不从心。

而在 贫血模型 中,则将 共性 统统视为 个性 ,这是抹掉 共性 的做法,与 充血模型 抹掉 个性 的做法刚好相反。前者是 白马非马 ,后者则是 白马即马 。都没有协调好 共性 个性 的关系。

因此真正自然的 领域模型 应该是这样的,如果对象的某些行为在任何场景都是通用的,那么就放在领域中去,将其绑定,这是尊重 共性 的约束;如果对象的某些依赖于具体的场景,那么则在具体的场景中注入相应的行为,赋予对象相应的角色,这是尊重 个性 的自由。所以,对象的行为该不该放入 领域模型 ,我们要先分析一下这些行为是对象所固有的,还是依赖于场景的,如果是固有的,即是共性的,就放入领域模型(domain) ,如果不是则延迟在具体的场景( service )中注入,赋予其角色的个性(DCI)

25. 设计模式可以在领域模型中使用 (domain) ,也可以在具体业务场景 (service) 中使用。设计模式是在局部、微观层面的一种支持变化的机制,在具体业务场景中使用再合适不过了。将来可能会出现的现象是,在领域层(domain) 各个模型中用的更多是 结构型 模式,而在业务层或服务层 (service) 的各个场景中用得更多的是 行为型 模式,两者都可以使用 创建型 模式。

26. 不要把业务和领域等同,业务是客观存在,领域是主观认识。  

27. 领域和场景是不同的。领域是宏观层面的,场景是微观层面的,虽然它们有相似点。
如果可以用一个领域模型来描述 企业 的所有业务,我当然希望是这样的,但问题是有谁可以做到,那就是共产主义了,完全可以凭此打败微软、IBM 之类的公司成为老大了。领域 有界无边 ,说起来拗口,简单说领域虽然有界,但其边界时可以扩展的,领域虽然有界,但其边界是无形的。比如说音乐界不断发展,但界还是存在,虽然界也是一种主观认识的划分。因为音乐界不能独立于人类的所有活动。  

28.  领域的边界是由用户的需求来决定的,用户的需求是动态可扩展,所以领域的边界也如此。所谓领域语言,就是从有所需的用户心中提炼出来的。用户需求相对于领域来说是一个 客观存在

29. 领域虽然是对客观存在主观上一种划分,但不是一成不变的。一个领域模型是根据业务(用户需求)对领域的一种素描,业务不同,素描也就不同。  

30 . 我们应该学会逆向思考:
1.服务是什么?自己在过去的项目中用过服务吗?如果不用,会怎么样?如果用了,用好了吗,出现了
DDD指出的问题吗?
2.聚合是什么?为什么要用聚合?自己用过聚合吗?DDD的聚合采取的形式是什么?除了
DDD中的聚合,还有别的聚合吗?
   DDD其他的要素可以以此类推。其实DDD不太适合初学者和小项目,DDD是经历过很多项目的失败和成功才总结出来的。所以我是反对把DDD当成一种理论学习的,在实战中不懂得东西不要乱用,但是一定要感悟,也就是说你可以先不去用DDD,但可以在实践中感悟DDD的思想和要素,然后明悟真伪。所以我发了这么多帖子,希望大家从自己的实战去印证,共鸣。这里不是没事无聊的闲谈,而是基于实践的关于DDD的灵魂的交流。  

31 . DDD是对一些复杂 IT 项目成功要素的总结而形成的一种理论。正确地理解DDD 是关键,只要成功运用DDD 的思想和要素就可以了,绝不是照搬。就算没见过DDD ,也可能在实践中运用过DDD 的思想和要素。  

32. 领域建模与 设计模式的思想不是只有高手才能领悟,就像数学与哲学思想不是只由数学家和哲学家才能领悟。思想与知识是两个相关又不同的范畴,建模思想与技术知识/ 经验同样如此。不要因为新手知识的不足,而认为新手就不能领悟思想。如果出现这种情况,我只有两种理解: a) 这个老手不懂; b )这个老手懂,但不想告诉你,可能出于自私,也可能觉得你还不适合。  

33. 不同于 设计模式的关注点在如何设计模型以支持需求的变化,分析模式的关注点如何将需求转化为模型。 Martin Fowler的《分析模式 -- 可复用的对象模型》、 Peter Coad的《对象模型 -- 策略、模式、应用》等等关于分析模式的著作,都尚未拜读,加上经验实在匮乏,所以本不想说。可是对于Peter Coad 的《 Java Modeling in Color with UML》一书中的 四色模型,一见如故,极其简单的 四色模型,在我心里已经将其作为自己关于分析模式的核心认知,这里说说自己对 四色原型的理解(再次感谢 Jdon 让我遇见了 四色模型)。

四色模型中有四个最核心的概念(刚好 设计模式 也是运用了OO 的四个基本概念 无巧不成书呀 ), 分别是 MI(Moment Interval) Role PPT(Party Place Thing) Desc(Description), 不同 设计模式运用的 OO Abstraction Encapsulation Inheritance Polymorphism 四个基本概念在中文有很好的翻译 抽象、封装、继承、多态。这四个词,直译起来,似乎没什么感觉:瞬间- 间隔时间、角色、聚会场所 - 物件、描述。查阅一些资料,其描述我不是十分满意,这里我尝试用简短的言语解释其本质:MI 描述活动, Role 代表参与活动的事物; PPT 代表未参与活动的事物, Desc 描述事物。举个例子,比如一件商品,商品本身就是PPT ,商品的类别信息、厂家等商品的元信息就是 Desc ,如果这件商品为顾客所购买,此件商品就变成了Role (当然顾客此时也是 Role) ,而顾客购买商品这个活动就是MI
世界可以简单理解为由事物及其运动 (MI)构成,事物又有动静之分,动即参与活动之事物 (Role) ,静即未参与活动之事物 (PPT) ,此外,可能还需要描述事物的信息 (Desc) 。为了将原型形象化,我们可以根据色彩的冷暖动静之感,将活动(MI) 涂上活跃的红色,将参与活动的事物 (Role) 涂以暖色黄色,将未参于活动的事物(PPT) 涂以安静的绿色,将描述事物的信息 (Desc) 涂以冷色蓝色。这样四色 原型就诞生了,通过四色原型,我们可以像孩子拼接积木一样,一块一块地拼接出需求模型的原型了。

34. 分析模式将需求转化为原型,设计模式支持需求模型的变化,而实现模式为何而生?实现模式关注的是代码本身,我们在分析和设计上付出的努力,终究要落实在代码上。分析和设计再好的模型,也可能被充满smell 的代码掩盖或破坏。 Kent Beck 在《实现模式》一书提出了77 个实现模式, 6 条原则和 3种价值观。 77 个实现模式涉及一些具体的代码规范的建议,暂且不论; 6条原则,局部化影响、最小化重复、将逻辑与数据捆绑、对称性、声明式表达和变化率,也暂且不议; 3 种价值观:沟通、简单、灵活,我也只想说说其中的一点:沟通,因为个人认为这是实现模式最根本的意义所在。Kent Beck 在书中说:过了 20 年,把别人当作空气一样的编程方式才在我眼中褪尽了颜色。作为一位programmer ,代码的作用不仅在于与机器交流,更重要的是与别的 programmer交流,将 沟通 的价值观贯彻到代码的编写中,是值得每个programmer 坚持去做的事情。 上面所说的三本很薄的书,可以作为关于分析、设计、实现模式的认知基础,值得收藏。  

  35. “领域相对于用户需求是主观的”,这句话隐含了一个假设或参照,我们对领域认识的来源于用户需求,可是用户需求来自哪里呢?用户需求同样来自于领域,来自于对领域的诉求!之前我的另一个观点,“领域是对用户需求的素描”,这句话同样隐含了一个前提,用户需求接近于领域的本质,我们对领域的认识可以通过对用户需求进行素描而实现。这里我重新确定领域、领域建模、用户需求三者的关系。

1)领域是客观的。
2)领域模型是主观的,体现了程序员对领域的认识,是程序员心中对领域的素描。
3)用户需求是主观的,体现了用户对领域的认识,是用户心中对领域的素描。
4)这点非常重要,就是“领域模型”与“用户需求”的关系,要展开来讲。“用户需求”是对领域的“素描”,用户的需求来自对领域的“诉求”,这些诉求往往是深刻的,因为其来源于用户对领域长期观察和使用的经验,比起我们程序员,一般更完整、更真实地接近领域的本质。我们对“用户需求进行素描”,就是“借鉴用户的宝贵经验”,可以更快、更好地素描客观领域,这可以说是一条认识未知领域的捷径。但是当用户需求不明朗或不清晰时,我们需要超越“用户需求”,对领域进行深入的摸索,去寻求更清晰的视角,对领域进行刻画。

 

最后再来回答一个问题,就是关于人(参与者)是否应该包含在领域模型之内?

领域模型(程序员对领域的认识)要包容用户需求(用户对领域的认识),这就像一个杯子要装满一杯水,我们在制作杯子时,制作的是空杯子,即要把水倒出来,之后才能装下水;再比如,一座房子要住人,我们在建造房子时,建造的房子是空的,唯有空的才能容乃人的居住。从这个意义上来说,领域模型要将用户(参与者)排除在外,这是《老子》的“无之以为用”观点的一种应用。

36.  对于对象职责的分配以前看过一本书<<UML和模式应用>>这本书将的非常好。里面有GRASP原则--通用职责分配软件模式(General Responsibility Assignment Software patterns),它里面讲了以下几个模式:
1) 创建者(Creator) :
决定对象应该有谁来创建的问题。一般情况下是包含类创建被包含的类,比如Order创建OrderLine等。
2) 信息专家(Information expert):
用此模式来确定如何给对象分配职责的问题。一般把职责分配给那些包含此职责有关信息的对象。这样也体现了高内聚性模式。这里面其实也是将行为和对象的数据统一起来,分配某个对象职责,就看看当前的对象里面有没有完成此职责的数据,如果有就可以分配职责。
3) 低耦合(Low coupling)
面向对象的设计,讲究对外封装,对内解耦,一个模块或者一个类,我们暴漏给外界的接口是粗粒度的,经过封装的,而模块或者对象内部需要进行细分,进行解耦,类与类,模块与模块之间耦合度越低,那么可扩展性就越好,维护起来也容易,不会造成牵一发而动全身的局面。
如何使得类于类之间低耦合呢,我觉得迪米特法则非常重要,迪米特法则就是通常说的“不要和陌生人说话”,一般类的行为主要可以由以下几种形式来实现: 
>>方法参数中的对象,通过调用方法参数中对象方法来实现职责
>>对象自身的方法,通过对象自身的方法实现职责
>>对象内部聚合的对象,通过对象内部聚合的方法实现职责。
>>方法创建或者实例化的对象方法,通过对象自身创建的对象的方法实现职责
以上四种方式都是我们可以接受的,下面这种方式,我们一般要避免,比如a.methodA().methodB(),调用者对象调用了methodA方法返回的对象的方法,这样就会使得调用对象和methodB所在对象发生了耦合,此时我们可以完全将methodB的调用封装在a里,那么此时调用者就只与a 通信,而不是和a,以及methodB所在对象都耦合在一起。
4) 控制器(Controller).
控制器模式主要解决将系统事件处理的职责分配谁的问题,控制器模式指出将处理系统事件的职责分配给控制器类,比如MVC模式中的C就属于这种模式,还比如Struts中的 ActionServlet这个Front controller(前段控制器 J2EE核心模式一书).
5) 高内聚(High Cohesion)
一个类如果是高内聚的,那么类和类之间也将变的松耦合,如果类和类之间耦合太多,那么势必类就丧失了内聚性,因此我们在分配职责的时候,高内聚和低耦合是相辅相成的。
6) 多态性(polymorphism)
多态性就是
OO中的多态,多态其实也是一种具体依赖抽象原则的体现,比如我们有一个产品有个打折策略,而打折策略非常多,那么这个时候怎么办呢?我们当然是定义一个抽象的打折接口,然后将不同的打折策略实现这些接口,而不是让每个不同的打折策略具有不同的接口。
7) 纯虚构(pure fabrication)
我们在面向对象的分析和设计当中,难免会遇到一些行为不能很好的分配给实体或者domain model,那么这个时候就需要虚构出一些概念,比如
DDD中的Repository就属于一种纯虚构的东东。
8) 间接性(indirection)
间接性从表面就可以知道它是为了解耦的,比如RBAC,通过引入Role将User和Permission解耦,这样通过引入一个间接的对象,使得原来紧耦合的对象变的松耦合,这样也方便维护,同时也是为低耦合原则服务的。
9) 防止变异(protected variations)

这个原则我们也经常遇到,按照我的理解,它应该是封装,类似于DDD中的防腐层,软件系统中的子模块或者子系统应该将不确定的因素封装在内部,而不要因为子模块而影响到外部系统。其实这也是一种封装思想的体现。  

==========

另外的 解释

困境

框架的约束

如Robin所言

robin 写道
如果你用的是Spring,没啥说的,必须贫血,你想充血也充不起来;
如果你用的是RoR,也没啥说的,直接充血,你想贫血也未必贫得下来;


这就是一个基本事实。Spring作者也坦言(Rod Johnson, JAOO, 2006),Spring的编程模型基本上是EJB的延续。从架构和分层的角度,它们是一脉相承的。这种分层的架构决定了,行为在Service里,数据 在Entity里。这种做法成为“最佳实现”,不是偶然的,是框架设计给你的必然结果。矛盾的集中体现在于Entity无法被注入(当然你可以注入,但是 这是不推荐的做法)。Spring后来尝试修复这个问题,引入了AspectJ来做Entity的注入。不过仍然不是主流,因为框架的阻碍只是一个小问 题,更大的问题在于这个Java的OOP实现本身就有问题。

语言的约束
在很多的讨论中,反对“充血”领域模型的同志都提到。把逻辑集中到领域模型中,会造成类的膨胀。在我个人的实践中,近千行的Entity类定义也是有的。而且这些行为往往不稳定,往往流程高度相关,复用程度并不是想象的那么高。
原因是因为,数据并没有所谓的内在行为。你不用它,它就没有行为。所以数据有什么行为,其实是使用者赋予的。而同样的数据往往会在不同的场合(context)下被使用。正如这位同志所言:

coolnight 写道
我们的系统有很多模块组成, 各模块基本上通过数据库来共享信息。
主要的模块大致有: 核心系统(非web), 网站、 bbs、 网站后台,核心系统后台,BI, 推广员等等
原来的打算是写个rich domain model供各模块来使用,以便代码重用减轻个模块开发的工作量
一个简单的例子, User 原有 changePassword, getFriends, addFriend ... 等等方法撇开配置以及获取User对象的复杂性不谈, 实际开发中发现, 这些东西重用的地方太少了,对网站来说很好的rich domain model, 在网站后台里面就麻烦,很多方法在那里根本就是对后台开发人员的干扰,而很多方法对核心系统、BI等等根本就毫无用处。


所以上面所画的图就得改一下了:



同样,很多同志也发现了这个问题,并给出了解决方案

Quake Wang 写道
用mixin实现领域模型是最简洁的做法, 但是受到Java语言的限制,得用大量mark性质的interface和composite模式,反而会让整个结构变得复杂。
相比C#的namespace或ruby自定义dsl(acts_as_xxx),在java玩领域模型需要更多的功力,推荐一下Rickard Öberg的qi4j,它将composite oriented发挥到了极致:
http://www.qi4j.org/

 

ray_linn 写道
我建议 用C#的扩展方法,或者Java用mimix(不知道打错没有),Domain还是POJO,方法在适当的时候“黏合”(C#通过 namespace引用)到POJO上,让POJO “充血”,而在充当VO,DTO的时候,POJO又可以“放血”回到“贫血的”状态上。


这个问题基本上是暴露了Java的OOP实现的缺陷。C#的Partial Class, Extension Method和Ruby的Mixin就是对这种“一个class定义一切”的做法的改进,允许行为被分片定义,而不是集中定义在一个文件中。

 

原创粉丝点击