贫血模型or领域模型_第1页
贫血模型or领域模型_第2页
贫血模型or领域模型_第3页
贫血模型or领域模型_第4页
贫血模型or领域模型_第5页
已阅读5页,还剩10页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

最近 taowen 同学连续发起了两起关于贫血模型和领域模型的讨论 引起了大家 的广泛热烈的讨论 但是讨论 或者说是争论 的结果到底怎样 我想值得商榷 问题是大家对贫血模型和领域模型都有自己的看法 如果没有对此达到概念上 的共识 那么讨论的结果应该可想而知 讨论的收获也是有的 至少知道了分 歧的存在 为了使问题具有确定性 我想从一个简单例子着手 用我对贫血模 型和领域模型的概念来分别实现例子 至于我的理解对与否 大家可以做评判 至少有个可以评判的标准在这 一个例子一个例子 我要举的是一个银行转帐的例子 又是一个被用滥了的例子 但即使这个例子 也不是自己想出来的 而是剽窃的 中的例子 原谅我可怜 的想像力 当钱从一个帐户转到另一个帐户时 转帐的金额不能超过第一 个帐户的存款余额 余额总数不能变 钱只是从一个账户流向另一个帐户 因 此它们必须在一个事务内完成 每次事务成功完成都要记录此次转帐事务 这 是所有的规则 贫血模型贫血模型 我们首先用贫血模型来实现 所谓贫血模型就是模型对象之间存在完整的关联 可能存在多余的关联 但是对象除了 get 和 set 方外外几乎就没有其它的方 法 整个对象充当的就是一个数据容器 用 C 语言的话来说就是一个结构体 所有的业务方法都在一个无状态的 Service 类中实现 Service 类仅仅包含一 些行为 这是 Java Web 程序采用的最常用开发模型 你可能采用的就是这种方 法 虽然可能不知道它有个贫血模型的称号 这要多亏 Martin Flower 这个家伙惯会发明术语 包结构包结构 在讨论具体的实现之前 我们先来看来贫血模型的包结构 以便对此有个大概 的了解 贫血模型的实现一般包括如下包 dao 负责持久化逻辑 model 包含数据对象 是 service 操纵的对象 service 放置所有的服务类 其中包含了所有的业务逻辑 facade 提供对 UI 层访问的入口 代码实现代码实现 先看 model 包的两个类 Account 和 TransferTransaction 对象 分别代表帐 户和一次转账事务 由于它们不包含业务逻辑 就是一个普通的 Java Bean 下面的代码省略了 get 和 set 方法 Java 代码 1 public class Account 2 private String accountId 3 private BigDecimal balance 4 5 public Account 6 public Account String accountId BigDecimal balance 7 this accountId accountId 8 this balance balance 9 10 getter and setter 11 12 Java 代码 1 public class TransferTransaction 2 private Date timestamp 3 private String fromAccountId 4 private String toAccountId 5 private BigDecimal amount 6 7 public TransferTransaction 8 9 public TransferTransaction String fromAccountId String toA ccountId BigDecimal amount Date timestamp 10 this fromAccountId fromAccountId 11 this toAccountId toAccountId 12 this amount amount 13 this timestamp timestamp 14 15 16 getter and setter 17 这两个类没什么可说的 它们就是一些数据容器 接下来看 service 包中 TransferService 接口和它的实现 TransferServiceImpl TransferService 定 义了转账服务的接口 TransferServiceImpl 则提供了转账服务的实现 Java 代码 1 public interface TransferService 2 TransferTransaction transfer String fromAccountId String t oAccountId BigDecimal amount 3 throws AccountNotExistedException AccountUnderflow Exception 4 Java 代码 1 public class TransferServiceImpl implements TransferService 2 private AccountDAO accountDAO 3 private TransferTransactionDAO transferTransactionDAO 4 5 public TransferServiceImpl AccountDAO accountDAO 6 TransferTransactionDAO transferTransactionDAO 7 this accountDAO accountDAO 8 this transferTransactionDAO transferTransactionDAO 9 10 11 12 public TransferTransaction transfer String fromAccountId S tring toAccountId 13 BigDecimal amount throws AccountNotExistedExceptio n AccountUnderflowException 14 Validate isTrue pareTo BigDecimal ZERO 0 15 16 Account fromAccount accountDAO findAccount fromAccoun tId 17 if fromAccount null throw new AccountNotExistedExc eption fromAccountId 18 if fromAccount getBalance compareTo amount 0 12 13 this accountId accountId 14 this balance balance null BigDecimal ZERO bala nce 15 16 17 public String getAccountId 18 return accountId 19 20 21 public BigDecimal getBalance 22 return balance 23 24 25 public void debit BigDecimal amount throws AccountUnderflo wException 26 Validate isTrue pareTo BigDecimal ZERO 0 27 28 if overdraftPolicy isAllowed this amount 29 throw new AccountUnderflowException this amount 30 31 balance balance subtract amount 32 33 34 public void credit BigDecimal amount 35 Validate isTrue pareTo BigDecimal ZERO 0 36 37 balance balance add amount 38 39 40 与贫血模型的区别在于 Account 类中包含业务方法 credit debit 注意没有 set 方法 对 Account 的更新是通过业务方法来更新的 由于 不允许从帐户 取出大于存款余额的资金 是一条重要规则 将它放在一个单独的接口 OverdraftPolicy 中 也提供了灵活性 当业务规则变化时 只需要改变这个 实现就可以了 TransferServiceImpl 类 Java 代码 1 public class TransferServiceImpl implements TransferService 2 private AccountRepository accountRepository 3 private TransferTransactionRepository transferTransactionRe pository 4 5 public TransferServiceImpl AccountRepository accountReposit ory 6 TransferTransactionRepository transferTransactionRe pository 7 this accountRepository accountRepository 8 this transferTransactionRepository transferTransactio nRepository 9 10 11 public TransferTransaction transfer String fromAccountId S tring toAccountId 12 BigDecimal amount throws AccountNotExistedExceptio n AccountUnderflowException 13 Account fromAccount accountRepository findAccount fro mAccountId 14 if fromAccount null throw new AccountNotExistedExc eption fromAccountId 15 Account toAccount accountRepository findAccount toAcc ountId 16 if toAccount null throw new AccountNotExistedExcep tion toAccountId 17 18 fromAccount debit amount 19 toAccount credit amount 20 21 accountRepository updateAccount fromAccount 对 Hibernate 来说这不是必须的 22 accountRepository updateAccount toAccount 对 Hibernate 来说这不是必须的 23 return transferTransactionRepository create fromAccount Id toAccountId amount 24 25 26 与贫血模型中的 TransferServiceImpl 相比 最主要的改变在于业务逻辑被移 走了 由 Account 类来实现 对于这样一个简单的例子 领域模型没有太多优 势 但是仍然可以看到代码的实现要简单一些 当业务变得复杂之后 领域模 型的优势就体现出来了 优缺点优缺点 其优点是 1 领域模型采用 OO 设计 通过将职责分配到相应的模型对象或 Service 可以很好的组织业务逻辑 当业务变得复杂时 领域模型显出巨大的优 势 2 当需要多个 UI 接口时 领域模型可以重用 并且业务逻辑只在领域层中 出现 这使得很容易对多个 UI 接口保持业务逻辑的一致 从领域模型的 分层图可以看得更清楚 其缺点是 1 对程序员的要求较高 初学者对这种将职责分配到多个协作对象中的方 式感到极不适应 2 领域驱动建模要求对领域模型完整而透彻的了解 只给出一个用例的实 现步骤是无法得到领域模型的 这需要和领域专家的充分讨论 错误的 领域模型对项目的危害非常之大 而实现一个好的领域模型非常困难 3 对于简单的软件 使用领域模型 显得有些杀鸡用牛刀了 我的看法我的看法 这部分我将提出一些可能存在争议的问题并提出自己的看法 软件分层软件分层 理解软件分层 明晰每层的职责对于理解领域模型以及代码实现是有好处的 软件一般分为四层 分别为表示层 应用层 领域层和基础设施层 软件领域 中另外一个著名的分层是 TCP IP 分层 分为应用层 运输层 网际层和网络接口 层 我发现它们之间存在对应关系 见下表 TCP IPTCP IP 分层分层 软件分层软件分层 表示层表示层 负责向用户显示信息 应用层应用层 负责处理特定的应用程序细节 如 FTP SMTP 等协议 应用层应用层 定义软件可以完成的工作 指挥领域层的对象来解决 问题 它不负责业务逻辑 是很薄的一层 运输层运输层 两台主机上的应用程序提供端到端的 通信 主要包括 TCP UDP 协议 网际层网际层 处理分组在网络中的活动 例如分组 的选路 主要包括 IP 协议 领域层领域层 负责业务逻辑 是业务软 件的核心 网络接口层网络接口层 操作系统中的设备驱动程序和计算机 中对应的网络接口卡 它们一起处理 与电缆 或其他任何传输媒介 的物 理接口细节 基础设施层基础设施层 为上层提供通用技术能力 如消息发送 数据持久化 等 对于 TCP IP 来说 运输层和网际层是最核心的 这也是 TCP IP 名字的由来 就像领域层也是软件最核心的一层 可以看出领域模型的包结构与软件分层是 一致的 在软件分层中 表示层 领域层和基础设施层都容易理解 难理解的 是应用层 很容易和领域层中 Service 混淆 领域 Service 属于领域层 它需 要承担部分业务概念 并且这个业务概念不易放入模型对象中 应用层服务不 承担任何业务逻辑和业务概念 它只是调用领域层中的对象 服务和模型 来 完成自己的功能 应用层为表示层提供接口 当 UI 接口改变一般也会导致应用 层接口改变 也可能当 UI 接口很相似时应用层接口不用改变 但是领域层 包 括领域服务 不能变动 例如一个应用同时提供 Web 接口和 Web Service 接口时 两者的应用层接口一般不同 这是因为 Web Service 的接口一般要粗一些 可 以和 TCP IP 的层模型进行类比 开发一个 FTP 程序和 MSN 聊天程序 它们的应 用层不同 但是可以同样利用 TCP IP 协议 TCP IP 协议不用变 与软件分层 不同的是 当同样开发一个 FTP 程序时 如果只是 UI 接口不同 一个是命令行 程序 一个是图形界面 应用层不用变 利用的都是 FTP 服务 下图给出领 域模型中的分层 RepositoryRepository 接口属于领域层接口属于领域层 可能有人会将 Repository 接口 相当于贫血模型中的 DAO 接口 归于基础设施 层 毕竟在贫血模型中 DAO 是和它的实现放在一起 这就涉及 Repository 接 口到底和谁比较密切 应该和 domain 层比较密切 因为 Repository 接口是由 domain 层来定义的 用 TCP IP 来类比 网际层支持标准以太网 令牌环等网 络接口 支持接口是在网际层中定义的 没有在网际层定义的网络接口是不能 被网际层访问的 那么为什么在贫血模型中 DAO 的接口没有放在 model 包中 这是因为贫血模型中 DAO 的接口是由 service 来定义的 但是为什么 DAO 接口 也没有放在 service 包中 我无法解释 按照我的观点 DAO 接口放在 service 包中要更好一些 将 DAO 接口放在 dao 包或许有名称上对应的考虑 对于领域 模型 将 Repository 接口放入 infrastructure 包中会引入包的循环依赖 Repository 依赖 Domain Domain 依赖 Repository 然而对于贫血模型 将 DAO 接口放入 dao 包中则不会引入包循环依赖 只有 service 对 DAO 和 model 的依赖 而没有反方向的依赖 这也导致 service 包很不稳定 service 又正 是放置业务逻辑的地方 JDepend 这个工具可以检测包的依赖关系 贫血模型中贫血模型中 FacadeFacade 有何用 有何用 我以前的做一个项目使用的就是贫血模型 使用了 service 和 facade 当我们 讨论 service 和 facade 有什么区别时 很少有人清楚 最终结果 facade 就是 一个空壳 它除了将方法实现委托给相应的 service 方法 不做任何事 它们 的接口中的方法都一样 Facade 应该是主要充当远程访问的门面 这在 EJB 时 代相当普遍 自从 Rod Johson 叫嚷 without EJB 之后 大家对 EJB 的热情降了 很多 对许多使用贫血模型的应用程序来说 facade 是没有必要的 贫血模型 中的 service 在本质上属于应用层的东西 当然如果确实需要提供远程访问 那么远程 Facade 或许叫做 Remote Service 更好 也是很有用

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论