Skip to content

四火的唠叨

一个纯正程序员的啰嗦

Menu
  • 所有文章
  • About Me
  • 关于四火
  • 旅行映像
  • 独立游戏
  • 资源链接
Menu

贫血模型和充血模型

Posted on 11/28/201110/08/2024 by 四火

这两个概念是早些时候 Martin Fowler 总结出来的两种常见模型设计类型,没有说谁好谁不好,为不同的模型类别选择合适的场景是设计者的工作。没有工具本身的问题,只有工具使用者的问题。

 

 

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

 image

 

优点是系统的层次结构清楚,各层之间单向依赖,Client->(Business Facade)->Business Logic->Data Access Object。可见,领域对象几乎只作传输介质之用,不会影响到层次的划分。

 

该模型的缺点是不够面向对象,领域对象只是作为保存状态或者传递状态使用,它是没有生命的,只有数据没有行为的对象不是真正的对象,在 Business Logic 里面处理所有的业务逻辑,对于细粒度的逻辑处理,通过增加一层 Facade 达到门面包装的效果。

 

在使用 Spring 的时候,通常暗示着你使用了贫血模型,我们把 Domain 类用来单纯地存储数据,Spring 管不着这些类的注入和管理,Spring 关心的逻辑层(比如单例的被池化了的 Business Logic 层)可以被设计成 singleton 的 bean。

 

假使我们这里逆天而行,硬要在 Domain 类中提供业务逻辑方法,那么我们在使用 Spring 构造这样的数据 bean 的时候就遇到许多麻烦,比如:bean 之间的引用,可能引起大范围的 bean 之间的嵌套构造器的调用。

 

贫血模型实施的最大难度在于如何梳理好 Business Logic 层内部的划分关系,由于该层会比较庞大,边界不易控制,内部的各个模块之间的依赖关系不易管理,可以考虑这样这样的实现思路:

  • (1)铺设扁平的原子业务逻辑层,即简单的 CRUD 操作(含批量数据操作);
  • (2)特定业务清晰的逻辑通过 Facade 层来组装原子操作实现。
  • (3)给业务逻辑层实施模块划分,保持模块之间的松耦合的关系。

 

举例说明:

原子业务逻辑层(Service)提供了用户模型的条件查询方法:

List<User> queryUser(Condition con)

Facade 层则提供了一种特定的业务场景的分子接口,满足 18 岁的中国公民,内部实现调用的正是上述的原子接口:

List<User> queryAdultChinese()

Facade、Service 层纵向划分为几个大的领域包:用户、内容和产品。

 

 

充血模型层次结构和上面的差不多,不过大多业务逻辑和持久化放在 Domain Object 里面,Business Logic 只是简单封装部分业务逻辑以及控制事务、权限等,这样层次结构就变成 Client->(Business Facade)->Business Logic->Domain Object->Data Access Object。

image

 

它的优点是面向对象,Business Logic 符合单一职责,不像在贫血模型里面那样包含所有的业务逻辑太过沉重。

 

缺点是如何划分业务逻辑,什么样的逻辑应该放在 Domain Object 中,什么样的业务逻辑应该放在 Business Logic 中,这是很含糊的。即使划分好了业务逻辑,由于分散在 Business Logic 和 Domain Object 层中,不能更好的分模块开发。熟悉业务逻辑的开发人员需要渗透到 Domain Logic 中去,而在 Domian Logic 又包含了持久化,对于开发者来说这十分混乱。  其次,如果 Business Logic 要控制事务并且为上层提供一个统一的服务调用入口点,它就必须把在 Domain Logic 里实现的业务逻辑全部重新包装一遍,完全属于重复劳动。

 

使用 RoR 开发时, 每一个领域模型对象都可以具备自己的基础业务方法,通常满足充血模型的特征。充血模型更加适合较复杂业务逻辑的设计开发。

 

充血模型的层次和模块的划分是一门学问,对开发人员要求亦较高,可以考虑定义这样的一些规则:

  • (1)事务控制不要放在领域模型的对象中实现,可以放在 facade 中完成。
  • (2)领域模型对象中只保留该模型驱动的一般方法,对于业务特征明显的特异场景方法调用放在 facade 中完成。

 

万事都不是绝对的,也有一些看起来不易解决的问题。例如,考虑到性能的需要,我需要一次查询出满足某种条件的用户和某种条件的产品,他们二者之间通过订购关系关联起来,可能发现这种情形下,上述的模型层次划分变得无解了……

 

怎么办呢?包括以上种种,欢迎大家讨论。

 

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》

×Scan to share with WeChat

你可能也喜欢看:

  1. 对象转换的问题
  2. 编程范型详解
  3. Issue record: “No thread for socket” about Memcached
  4. Singletons are Evil?
  5. J2EE 核心模式学习理解和记录

