为什么组合好于继承?

15-03-12 banq
                   

本文使用亲身案例形象说明了软件设计领域为什么组合Composition要好于继承(包括接口继承),只有需求分析域的问题分解,才有设计编程的组合应用。

来自游戏公司GameSys的Yan Cui发表了博文:

This is why you need Composition over Inheritance

他试图对一个刚刚接触自己还是不太熟悉的系统进行一些旧代码修改,很自然地,第一步首先是了解这些旧代码是做什么的,开始从需要改变的地方查看类代码了。


有过类似经验的人可能不会奇怪,他一无所获,无法发现这些类是干什么的,这个类只包含少数override方法,但是无法知道它们是如何搭配在一起工作的。

于是他进一步深入抽象类的多个层次,直到到达这个类层次的基类,从这个基类开始在顺着继承树形结构来回好几次,终于得到了一个有关业务逻辑分散在各层次之间的模糊概念。


更有甚者,因为与原来树形控制流有点区别,因此他们有得重新做一个新的树形继承结构的控制流,这些使人更加难以理解了。

这些都不是程序员想要,他们需要容易方便地且有信心地思考reason自己的代码。或者说,让自己的代码像文章一样有条理性。

是不是大部分人都有这样类似痛苦经历呢?那么如何应用组合而不是继承呢Com­po­si­tion over Inher­i­tance?

Wiki中Com­po­si­tion over Inher­i­tance定义是领域建模:

使用组合而不是继承是一种设计原则,能够带来设计的更高灵活性,带给业务领域的类代码更长时间的稳定性。

对于Wiki这段结论,作者他说自己不是一个“下结论的粉丝”(a fan of con­clu­sions banq注:应试教育容易导致人们喜欢下结论以及看文章时只有看到下结论才认为自己看明白,因为考试题总是有唯一标准肯定终结的答案的,长期做考题容易被误导成这种思维模式)。

既然我们不直接接受这种结论,那么我们就需要质疑反思,这段话到底是什么意思呢?为什么有这段话呢?能给出高灵活性和业务领域更稳定的证明或经验证据吗?

从作者的角度看,使用组合而不是继承是鼓励更好的问题域的解耦,起初如果你不将一个大问题分解成一个个更小的容易解决的小部分问题,后来你就无法使用组合来组装它们。

Scott Wlaschin的rail­way ori­ented pro­gram­ming意味使用了一个很好的案例来说明在实践中如何使用组合。

EventSourcing/CQRS的倡导者Greg Young还指出,问题域的分解是我们当前软件工业的最大问题。

问题域的分解不只是局限于代码组织,微服务也是一个这方面的典型案例,从巨石monolithic铁板一块哦系统迁移到微服务是另外一种问题域的解耦。

因此,我们需要使用利刀分解前面描述的类层次树形结构,使用更小的、可组合的替换它们,包括使用具有这样特点的语言如F#等。

参考:

Go语言是彻底的面向组合的并发语言

分解和组合的抽象方法

范畴category:组合的本质

[该贴被banq于2015-03-12 19:14修改过]

[该贴被banq于2015-03-13 13:06修改过]

                   

18
liangshan
2015-03-14 11:01

我明白了。可能是因为平面隐藏了信息,如果能是立体的,是能够根据主体的观察需要变成可穿透的(让主体的眼睛能够看到立体图形内部的图形的意思是主体“分神”进入问题空间)的话就好了。

liangshan
2015-03-14 11:05

这里见到了这样两张图

flyzb
2015-03-14 17:34

“拆分”和“复用”是系统设计中2个永恒的主题,在这两者之间如何达到一种平衡是艺术。从技术角度讲,继承和组合没有好与坏之说。不知道大家是否注意到,“组合比继承好”是一种实战观点,都是经验之谈。其实这很好理解。因为业务越来越复杂,变化越来越快,原来的业务设计思路已经不满足现在的业务需要,继承会限制系统的重构。从业务角度看,如果有人能够综观某个事物(行业)几十年的发展,也就是常说的吃透了业务,在这种假定之下来用继承来设计业务,那一定会成为经典。但现实的场景中,这样的人太少,对于快速发展的中国尤其如此,所以我们才说“组合比继承好”。从本质上说,这是我们总是暗示自己“我们总是目光短浅,组合的风险比继承少”。

[该贴被flyzb于2015-03-14 17:36修改过]

banq
2015-03-14 18:26

2015-03-14 17:34 "@flyzb"的内容
从本质上说,这是我们总是暗示自己“我们总是目光短浅,组合的风险比继承少” ...

写得很好,理解透彻。

我想追加的是:函数式编程FP也是基于这样的暗示,这里面再延伸一下,由于组合的碎块很多,我们就不再进入每个碎块内部研究了,假设每个碎块是一个黑盒子,我们只要关心这些碎块之间的组合关系,而这背后有一门专门的严谨的理论指导:范畴论,或者称为群论。

现在就比较好玩了,有两个对比,一个是继承系统需要依赖人的经验;一个是组合,可以凭借范畴论这个拐杖在黑暗中摸索。哪个更符合科学一些,当然后者,但是哪个更可具操作性,当然是前者。

所以,当我们还在纠结Java的继承为什么是单继承,而不是多继承时,其实我们已经除了继承这条充满争议的方向,有了另外一个更可靠的方向,以范畴论指导组合。

以上只是个人想法,仅供参考。

[该贴被banq于2015-03-14 18:27修改过]

2Go 1 2 下一页