首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

血亏模型和充血模型

2012-06-28 
贫血模型和充血模型这两个概念是早些时候Martin Fowler总结出来的两种常见模型设计类型,没有说谁好谁不好,

贫血模型和充血模型

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

?

?

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

?


血亏模型和充血模型

?

优点是系统的层次结构清楚,各层之间单向依赖,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。

?


血亏模型和充血模型

?

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

?

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

?

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

?

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

(1)事务控制不要放在领域模型的对象中实现,可以放在facade中完成。

(2)领域模型对象中只保留该模型驱动的一般方法,对于业务特征明显的特异场景方法调用放在facade中完成。

?

?

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

?

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

?

public void setFacilityHex(String facilityHex)throws WrongFormatOfHexStrException {if (!volidate(facilityHex)) {//校验后发现为非法输入。。。。。。throw new WrongFormatOfHexStrException("The parameter of facilityHex:'"+ facilityHex+ "' is uncorrect,"+ "please make sure that it is like this:0xA2 0XA2 0xa2 0Xa2 a2 or A2");}if (facilityHex.startsWith("0x") || facilityHex.startsWith("0X")) {this.facilityHex = facilityHex;} else {this.facilityHex = "0x" + facilityHex;}}
不知道有什么更好的解决方案没?

就你的问题而言,通常我会倾向于传user这样的对象,本身是基于对象模型的操作,再者也便于参数扩展。public void setFacilityHex(String facilityHex)throws WrongFormatOfHexStrException {if (!volidate(facilityHex)) {//校验后发现为非法输入。。。。。。throw new WrongFormatOfHexStrException("The parameter of facilityHex:'"+ facilityHex+ "' is uncorrect,"+ "please make sure that it is like this:0xA2 0XA2 0xa2 0Xa2 a2 or A2");}if (facilityHex.startsWith("0x") || facilityHex.startsWith("0X")) {this.facilityHex = facilityHex;} else {this.facilityHex = "0x" + facilityHex;}}
不知道有什么更好的解决方案没?
你说的参数输入错误属于异常流程,在开发人员设计和编码过程中,我们尽量避免这些旁枝错节对开发人员的干扰,在业务实现中,他们最好只需要关注主流程即可。
因此,定义一个或多个运行时异常就是一种很好的方式,给每一个你这里关注的对象实现一个validate接口,接口用来判断参数输入的正确性,错误的输入将抛出运行时异常,错误信息通过异常机制往外传递。
外部需要关注异常时,可以捕获;不想关注时,可以无视,到最外部的异常拦截器统一处理即可。
参数校验行为的发生可以使用AOP来完成。 7 楼 stevenjohn 2011-12-30   你说的这些貌似都用过的,呵呵,不过看下能够加深映像 8 楼 feikiss 2012-02-04   feikiss 写道
外部需要关注异常时,可以捕获;不想关注时,可以无视,到最外部的异常拦截器统一处理即可。
参数校验行为的发生可以使用AOP来完成。
是说把需要校验的内容单独拿出来,由单独的类来专一来处理?
9 楼 RayChase 2012-02-04   RayChase 写道feikiss 写道
外部需要关注异常时,可以捕获;不想关注时,可以无视,到最外部的异常拦截器统一处理即可。
参数校验行为的发生可以使用AOP来完成。
是说把需要校验的内容单独拿出来,由单独的类来专一来处理?

首先,入参的校验,被称作Guard Statement,本身就可以属于方法的一部分,你这样的实现本来就未尝不可。

如果你希望把入参校验单独抽取出来,那么,这样的事情做得少一点为妙,比如仅仅是组件对外部暴露的接口才做。

如果其中的方法参数没有封装到对象里面去,可以参见Struts2的Action中对参数校验的实现(validate-xxx.xml),它把参数检查的规则放置到配置文件里面去了;

如果你能把方法入参做一个包装,单纯使用AOP来对参数进行校验的时候,起码看起来会优美得多:
setFacilityHex(SetFacilityHexEvent)
而其中的SetFacilityHexEvent定义如下,validate的调用借由AOP来完成:
class SetFacilityHexEvent{
    String facilityHex;
    public void validate(){
        …… //此处进行参数校验
    }
} 10 楼 huashuizhuhui 2012-02-05   哈哈 这个问题我是这样解决的
CQRS查询命令分离
http://user.qzone.qq.com/46580583?ADUIN=46580583&ADSESSION=1328406420&ADTAG=CLIENT.QQ.3493_MyTip.0&ptlang=2052#!app=4

热点排行