有关继承的思考(1_of_n)

来源:互联网 发布:数据能看出男宝女宝吗 编辑:程序博客网 时间:2024/05/20 22:37

前言:
继承是我认为的最容易实现代码重用设计方案。
但有人说它不好。
所以我得想想到底哪里不好。

 

一种思考:
首先,程序员都爱偷懒,说得好听一点都爱“复用”别人的代码。这让我想到生活中一个很有趣的例子。大家是不是经常遇到需要拧开一些螺丝却没有螺丝刀,此时我们通常的做法是用一些小刀子尖来替换一下,但是,结果往往不是很顺心,或是把刀子尖给弄不像样了,或是把螺丝的十字口给划平了。最后我们往往会怪这把刀子不好用。


说到这里大家应该猜到了我想表达什么意思:在大多数情况下,我们的继承往往过于牵强。那么怎么样的继承是正确的,怎么样的继承是牵强的呢,我们总得有一个简单的标准来实施我们平时的设计决策吧。


这里我还是用一个实际的例子来说明,跟上个例子不一样,这次我举两个java程序员都应该非常熟悉的例子。


例子一:
先看看这个继承结构
Object
_AbstractStringBuilder //定义:一个易变的字符序列
__StringBuilder //对AbstractStringBuilder的纯实现
__StringBuffer //对AbstractStringBuilder的纯实现+(方法调用的)多线程安全

 

我首先要说的是,这个继承结构是相对正确的,大家也应该没有发现什么问题吧。

 

假如改成这样

Object
_AbstractStringBuilder //定义:一个易变的字符序列
__StringBuilder //对AbstractStringBuilder的纯实现
___StringBuffer //对AbstractStringBuilder的实现+(方法调用的)多线程安全

 

我不说这个设计是错的,只说这个继承几乎没有意义,因为当你想实现一个高性能StringBuffer时,你会发现,基本上你会重StringBuilder的所有方法。

 

再假如改成这样
_String
__StringBuilder

 

我仍不说这是错的,但是,你会发现实现起来相当困难。假如你看看String的源代码你会发现为什么。

 

现在我说以上两种假设的继承都是相当牵强的。


请容许我在得出“设计方法论”前再引入一个正确继承的例子且不像例例子一一样继承自Abstract。这其实挺难想的
_java.util.Date
__java.sql.Date

*为了表述方便,我把java.util.Date称为uDate,另一个叫sDate.


我们来看看sDate的实现,先不理会申明为@deprecated的方法。我们会发现,它重载了一个toString方法以及如下方法setTime。

    public void setTime(long date) {
 // If the millisecond date value contains time info, mask it out.
 super.setTime(date); 
    }


这里很奇怪,既然不是构造函数,为什么要多此一举地再写一个相同的方法调用一下?莫非setTime(date)在uDate中修饰符为protected?不可能啊,这方法我不知道用我N次了。
来回看了几下之后,我觉得我应该是找到原因了,大家看看这两个相同方法的注释就知道了。

 

其实,我举最后的一个例子只想说明一点:子类往往是需要使用父类大多数甚至是全部的方法。

 

好了,最后我说出我想说的一条“设计方法论”

继承要确保is a关系,而不是like a,再具体但不严谨一点:父类方法签名在子类中仍要保持实际的意义,且只能允许有少量的重写。

 (需要验证这个方法论是否可行,需要大量的实践验证,而我只想时不时地得出一些可能正确的结论。)