词条统计
浏览次数:4457 次
编辑次数:3次 历史版本
最近更新:2013/6/20
创建者:掷鸡蛋者

本文原是论坛帖子,讨论见此处:http://www.wojilu.com/Forum1/Topic/167 


本文主题带有争议性,纯主观看法,敬请分辨。

什么叫充血模型?简单的说,就是在模型中带上方法。
什么叫贫血模型?简单地说,就是模型中只有属性,没有方法。

到底要不要使用充血模型这个问题,往往和另外一个问题紧密相关,就是系统如何分层,该分几层。本文第一部分先谈充血模型和简化分层的益处,第二部分再补充贫血模型的不可抹杀的优点,试图对这个问题的两个方面有一个比较完善的回答。

第一部分:充血模型的必要性和简化分层

一种观点认为系统设计应该多多分层,因为只有这样,系统的各部分职责才能更加明确,才能更加容易扩展和切换,比如表现层+业务逻辑层service层+DAO层等。

但是分层分到什么地步才是恰当的?这才是最实际的问题,不是一个僵化的规则可以限定的。典型的观点是Martin Fowler访谈中提到的:

“记者:问您一个具体的技术问题。在很多企业应用中,人们都会开发一个数据访问层,这是为什么呢?为什么不直接在业务逻辑层中存取数据库?
MF:我本人喜欢在不同的地方作不同的事情。业务逻辑是一回事,存取数据库是另外一回事,所以它们不应该纠缠在一起。这是主要的理由。还有一个次要的理由,设计一个数据访问层,一旦数据库发生变化,移植起来会快得多。
记者:但是这样的设计也有缺点。当我们要改动数据表结构时,往往需要相应地在数据访问层做改动,接着在业务逻辑层作改动,有时甚至要改表现层。这不是很麻烦吗?
MF:哈,这是人们经常抱怨的事情,但是这就是所谓的trade-off。分层在带来一些好处的同时也带来了这样的麻烦。

这一段的引用告诉我们:分层会带来麻烦,这一点是绝对性的。不只是某些人觉得麻烦,而是很多很多人经常性的觉得麻烦。也就是说,千万不要无条件的崇拜分层,那是愚蠢的行为。

而Martin Fowler在前面回答记者的问题的时候也同时强调:


记者:可是,我们几乎不需要做任何大的设计工作,体系和架构是确定的,还有很多框架在辅助我们,并将所有有趣的设计工作都代替我们做了,在这样的开发过程中,趣味何在?
MF:如何快速而准确的满足业务需求,对业务逻辑进行建模才是有趣的事情。而像O/R Mapping这样的事情,是非常乏味而枯燥的。对我而言,绝不会认为撰写一个O/R Mapping的工具是一件有趣的事情。它与业务本身无关,而只是作为基础设施而存在。有这样的工具我觉得非常好,但是我认为更重要的,还是把握客户对业务的需求。

对业务逻辑(客户的业务需求)的灵活、精确的把握、以及实现,才是最重要的,也是最终需要检验的东西。——这个必须要求系统设计和开发框架足够灵活,满足编程时候的敏捷机动性。

因此,烦杂的4层以上的分层设计,对于复杂的、需求可能会变动的大型系统来说,往往是一场梦魇和灾难。而不是如某些人认为的那样相反,那些人认为越是大型系统、越是复杂的逻辑,就越是需要细密的分层。

其实,业务逻辑本身已经足够复杂了,复杂在这里不仅意味着系统的复杂度,而且也意味着系统一旦变动起来,复杂度往往指数级上升。一个极其简单的需求变更,都要涉及到不同层的修改,更何况在复杂大系统中需求的变动是密集出现的。所以我的观点是:越是复杂多变的大系统,越是需要控制并简化分层。当分层减少,针对需求变动的更改之处,也会呈指数级下降,劳动量和心理压力也随之直线降低。

当然,简化分层的前提是必须分层。

总结起来,关于分层,我的观点是:

1、分层是必须的;
2、不要无条件的崇拜分层;
3、分层的唯一依据是:随需应变;
4、编程的终极目的是:能够随时变更业务逻辑,而不是固守系统设计;
5、越是复杂多变的系统,越是需要谨慎控制分层、需要尽量简化分层;
6、只有需求稳定的系统才适合进行繁复的分层设计;
7、三到四层是一个比较合适的数目。
8、五层以上的体系,很多系统是不合适的。
9、同一个系统中,不同模块可以有不同的分层方式。
10、最后,检验分层是否合适的标准:修改业务功能和代码重构是否很麻烦。如果可以轻松的修改,不用考虑关联风险,那就是成功的;如果总是要提醒自己不要轻易修改,那分层也许就是有问题的。

在谈到简化分层的必要性之后,就要谈到充血模型。充血模型因为将对象的行为(方法)放在模型之中,所以带来了层次上的简化。当然充血模型要能做到这一点,需要一个简单易用的ORM支持才行。充血模型的好处在于,代码更加清晰易读,业务逻辑也看起来更加明白清爽。

如何使用充血模型?简单的做法就是,使用充血模型代替贫血的实体类,去除DAO层,持久层集成在模型的方法中。一般说来,简单的系统,尤其是需求变化比较快的简单系统,三层足够:Domain + Controller + Presentation(表现层)

第二部分 贫血模型的必要性和增加服务层

如果充血模型的方法代码超过两三百行(没有固定标准),则需要考虑将行为代码移到service层去。原因:
核心领域模型过度膨胀,其实是很恐怖的一件事。比如一个1000多行的模型对象,给程序设计带来的绝不是方便快捷;而是阅读、修改、重构的障碍以及不断产生bug的可能性。

另外,复杂的系统,在某一业务逻辑需要用到不同的领域对象的时候,添加service层就很必要了。service层可以用于集中不同的领域模型的操作:
Domain + Service + Controller + Presentation

比如论坛的管理操作,一个简单的删帖动作,同时意味着需要删除标签、通知被删除作者等后续的事务性操作。因此可以设计一个PostServise,其中 DeletePost() 方法包括:deleteRecord() 、notifyAuthor() 、deleteTags()等相应的操作流程。

总结:充血模型复杂到一定程度,往往就要考虑贫血模型。

更进一步,我们要看到,复杂的、高负载的系统,往往首先考虑的是性能、稳定和复杂度的可控制型。

贫血模型意味着行为/职责外分,意味着以交互行为关系为中心设计系统,而不是以数据模型为中心设计系统。对于系统来说,数据对象具有有什么属性,有时候并不是最重要的;关键是数据和数据之间的关系处理。这时候如果单独出现一个服务层service,负责在不同的模块之间的数据管理和调用,可以非常有效的处理好系统的复杂度。同时带来的好处是,将系统和数据层松耦合,并能很方便的提升性能。

以下条件一般使用贫血模型:

1、对扩展性有较高要求(不同职责的分层,更加容易替换)
2、对存储性能有较高要求(专门抽离出一个dao层,便于性能调优)
3、业务逻辑复杂,经常一个操作涉及到n个领域对象(抽离出一个service层,能使代码更加清楚)
4、业务需求比较固定的。比如通用产品的需求就比较固定,而项目的需求往往变化很快。

以下条件一般使用充血模型:

1、业务逻辑不算特别复杂
2、不需要多高的扩展性
3、业务需求变化多端
4、快速反应的项目