面向对象相比面向过程,对于我这种菜逼来说,首先感受到的就是将程序中的基本单元从函数变成了类/对象,类/对象下还有成员方法和成员数据。
相比面向过程,面向对象提供了一个新的层次去解析你的问题,这样每一层需要考虑的东西相对变少了一点。
这大概就是大家说的面向对象的第一个特征:“封装”。
当然,封装在coding中随处可见,不应该仅仅当做面向对象的一个基本特征。
继承和多态不应该分开叙述,原因不赘述。
曾经我以为继承是面向对象的基本特征,直到我看到了Go;曾经我以为多态是面向对象的基本特征,直到我看到了Python。
Go中取消了继承,但是保留了接口;Python中虽然有继承,但是由于有duck typing的存在,个人很少用到继承。
小弟在C++中,认为多态就是允许将子类类型的指针赋值给父类类型的指针。
但是在Python这样支持duck typing的语言中,连继承都可以不用,又何来的这种狭义的多态?
事实上,面向对象最初需要解决的问题,究竟是什么?
是用更容易被人类理解的思路去利用代码表达解决问题的方法。
面向对象的第一步,就是描述对象。
那么让我们想象一下,当我们向其他人描述一个他从未见过从未理解过的东西,我们该如何介绍?
“他的眼镜很大,他的腰带很高……”或者是“他长得像一个蛤蟆”。
是的,我们用一系列对方理解的东西组合了一个描述或者是用一个对方理解的东西概括。
前者我们一般叫组合,后者我们往往是利用继承来实现的。
但很明显,继承绝不是完完全全的复制,那样没有任何意义。子类必然会在父类的基础上进行修改。所以这个时候,我们可能就要说“他长得像一个蛤蟆,但是他的姿势水平很高”。
那么出现了什么问题?
如果我们对整个系统理解充分、抽象合理,继承这种“is a”的描述可以节约大量的“口水”,在编程时意味着减少代码量、节约打字时间。
然而现实是,我们往往无法在面对一个陌生系统时,快速、准确的对他进行概括。更多的时候是描述。
在一个陌生领域过早的使用继承构建对象系统,往往很容易陷入重构甚至重写的深渊。
相信我,绝大多数时候,继承节约的敲代码的时间远不如重构需要的时间多。
那么换个思路,我们不用继承,用接口,个人习惯,接口都用形容词命名,表示一个类的特征。
依次实现“会跳动”、“有线连接”、“纺锤形”接口,然后你就实现了一个有线跳蛋类。
当你的老婆拿来一个无线跳弹,你只需要依次实现“会跳动”、“纺锤形”、“无线连接”接口,即可实现。虽然写代码的时间可能会比较长,但是无脑实现我想总要比绞尽脑汁理清对象树轻松愉快。(此处举例为Java8之前这种不能再接口中实现方法的情况)
那么再进一步,C++中支持多继承,往往多继承被认为容易打乱思路,但如果利用多继承来模拟Java中的接口呢?
基类都利用形容词命名,而不是名词,将多继承的含义从“是什么东西”变成“有什么特点”,是否会兼具灵活和便利?
Java8中接口的default方法是否就是这个考虑?
面向对象一直以来需要解决的仅仅是“对拥有同一个接口的一类对象,都可以不加处理的进行调用”,即调用者不需要考虑对象实现细节,仅需要考虑对象拥有哪些接口。
在Java/C++等语言中,这一点由编译器强制保证;在Python等语言中,是利用扣工资等非技术手段逼迫开发人员自己保证。