8.3 建模步骤C-2 识别类的关系
8.3.4 识别关联关系
8.3.4.6 DDD话语“聚合”中的伪创新
DDD话语中也有“聚合”。Eric Evans的“Domain-Driven Design: Tackling Complexity in the Heart of Software”书中是这样说的:
An AGGREGATE is a cluster of associated objects that we treat as a unit for the purpose of data changes. Each AGGREGATE has a root and a boundary. ……The root is the only member of the AGGREGATE that outside objects are allowed to hold references to, ……
一个AGGREGATE是一簇相关联的对象,我们把它作为数据变化的单元来对待。每个AGGREGATE有一个根和一个边界。……根是AGGREGATE的成员中唯一允许外部对象持有引用的,……
以上文字提到了两个概念:
(1)aggregate(聚合体),指整个聚合/组合结构。
(2)aggregate root(聚合根),聚合/组合结构中扮演整体的对象。
先阐明本书对于这两个概念的观点:
两者都不提倡使用。其中,aggregate属于冗余概念,aggregate root属于错误概念。
接下来说一下原因。
我们以图8-138中Grady Booch关于系统结构的一段隐喻作为素材,比较aggregate、aggregate root以及前文所说的UML的aggregation(composition)。
图8-138 摘自《面向对象分析与设计(原书第2版)》,Grady Booch 著;冯博琴 等 译,英文原版出版于1994年
把图8-138的内容用类图和组合结构图表达如图8-139,并在标出三个用语的位置。
图8-139 三个用语的位置
(1)aggregation是本质概念
aggregation指类之间的“整体-部分”关联,可以称为“聚合关联”。
例如,图8-139中,“植物”和“根”、“茎”、“叶”存在aggregation关联,说明可能会存在“植物”对象,它的组成部件是“根”、“茎”、“叶”对象。
根据前文提到的本书观点,“整体-部分”关联就是“组合(composition)”,因此,图8-139以及后面的图形都会使用实心菱形。
(2)aggregate是冗余概念
在面向对象建模领域,aggregate并非Eric Evans在“Domain-Driven Design”书中首先使用。
1991年,James Rumbaugh等人的书“Object-Oriented Modeling and Design”中就阐述了aggregation和aggregate,如图8-140所示。
图8-140 摘自Object-Oriented Modeling and Design,James Rumbaugh et al. ,1991
可以看到,图8-140中的标题是Aggregation,不是Aggregate。
1999年出版的“The Unified Modeling Language Reference Manual(UML参考手册)”第1版中,也有“aggregate”词条,但内容只有一句话,接下来的“aggregation”词条的内容却达4页之多,如图8-141。
图8-141 摘自The Unified Modeling Language Reference Manual,James Rumbaugh et al. ,1999
可以看出来,UML规范认为aggregation才是本质的概念。
为什么两者待遇不一样?
因为aggregate只是类(类图上的结点)在aggregation关联(类图上的边)中扮演的一个角色。同一个类可能在某个aggregation关联中扮演aggregate(聚合体,整体),在另外一个aggregation关联中扮演part(部件)。
我们进一步扩展图8-139中的类图,得到图8-142。可以看到,包括“植物”在内的很多类,既扮演整体,也扮演部分。
图8-142 既扮演整体又扮演部分
一个对象可能由很多部件(最小部件是其简单属性值)组成,同时它又可以成为更大对象的部件。
离开特定的关联,指着一个对象说“它是整体”、“它是aggregate”或“它是aggregate root”,都是不合适的,除非只存在一级整体-部分结构。
这也是现在DDD实践中aggregate的现状——再多一级的话,不妨祭出“性能”遮羞布遮掩过去——“这得加载多少数据啊,会影响性能的!”。
即使只存在一级整体-部分结构,也没有必要在类上标注“aggregate root”,或者圈一个边界说这是一个aggregate,关系上的菱形标记已经提供了足够的信息。
图8-143 这两个标记都是冗余的
同理,如果要体现某些领域驱动设计实践投资少、见效快、产量高、门槛低、仪式感十足的特点,就可以考虑图8-143的标记。
(3)aggregate root是伪创新
在“Domain-Driven Design”之前的软件开发书籍中,没有出现aggregate root的说法,aggregate root应该是Eric Evans的造词。
可惜,这是一个伪创新。
我们再看一遍“Domain-Driven Design”中的描述:
An AGGREGATE is a cluster of associated objects that we treat as a unit for the purpose of data changes. Each AGGREGATE has a root and a boundary. ……The root is the only member of the AGGREGATE that outside objects are allowed to hold references to, ……
一个AGGREGATE是一簇相关联的对象,我们把它作为数据变化的单元来对待。每个AGGREGATE有一个根和一个边界。……根是AGGREGATE的成员中唯一允许外部对象持有引用的,……
以图8-139为例,按照这个说法,我们可以说:
①(“植物”+“根”+“茎”+“叶”)等一簇相关联的对象形成了一个aggregate。
②其中“植物”是该aggregate的aggregate root。
先来看①的问题。
图8-144 对象不需要也不能和其部件并列
如图8-144,“植物”已经包括“根”、“茎”、“叶”等部件在内,不能也不需要再和这些部件并列。
在类级别,说“植物和根、茎、叶是整体-部分关联”,可以。
在对象级别,说“某个植物对象由若干根、茎、叶对象组成”,可以。
但是,说“某个植物对象和它的根、茎、叶一起组成aggregate”,不可以。
说“植物对象是一个aggregate”,可以,但这样的说法无意义。所有对象都是aggregate,否则要它干什么呢?
要把这个圆过去,可以把“植物”排除在组成aggregate的“一簇相关联的对象”之外,说“一簇根、茎、叶对象组成了植物aggregate”,不过,Eric Evans又说了“根是AGGREGATE的成员”,看来是圆不过去了。
再来看②的问题。
即使我们排除了①的错误,采取“一簇根、茎、叶对象组成了植物aggregate”的说法,但说法②仍然不合适。我们可以看下面的表述:
植物由根、茎、叶组成,所以,植物是[根……]的根?
汽车由发动机、车身、底盘组成,所以,汽车是[发动机……]的根?
墙由砖垒成,所以,墙是砖的根?
分子由原子组成,所以,分子是原子的根?
之所以会有aggregate root这样的错觉,有可能是受了关系数据库思考方式的影响。
图8-139的类图,如果用关系数据库来保存对象,可能会得到图8-145的几个表,这样就产生了“植物”就是“植物”表的错觉,于是觉得有必要提醒它是aggregate root,别忘了带上其他几个小弟。
图8-145 受关系数据库思考方式影响的aggregate root错觉
aggregate root错觉另一个可能的原因来自人类社会的直觉。
(待续……)