背景
什么样的情况下需要引入父类?这就是今天的话题,也是对昨天的文章(设计原则:不要为了复用而使用继承)的一个补充。
让我们站在抽象的角度思考这个问题,下面两幅图片是我们讨论的上下文。
设计1
设计2
为什么引入了两个父类(Base2和Base3)?
为了复用实现
面对这个问题,我可能给出的一种回答是:A和B为了复用方法(行为)或数据(状态),如果我接受这个答案,那么如何应对“B和C之间的复用”,很多语言都是单实现继承的,这说明复用实现不是继承的本质原因,我对这个答案不够满意,继续思考。
为了引入抽象
如果我给出的答案是:为了引入抽象,这个答案本身就够抽象了,估计会被很多人批评(期望被批评),先让我简单的解释一下。
引入了父类,在某种程度上是引入了一个继承体系,父类就是这个继承体系的抽象表示,使用者只需要关心这个抽象即可(父类),这也是“面向接口编程”的核心思想。父类定义了契约,子类实现契约,子类之间表现出多态。
这个答案也可以这么解说:为了复用契约、为了面向接口编程、为了多态。
总结:继承可以实现复用,不要为了复用而使用继承。
如何复用B和C之间重复的代码?
假如Base2和Base3都是合理的,如何复用B和C之间的代码?抽象的表达是:如何在不同的继承体系之间复用代码?
让我们举个例子
ICompareable<T> 接口定义了一个方法Compare,如果A.Compare(B)的结果为-1,表示A<B,如果为0,表示A==B,如果为1,表示A>B。
我们将ICompareable<T>.Compare命名为“定律方法”。
CompareableExtentions 扩张了ICompareable<T>,提供了:Less、LessEqual、Greate、GreateEqual、Equal和Between。
我们将Less、LessEqual、Greate、GreateEqual、Equal和Between命名为“推论方法”。
总结:在多个继承体系中,用接口继承复用“定律方法”,用掺入(C#中叫扩张方法)复用“推论方法”。
关于掺入可以参考如下文章:
备注
每天能想明白一点东西,真是让人开心,感谢博客园和群里的兄弟们,这个问题暂时告一段落,做好否定自己的准备。