当前位置: 首页 > 科技观察

面向对象编程死了吗?函数式编程瞄准了错误的对象

时间:2023-03-20 11:57:47 科技观察

1960年代的编程遇到了一个很大的问题:当时的计算机还没有那么强大,需要通过某种方式在数据结构和进程之间分配容量。这意味着,如果您有大量数据,那么如果不将计算机推向极限,您将无能为力。另一方面,如果你需要做很多事情,你不能使用太多的数据,否则电脑会永远占用空间。然后,AlanKay在1966年或1967年左右提出理论,您可以使用不共享数据但通过消息传递进行通信的封装微型计算机。这允许更经济地使用计算资源。尽管这个想法很巧妙,但直到1981年面向对象编程才成为主流。但从那时起,它并没有停止吸引软件开发新手和老手。面向对象编程的程序员一如既往地忙碌。但近年来,这种已有十年历史的范式受到越来越多的批评。难道经过40年的面向对象编程,技术已经超越了这种范式?耦合函数与数据是否荒谬?面向对象编程的主要思想很简单:尽量把一个程序分解成功能性的整体。除此之外,将数据片段与那些仅用于相关数据的函数结合起来。请注意,这仅涵盖封装的概念。也就是说,位于对象内部的数据和函数对外部是不可见的,只能通过消息(通常称为getter和setter函数)与对象的内容进行交互。继承和多态性不包括在最初的想法中,但对于今天的面向对象编程来说是必需的。继承,这基本上意味着开发人员可以定义一个具有其父类所具有的所有属性的子类,直到1976年才被引入——在面向对象编程的概念出现十年之后。十年后,多态性进入了面向对象编程。基本上,这意味着一个方法或对象可以用作其他方法的模板。从某种意义上说,这是继承的泛化,因为并非原始方法或对象的所有属性都需要转移到新实体;相反,可以选择性地覆盖属性。多态的特别之处在于,即使两个实体在源代码中相互依赖,被调用实体的工作方式更像是一个插件。这让开发人员的生活更轻松,他们不再需要担心运行时依赖性。值得一提的是,继承和多态性并不是面向对象编程所独有的。真正的区别是封装的数据和它所属的方法。在计算资源远比今天稀缺的时代,这是一个天才的想法。面向对象编程并不好笑,它使编码变得容易得多。面向对象编程中的前五个问题面向对象编程在代码首次出现时改变了开发人员查看代码的方式。在80年代之前,面向过程的编程通常以机器为中心,开发人员需要很好地理解计算机的工作原理才能编写出好的代码。通过封装数据和方法,面向对象编程使软件开发更加以人为中心。与人的直觉一致,方法drive()属于数据组car,而不属于teddybear组。这在继承发生时也是直观的。Hyundai是car的一个子类并且具有相同的属性,这是完全有道理的,但PooTheBear不是。这听起来像是一台强大的机器。但问题是,只懂面向对象代码的程序员会以这种方式思考他们所做的一切。就像人们到处都看到钉子一样,因为他们只有一把锤子。正如我们将在下面看到的,当你的工具箱里只有一把锤子时,它可能会导致致命的问题。大猩猩丛林香蕉问题如果您正在设置一个新程序并正在考虑设计一个新类。您可能会想起您为另一个项目创建的一个整洁的小类,并意识到它非常适合您当前正在尝试做的事情。没问题!旧项目中的类可以在新项目中重用。除了该类实际上可能是另一个类的子类之外,所以现在还需要包括父类。然后你意识到父类也依赖于其他类,最终包含大量代码。Erlang的创造者JoeArmstrong的这句话非常有名:“面向对象编程语言的问题在于它们拥有它们所携带的所有隐式环境。你想要香蕉,但你得到的是一只大猩猩香蕉和整个丛林。”这是该方法的一个很好的说明。类可以重用,其实这大概就是面向对象编程的主要优点。但是也不要走极端,有时候写一个新的类总比为了写重复的代码而增加很多依赖要好。要灵活,不要死板地遵循某种范式。脆弱的基类问题如果您已经成功地将另一个项目中的类重用于新代码,基类会发生什么变化?它甚至可以在你不碰它的情况下破坏整个代码。您可能有一个项目在某一天大放异彩,而在下一天却被打退堂鼓,因为有人更改了基类中的一个微小细节,而这些细节最终对项目至关重要。您使用继承的次数越多,潜在的维护工作就越多。因此,尽管重用代码在短期内似乎非常有效,但从长远来看,它的成本可能非常高。DiamondProblemInheritance是一个可爱的小东西,你可以从一个类继承属性并将它们转移到其他类。但是如何结合两个不同类的属性呢?可能不行,至少不能用简洁的方式,比如Copier类。(作者借用了CharlesScalfani的热门文章《再见,面向对象的编程》的这个例子,以及这里出现的问题的一些信息。)复印机扫描文档的内容并打印在空白纸上,它应该是Scanner或Scanner的孩子打印机?上课?根本没有好的答案。尽管这个问题不会破坏代码,但它经常发生,足以令人沮丧。层次问题在菱形问题中,问题是Copier是哪个类的子类。但是我没有说完,有一个简单的解决方案。假设Copier是父类,Scanner和Printer是只继承部分属性的子类。这变得非常简单。但是如果复印机只能黑白复印,而打印机也可以彩色打印呢?从这个意义上讲,打印机不包括复印机吗?如果打印机连接到WiFi但复印机没有连接怎么办?您在类上堆积的属性越多,建立适当的层次结构就越困难。实际上,在处理属性集群时,Copier共享一些但不是所有Printer的属性,反之亦然。如果你试图将它放在一个层次结构中,而且它是一个大型复杂项目,它可能会导致混乱。不要混淆层次结构,否则你可能会搞得一团糟。参考某人可能会说的问题,那么我们将进行没有层次结构的面向对象编程。相反,我们可以使用属性集群并根据需要继承、扩展或覆盖属性。这会有点混乱,但它会准确地表示手头的问题。还有一个问题。封装的全部意义在于使数据片段彼此安全,从而提高计算效率。如果没有严格的层次结构,这是行不通的。如果对象A通过与另一个对象B交互来覆盖层次结构,会发生什么情况?A与B的关系无关紧要,除非B不是直系父母。然后A必须包含对B的私有引用,否则将无法进行交互。但是,如果A包含B的孩子也有的信息,则可以在多个地方修改该信息。因此,关于B的信息不再安全,封装被破坏。虽然很多面向对象的程序员都使用这种架构来构建程序,但那不是面向对象的编程,只是乱七八糟。单一范式的危险这五个问题的共同点是它们在解决方案不是最优的情况下实施继承。由于继承甚至不包括在面向对象编程的原始形式中,我不会将这些问题称为面向对象编程的固有问题,它们只是太教条的例子。但不仅仅是面向对象的编程会被夸大。在纯函数式编程中处理用户输入或将消息打印到屏幕是极其困难的。对于这些目的,面向对象或过程编程要好得多。仍然有开发人员试图将这些东西实现为纯函数,并将他们的代码分解成几十行,没人能看懂。使用另一种范例,他们可以轻松地将代码减少到几行可读代码。范式有点像宗教,它们都有一些合理性,耶稣、穆罕默德和佛陀说了一些很酷的话。然而,如果你继续遵循教条,你最终可能会让你自己的生活和你周围的人的生活变得悲惨。编程范式也是如此。毫无疑问,函数式编程越来越受欢迎,而面向对象编程在过去几年受到了一些严厉的批评。了解新的编程范例并在适当的时候使用它们是有意义的。如果面向对象编程是使开发人员无论走到哪里都能看到钉子的锤子,那么它是否就是将锤子扔出窗外的原因呢?没有。您在工具箱中添加一把螺丝刀,也许是一把刀或一把剪刀,您只需根据手头的问题选择工具即可。函数式编程和面向对象编程的程序员不应该像对待宗教一样对待编程范式。它们是工具,它们都在某个地方起作用,你使用什么只取决于要解决的问题。一个大问题:我们是否正处于一场新革命的风口浪尖?最终,关于函数式编程和面向对象编程的(相当激烈的)争论归结为:是否有可能进入面向对象编程时代的末日?随着越来越多的问题出现,函数形式化编程通常是更有效的选择。比如数据分析、机器学习和并行编程,在这些领域投入越多,就会越喜欢函数式编程。但纵观现状,面向对象的程序员有十几种产品,而函数式编码器有一种。这并不意味着你不会喜欢这份工作,现在函数式编程开发人员仍然非常稀缺。最有可能的是,面向对象编程将继续存在十年左右。函数式编程当然越来越受欢迎,但这并不意味着应该放弃面向对象的编程。将面向对象编程作为保留技能还是很有优势的。所以在接下来的几年里不要将面向对象编程从你的工具箱中扔掉,但要确保它不是你唯一的工具。本文转载自微信公众号“读芯”,可通过以下二维码关注。转载本文请联系核心阅读公众号。