设计原则:什么样的情况下需要引入父类?

背景

什么样的情况下需要引入父类?这就是今天的话题,也是对昨天的文章(设计原则:不要为了复用而使用继承)的一个补充。

让我们站在抽象的角度思考这个问题,下面两幅图片是我们讨论的上下文。

设计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#中叫扩张方法)复用“推论方法”。

关于掺入可以参考如下文章:

  1. PHP中实现掺入
  2. JS、C#、Java、Ruby、Python、C++中实现掺入

备注

每天能想明白一点东西,真是让人开心,感谢博客园和群里的兄弟们,这个问题暂时告一段落,做好否定自己的准备

原文地址:https://www.cnblogs.com/happyframework/p/3279560.html