20 世纪 60 年代,编程遇到了一个大问题:计算机还没有那么弱小,须要以某种形式均衡数据结构和程序之间的能力。
这意味着,如果你有大量数据,那么不将计算机推向极限就无奈充分利用这些数据。另外,如果你须要做很多事件,那么你就不能应用过多的数据,否则计算机将会始终运行上来。
接下来到了 1966、1967 年,Alan Kay 从实践上证实能够应用封装的微型计算机。这些微型计算机不共享数据,而是通过消息传递进行通信。这样就能够更加经济地应用计算资源。
只管这个想法很奇妙,但直到 1981 年,面向对象编程才成为支流。在那之后,它就没有进行过吸引新的和经验丰富的软件开发者。面向对象的程序员市场判若两人地繁忙。
然而在最近几年中,这种已有几十年历史的编程范式受到越来越多的批评。难道是在面向对象编程大行其道 40 年之后,技术曾经超过了这种范式?
函数和数据耦合
面向对象编程的次要思维非常简单:尝试将一个功能强大的程序整体合成为性能同样弱小的多个局部。这样就能够将一些数据和那些只在相干数据上应用的函数耦合起来。
留神,这仅涵盖封装的概念。也就是说,位于对象外部的数据和函数对于内部是不可见的。咱们只能通过音讯(通常通过 getter 和 setter 函数)与对象的内容进行交互。
继承性和多态性并没有蕴含在最后的设计想法中,然而对于当初的面向对象编程而言是必须的。继承基本上意味着开发者能够定义具备其父类所有属性的子类。直到 1976 年,即面向对象的程序设计的概念问世十年之后,继承性才被引入。
又过了十年,多态性才进入面向对象的编程。简略来讲,这意味着某种办法或对象能够用做其余办法或对象的模板。从某种意义上说,多态性是继承性的泛化,因为并不是原始办法或对象的所有属性都须要传输到新实体。相同,你还能够抉择重写一些属性。
多态性的非凡之处在于,即便两个实体在源代码中相互依赖,被调用实体的工作形式也更像插件。这使得开发人员的工作变得轻松,因为他们不用放心运行时的依赖关系。
值得一提的是,继承性和多态性并不是面向对象编程所特有的。真正的区别在于封装数据及其蕴含的办法。在计算资源比明天稀缺得多的时代,这是一个蠢才的想法。
面向对象编程中的 5 大问题
面向对象的编程一经问世,便扭转了开发人员对待代码的形式。20 世纪 80 年代以前,过程式编程十分面向机器。开发人员须要十分理解计算机的工作原理能力编写好的代码。
通过封装数据和其余办法,面向对象的编程使软件开发更加以人为核心,合乎人类的直觉。比方,办法 drive() 属于 car 数据组,而不是 teddybear 组。之后呈现的继承性也很直观。比方,古代汽车(Hyundai)是汽车的一个子类,并且具备雷同的属性,但 PooTheBear 不是,这样很好了解。
香蕉猴子丛林问题
设想一下,你正在设置一个新程序,并且正在思考设计一个新类。而后,你回想起为另一个我的项目创立的简洁的小类,发现其对正在进行的工作很适合。
没问题,你能够将以前我的项目中的类在新我的项目中复用。
这里有一个问题:这个类可能是另一个类的子类,因而你须要将它的父类也蕴含在内。而后你会发现,这个父类可能也是另一个类的子类,以此类推,最初要面对一堆代码。
Erlang 的创建者 Joe Armstrong 曾有一句名言:「面向对象语言的问题在于,它们自带其本身四周的所有隐式环境。你想要香蕉,然而失去的却是拿着香蕉的大猩猩和整个丛林。」
这简直能够阐明所有。复用类是能够的,实际上这可能是面向对象编程的次要长处,但不要将其施展到极致。有时你应该建设一个新的类,而不是增加大量依赖项。
软弱的基类问题
设想一下,如果你曾经胜利地将另一个我的项目中的类复用于新的代码,那么如果基类发生变化会怎么?
这可能会毁坏你整个新我的项目的代码,即便你可能什么也没做。一旦有人更改了基类中的一个细节,而这一点又对你的我的项目至关重要,那么这种影响将是十分大并且忽然的。
应用继承的次数越多,潜在的保护工作就越多。因而,即便在短期内复用代码十分无效,但从久远来看,它可能让你付出肯定的代价。
菱形继承问题
利用继承能够将一类中的属性传递给其余类。然而,如果你想混合两个不同类的属性怎么办?
没错,这无奈实现,至多惯例的办法都不行。以 Copier 类为例(在此援用以下链接文章中的例子:https://medium.com/@cscalfani…),Copier 将扫描文件的内容并将其打印在白纸上。那么它应该是 Scanner 还是 Printer 的子类?
这个问题基本没有完满的答案。即便这个问题不会毁坏你的代码,但它经常出现,会让人很丧气。
层级问题
在菱形继承问题中,Copier 是哪个类的子类是问题的关键所在。但或者有个投机取巧的计划:假如 Copier 是父类,Scanner 和 Printer 是仅继承属性子集的子类,那么问题就解决了。
但如果你的 Copier 是黑白的,而 Printer 也可能解决黑白,那怎么办?从这个意义上说,Printer 不是 Copier 的一种泛化吗?如果 Printer 连贯了 WiFi,而 Copier 没有呢?
类上沉积的属性越多,建设适当的层次结构就越艰难。在你所解决的属性集群中,Copier 共享了 Printer 的一些属性,但不是全副属性,反之亦然。在大型简单我的项目中,层次结构的问题会导致很大的凌乱。
援用问题
你可能会想到进行没有层次结构的面向对象编程。咱们能够应用属性集群,并依据须要继承、扩大或重写属性。兴许这有点凌乱,但这将是对以后问题的精确示意。
这里只存在一个问题:封装的全副目标是使数据片段彼此之间放弃平安,从而使计算效率更高,但没有严格的层次结构,这是行不通的。
假如一个对象 A 通过与另一个对象 B 交互来笼罩层次结构,会产生什么状况?其余关系的状况并不重要,但当 B 不是 A 的间接父类时,A 必须蕴含 B 的全副公有援用,否则,它们将无奈交互。
然而,如果 A 蕴含 B 的子类也具备的信息,那么就能够在多个地位批改该信息。因而,无关 B 的信息曾经不再平安,并且封装曾经被毁坏。
只管许多面向对象的程序员都应用这种架构来构建程序,但这并不是面向对象编程,只是一团糟。
繁多范式存在的危险
以上 5 个问题的共同点是它们都存在不适合的继承。因为继承没有蕴含在面向对象编程的原始模式中,所以这些问题可能不能称为面向对象自身的问题。
然而也并不是只有面向对象编程会被夸张。在纯正的函数式编程中,解决用户的输出或在屏幕上输入音讯极其艰难。对此,面向对象或面向过程编程会好很多。
但依然有一些开发人员试图将这些货色用纯函数的形式实现,并且编写几十行没人能看懂的代码。而应用另一种范式就可能轻松地将代码简化为几行可读的代码。
毫无疑问,函数式编程正在失去更多关注,而面向对象编程近几年受到一些诟病。理解新的编程范式并在适当的时候应用它们是很有意义的。无论哪种编程范式,都不须要只遵循一种,在适当的时候应用不同的编程范式能力更好地解决问题。
面向对象编程真的要被取代了吗?
面对越来越多的问题,函数式编程可能是更无效的一种抉择。数据分析、机器学习、并行编程,这些畛域你投入的越多,你就会越喜爱函数式编程。
然而目前面向对象开发的程序员的岗位需求量仍然比函数式编程开发程序员多得多。然而这也并不意味着你不能成为后者,函数式编程开发的程序员目前依然比拟稀缺。
最有可能的状况是,面向对象的编程将会持续存在十年左右。当然,抉择绝对前卫的形式是好的,但这并不意味着你应该放弃面向对象编程。所以在接下来的几年中,不要齐全放弃它,但至多确保它不是你惟一把握的程序设计形式。
看完三件事❤️
如果你感觉这篇内容对你还蛮有帮忙,我想邀请你帮我三个小忙:
- 点赞,转发,有你们的『点赞和评论』,才是我发明的能源。
- 关注公众号『Java 斗帝』,不定期分享原创常识。
- 同时能够期待后续文章 ing????