关于java:DDD之贫血模型与充血模型

62次阅读

共计 2440 个字符,预计需要花费 7 分钟才能阅读完成。

| 前言

要想深刻把握和理解 DDD 畛域驱动设计的外围,那无论如何也绕不开两大较为形象的概念——“贫血模型”、“充血模型”:

  • 贫血模型即事务脚本模式。
  • 充血模型即畛域模型模式。

| 贫血模型

贫血模型最早广泛应用源于 EJB2,最强盛时期则是由 Spring 发明,将:

  • “行为”(逻辑、过程);
  • “状态”(数据,对应到语言就是对象成员变量)。

拆散到不同的对象中:

  • 只有状态的对象就是所谓的“贫血对象”(常称为 VO——Value Object);
  • 只有行为的对象就是,咱们常见的 N 层构造中的 Logic/Service/Manager 层(对应到 EJB2 中的 Stateless Session Bean)。

——已经 Spring 的作者 Rod Johnson 也抵赖,Spring 不过是在因循 EJB2 时代的“事务脚本”,也就是面向过程编程。

贫血畛域模型是一个存在已久的反模式,目前仍有许多拥趸者。

Martin Fowler 已经和 Eric Evans 聊天谈到它时,都感觉这个模型仿佛越来越风行了。作为畛域模型的推广者,他们感觉这不是一件坏事。

贫血畛域模型的基本特征是:它第一眼看起来还真像这么回事儿。我的项目中有许多对象,它们的命名都是依据畛域来的。对象之间有着丰盛的连贯形式,和真正的畛域模型十分类似。但当你检视这些对象的行为时,会发现它们基本上没有任何行为,仅仅是一堆 getter/setter。

其实,这些对象在设计之初就被定义为只能蕴含数据,不能退出畛域逻辑;逻辑要全副写入一组叫 Service 的对象中;而 Service 则构建在畛域模型之上,须要应用这些模型来传递数据。

这种反模式的恐怖之处在于:它齐全和面向对象设计南辕北辙。 面向对象设计主张将数据和行为绑定在一起,而贫血畛域模型则更像是一种面向过程设计,Martin Fowler 和 Eric 在 Smalltalk 时就竭力拥护这种做法。更蹩脚的是,很多人认为这些贫血畛域对象是真正的对象,从而彻底误会了面向对象设计的涵义。

现在,面向对象的概念曾经流传得很宽泛了,而要拥护这种贫血畛域模型的做法,还须要更多论据。贫血畛域模型的基本问题是,它引入了畛域模型设计的所有老本,却没有带来任何益处。最次要的老本是将对象映射到数据库中,从而产生了一个 O /R(对象关系)映射层。

只有当你充沛应用了面向对象设计来组织简单的业务逻辑后,这一老本才可能被对消。如果将所有行为都写入到 Service 对象,那最终你会失去一组事务处理脚本,从而错过了畛域模型带来的益处。正如 martin 在企业应用架构模式一书中说到的,畛域模型并不一定是最好的工具。

将行为放入畛域模型,这点和分层设计(畛域层、长久化层、展示层等)并不抵触。因为畛域模型中放入的是和畛域相干的逻辑——验证、计算、业务规定等。如果你要探讨是否将数据源或展示逻辑放入到畛域模型中,这就不在本文阐述范畴之内了。

一些面向对象专家的观点有时会让人产生纳闷,他们认为确实应该有一个面向过程的服务层。然而,这并不意味着畛域模型就不应该蕴含行为。事实上,service 层须要和一组富含行为的畛域模型联合应用。

Eric Evans 的 Domain Driven Design 一书中提到:

应用层(即 Service 层)

形容应用程序所要做的工作,并调度丰盛的畛域模型来实现它。这个档次的工作是形容业务逻辑,或和其它我的项目的应用层做交互。这层很薄,不蕴含任何业务规定或常识,仅用于调度和派发工作给下一层的畛域模型。这层没有业务状态,但能够为用户或程序提供工作状态。

畛域层(或者叫模型层)

示意业务逻辑、业务场景和规定。该档次会管制和应用业务状态,即便这些状态最终会交由长久化层来存储。总之,该层是软件外围。

服务层很薄——所有重要的业务逻辑都写在畛域层。他在服务模式中复述了这一观点:现在人们常犯的谬误是不愿花工夫将业务逻辑放到适合的畛域模型中,从而逐步造成面向过程的程序设计。

我不分明为什么这种反模式会那么常见。我狐疑是因为大多数人并没有应用过一个设计良好的畛域模型,特地是那些以数据为核心的开发人员。此外,有些技术也会推动这种反模式,比方 J2EE 的 Entity Bean,这会让我更偏向于应用 POJO 畛域模型。

总之,如果你将大部分行为都搁置在服务层,那么你就会失去畛域模型带来的益处。如果你将所有行为都放在服务层,那你就无可救药了。

长处

简略:

  • 对于只有大量业务逻辑的利用来说,应用起来十分天然;
  • 开发迅速,易于了解;
  • 留神:也不能齐全排挤这种形式。

毛病

无奈良好的应答简单逻辑:

  • 比方支出确认规定发生变化,例如在 4 月 1 号之前签订的合同要应用某规定……
  • 和欧洲签订的合同应用另外一个规定……

| 充血模型

面向对象设计的实质是:“一个对象是领有状态和行为的”。

比方一个人:

  • 他眼睛什么样鼻子什么样这就是状态;
  • 人能够去打游戏或是写程序,这就是行为。

为什么要有一个“人 Manager”这样的货色存在去帮人“打游戏”呢?举个简略的 J2EE 案例,设计一个与用户(User)相干性能。

传统的设计个别是:

  • 类:User+UserManager;
  • 保留用户调用:userManager.save(User user)。

充血的设计则可能会是:

  • 类:User;
  • 保留用户调用:user.save();
  • User 有一个行为是:保留它本人。

其实它们没有什么特地实用的方向,集体更偏向于总是应用充血模型,因为 OOP 总是比面向过程编程要有更丰盛的语义、更正当的组织、更强的可维护性—当然也更难把握。

因而理论工程场景中,是否应用,如何应用还依赖于设计者以及团队充血模型设计的了解和把握,因为当初绝大多数 J2EE 开发者都受贫血模型影响十分深。另外,理论工程场景中应用充血模型,还会碰到很多很多细节问题,其中最大的难关就是“如何设计充血模型”或者说“如何从简单的业务中拆散出恰到好处且蕴含语义的逻辑放到 VO 的行为中”。

如果一个对象蕴含其余对象,那就将职责持续委托上来,由具体的 POJO 执行业务逻辑,将策略模式更加细粒度,而不是写 if else。

正文完
 0