原文:https://medium.com/@cscalfani/goodbye-object-oriented-programming-a59cda4c0e53#.z48fmajih
这是一篇长文,共有三大块,我先翻译第一块,以后有时间更新后面的
Paste_Image.png
注意到扫描仪类和打印机类都实现了一个叫做“开始”的方法吗?
所以复印机到底继承了哪个类里面的“开始”方法?扫描仪的还是打印机的?肯定不能是两个都继承了吧。
钻石问题解决方案
答案很简单,不要这么用。
没错,大多数面向对象编程语言不会让你这么用的。
但是,如果我非要这样建模呢?我想复用啊!
那你必须,包含并且委托。
看到了吗,现在复印机类包含了一个打印机的实例和一个扫描仪的实例。它把“开始”方法委托给了打印机类去实现。当然,你也可以轻易的把它委托给复印机类。
这是继承这一核心特性的又一瑕疵。
脆弱基类问题
所以我尽量使我的层级足够浅,而且避免循环继承。嗯这下没有钻石问题了。
嗯,世界还是那么美好一切运行良好,直到。。。
一天,我的代码运转正常,但第二天去停止工作了。那么问题来了,我™没改过代码啊!
嗯,也许是出bug了,等等,不对,确实有什么地方更改了。
但改动的部分不是我的代码。是我继承的基类代码改了。
为毛基类的变化会使我的代码崩溃?
原因在于。。。
想象一下下面的基类(虽然是用JAVA写的,但即使你不是JAVA程序员应该也很容易理解)
看到注释那行代码了吗,就是这行代码的改变影响了我的代码。
这个类有两个接口类:add()和addAll()。add()方法会向数组中添加一个元素,而addAll()方法会通过调用add()方法来把多个元素添加到数组里。
下面是继承类
ArrayCount类是基类Array的具体化。ArrayCount与Array的唯一区别是,ArrayCount会在添加了元素以后再计算一下元素的个数。
让我们来仔细看看这两个类。
基类Array的add方法把一个元素添加到一个本地数组中。基类Array的addAll方法调用本地数组的add方法来把每个元素添加到数组中。
ArrayCount类的add方法调用父类的add方法,并且把count的数目自增1。ArrayCount类的addAll方法调用父类的addAll方法并且把count加上元素的个数。
一开始都没有问题。
现在到了关键的一行代码了!注释的这行基类的代码改成了下面这样:
Paste_Image.png
只要考虑到基类的拥有者,方法还是能正常运行。所有的自动测试也能通过。但继承类并不知道是哪个拥有者,是基类的拥有者,还是继承类的拥有者,于是继承类的拥有者被粗鲁的唤醒了。
现在,ArrayCount的addAll方法调用了他的父类中的addAll方法。父类内部会调用已经被继承类覆盖的add方法。会导致循环添加元素的时候,每次循环都让count自增了1,最后又会让count增加上元素的个数。这™就尴尬了。丫算了两次!
这种情况确实存在,继承类的编写者必须清楚基类是如何实现内部的方法的。并且基类有任何变化,必须都同步给他,要不他的继承类会以不可预知的方式崩溃!
啊!这个巨大的漏洞成了悬在继承这一核心特性头上的达摩克利斯之剑,随时威胁着我们所珍视的继承特性的稳定性。
脆弱基类的解决方案
再一次祭出包含和委托大法吧。
通过使用包含和委托,我们从白盒编程(这个不知道的小白同学请自行百度,还有灰盒,国内一般测试用这种说法比较多)变成了黑盒编程。白盒编程,我们必须了解基类的实现过程。
而黑盒编程,因为我们不能通过覆盖基类的方法来改写基类的代码。我们能完全忽略基类的实现过程。我们只需要了解接口就好了。
这个走向有点令人困惑啊。
继承本来是应该实现复用的。
但是面向对象编程并没有使得包含和委托这种繁琐操作更加加单。反而是包含和委托使得继承用起来容易些。。。
如果你跟我的经历差不多,你估计也开始重新思考继承这个玩意了。但最重要的,这应该打击到你对分层分类的信心了。
分层问题
每次我进入一家新公司,我老是纠结怎么给我的公司文档建立目录结构。比如说员工手册。我是应该建立一个叫做文档的文件夹,然后在里面建立一个叫做公司的文件夹?还是应该建立一个叫做公司的文件夹,然后在里面建立一个叫做文档的文件夹?
两种都可以,但是那种是对的?哪种更好?
分类分层的假设是说,存在着一些基类(父类),他们是最基础的,然后他们的继承类(子类)会在他们的基础上更加具体化。而且层级越往下走,就应该越具体。(看看上面的层级形状示例)
但如果父类和子类不断的调换位置,那这个模型很明显有问题。
分层问题的解决方案
麻蛋
分类分层就没有个卵用
那分层到底有什么好处?
包含
如果你看看真实世界,你会发现包含层级(专有)几乎无处不在
但你在真实世界里找不到叫做分类分层的东西。我们先把它放在一边。面向对象范式是以充满对象的真实世界为基础的。但却使用了一个有问题的模型,也就是真实世界里根本找不到的分类分层。
但真实世界确实充满了包含层级。包含层级的一个不错的例子就是你的袜子。你的袜子放在抽屉里,而抽屉是在衣柜里,衣柜又在你的卧室里,卧室又在你的房子里,等等。
你硬盘上的目录也是包含层级的一个例子。他们包含着文件
那我们我们该如何分类?
哈,如果你还想着公司文档,其实我怎么分类都可以。我可以把他们放在叫做文档的文件夹里或者叫做资料的文件夹里。
我分类的方式是贴标签,我是这么给文件贴标签的:
文档公司手册
标签没有顺序或者层级(这下也把钻石问题解决了)
标签类似于接口,你可以给文件关联上多种类型。
但是有这么多毛病的继承核心特性,看起来失败了。
再见吧,继承!
网友评论