2 thoughts on “贫血模型和充血模型”

  1. 白条 says:
    12/14/2013 at 6:12 PM

    我有个疑问,在充血模型的架构中,业务规则的校验应该如何实现,放在领域模型中吗?你有什么好的思路呢?

    Reply
  2. sp42 says:
    01/06/2013 at 9:12 AM

    学习!看完楼主的这篇文章,解答了我心中很多的疑问!

    Reply

Leave a Reply to sp42 Cancel reply

Your email address will not be published. Required fields are marked *

订阅·联系

四火,啰嗦的程序员一枚,现居西雅图

Amazon Google Groovy Hadoop Haskell Java JavaScript LeetCode Oracle Spark 互联网 亚马逊 前端 华为 历史 同步 团队 图解笔记 基础设施 工作 工作流 工具 工程师 应用系统 异步 微博 思考 技术 数据库 曼联 测试 生活 眼界 程序员 管理 系统设计 缓存 编程范型 美股 英语 西雅图 设计 问题 面向对象 面试

分类

  • Algorithm and Data Structure (30)
  • Concurrency and Asynchronization (6)
  • System Architecture and Design (43)
  • Distributed System (18)
  • Tools Frameworks and Libs (13)
  • Storage and Data Access (8)
  • Front-end Development (33)
  • Programming Languages and Paradigms (55)
  • Testing and Quality Assurance (4)
  • Network and Communication (6)
  • Authentication and Authorization (6)
  • Automation and Operation Excellence (13)
  • Machine Learning and Artificial Intelligence (6)
  • Product Design (7)
  • Hiring and Interviews (14)
  • Project and Team Management (14)
  • Engineering Culture (17)
  • Critical Thinking (25)
  • Career Growth (57)
  • Life Experience and Thoughts (45)

推荐文章

  • 聊一聊分布式系统中的时间
  • 谈谈分布式锁
  • 常见分布式系统设计图解(汇总)
  • 系统设计中的快速估算技巧
  • 从链表存在环的问题说起
  • 技术面试中,什么样的问题才是好问题?
  • 从物理时钟到逻辑时钟
  • 近期面试观摩的一些思考
  • RSA 背后的算法
  • 谈谈 Ops(汇总 + 最终篇):工具和实践
  • 不要让业务牵着鼻子走
  • 倔强的程序员
  • 谈谈微信的信息流
  • 评审的艺术——谈谈现实中的代码评审
  • Blog 安全问题小记
  • 求第 K 个数的问题
  • 一些前端框架的比较(下)——Ember.js 和 React
  • 一些前端框架的比较(上)——GWT、AngularJS 和 Backbone.js
  • 工作流系统的设计
  • Spark 的性能调优
  • “残酷” 的事实
  • 七年工作,几个故事
  • 从 Java 和 JavaScript 来学习 Haskell 和 Groovy(汇总)
  • 一道随机数题目的求解
  • 层次
  • Dynamo 的实现技术和去中心化
  • 也谈谈全栈工程师
  • 多重继承的演变
  • 编程范型:工具的选择
  • GWT 初体验
  • java.util.concurrent 并发包诸类概览
  • 从 DCL 的对象安全发布谈起
  • 不同团队的困惑
  • 不适合 Hadoop 解决的问题
  • 留心那些潜在的系统设计问题
  • 再谈大楼扔鸡蛋的问题
  • 几种华丽无比的开发方式
  • 我眼中的工程师文化
  • 观点的碰撞
  • 谈谈盗版软件问题
  • 对几个软件开发传统观点的质疑和反驳
  • MVC 框架的映射和解耦
  • 编程的未来
  • DAO 的演进
  • 致那些自嘲码农的苦逼程序员
  • Java 多线程发展简史
  • 珍爱生命,远离微博
  • 网站性能优化的三重境界
  • OSCache 框架源码解析
  • “ 你不适合做程序员”
  • 画圆画方的故事

近期评论

  • Ticket: TRANSACTION 1.922915 BTC. Go to withdrawal >> https://yandex.com/poll/enter/BXidu5Ewa8hnAFoFznqSi9?hs=20bd550f65c6e03103876b28cabc4da6& on 倔强的程序员
  • panshenlian.com on 初涉 ML Workflow 系统:Kubeflow Pipelines、Flyte 和 Metaflow
  • panzhixiang on 关于近期求职的近况和思考
  • Anonymous on 闲聊投资:亲自体验和护城河
  • 四火 on 关于近期求职的近况和思考
  • YC on 关于近期求职的近况和思考
  • mafulong on 常见分布式基础设施系统设计图解(四):分布式工作流系统
  • 四火 on 常见分布式基础设施系统设计图解(八):分布式键值存储系统
  • Anonymous on 我裸辞了
  • https://umlcn.com on 资源链接
© 2025 四火的唠叨 | Powered by Minimalist Blog WordPress Theme