本文使用亲身案例形象说明了软件设计领域为什么组合Composition要好于继承(包括接口继承),只有需求分析域的问题分解,才有设计编程的组合应用。
来自游戏公司GameSys的Yan Cui发表了博文:
This is why you need Composition over Inheritance
他试图对一个刚刚接触自己还是不太熟悉的系统进行一些旧代码修改,很自然地,第一步首先是了解这些旧代码是做什么的,开始从需要改变的地方查看类代码了。
有过类似经验的人可能不会奇怪,他一无所获,无法发现这些类是干什么的,这个类只包含少数override方法,但是无法知道它们是如何搭配在一起工作的。于是他进一步深入抽象类的多个层次,直到到达这个类层次的基类,从这个基类开始在顺着继承树形结构来回好几次,终于得到了一个有关业务逻辑分散在各层次之间的模糊概念。
更有甚者,因为与原来树形控制流有点区别,因此他们有得重新做一个新的树形继承结构的控制流,这些使人更加难以理解了。
这些都不是程序员想要,他们需要容易方便地且有信心地思考reason自己的代码。或者说,让自己的代码像文章一样有条理性。
是不是大部分人都有这样类似痛苦经历呢?那么如何应用组合而不是继承呢Composition over Inheritance?
Wiki中Composition over Inheritance定义是领域建模:
使用组合而不是继承是一种设计原则,能够带来设计的更高灵活性,带给业务领域的类代码更长时间的稳定性。
对于Wiki这段结论,作者他说自己不是一个“下结论的粉丝”(a fan of conclusions banq注:应试教育容易导致人们喜欢下结论以及看文章时只有看到下结论才认为自己看明白,因为考试题总是有唯一标准肯定终结的答案的,长期做考题容易被误导成这种思维模式)。
既然我们不直接接受这种结论,那么我们就需要质疑反思,这段话到底是什么意思呢?为什么有这段话呢?能给出高灵活性和业务领域更稳定的证明或经验证据吗?
从作者的角度看,使用组合而不是继承是鼓励更好的问题域的解耦,起初如果你不将一个大问题分解成一个个更小的容易解决的小部分问题,后来你就无法使用组合来组装它们。
Scott Wlaschin的 railway oriented programming 意味使用了一个很好的案例来说明在实践中如何使用组合。
EventSourcing/CQRS的倡导者Greg Young还指出,问题域的分解是我们当前软件工业的最大问题。
问题域的分解不只是局限于代码组织,微服务也是一个这方面的典型案例,从巨石monolithic铁板一块哦系统迁移到微服务是另外一种问题域的解耦。
因此,我们需要使用利刀分解前面描述的类层次树形结构,使用更小的、可组合的替换它们,包括使用具有这样特点的语言如F#等。
参考:
Go语言是彻底的面向组合的 并发 语言
分解和组合的抽象方法
范畴category:组合的本质