写点什么

面向对象编程已死?

2020 年 9 月 30 日

面向对象编程已死?

本文最初发表于 Towards Data Science 博客,经原作者 Rhea Moutafis 授权,InfoQ 中文站翻译并分享。


在 20 世纪 60 年代,编程有一个很大的问题:计算机还远没有那么强大,而且不知何故,它们还需要在数据结构和过程之间以某种方式分配容量


这意味着,如果你有大量数据的话,你就不能用它做那么多的事情,否则就会把计算机逼到极限。而另一方面,如果你需要做很多事情,你就不能使用太多的数据,否则计算机将会耗费大量时间。


后来,Alan Lay 在 1966 年或 1967 年提出了一个理论,即人们可以使用封装起来的微型计算机,这种计算机并不共享数据,而是通过消息传递并进行通信。通过这种方式,可以做到更经济地使用计算资源。


尽管这个想法很有创意,但直到 1981 年,面向对象编程才成为主流。然而,从那时起,它就一直吸引着新入门的和经验丰富的软件开发人员。面向对象的程序员市场一如既往地繁忙。


但近年来,这一有着十年历史的范式受到了越来越多的批评。有没有可能,在面向对象编程普及四十年后,技术已经超越了这种方式?


耦合函数与数据有那么愚蠢吗?


面向对象编程背后的主要思想非常简单:试图将一个程序分解成与整体一样强大的部分。接下来,你需要将数据片段和那些仅在相关数据上使用的函数耦合在一起。


请注意,这仅仅只是涵盖了封装的概念,也就是说,位于对象内部的数据和函数对外部都是不可见的。人们只能通过消息与对象的内容进行交互,通常称为gettersetter函数。


最初的想法中没有包含的,但被认为是当今面向对象编程必不可少的是:继承和多态性。继承基本上意味着开发人员可以定义子类,这些子类拥有其父类所拥有的所有属性。直到 1976 年才被引入到面向对象编程中,也就是在其概念提出十年之后。


十年后,多态性才进入面向对象编程。由基本概念而言,多态性意味着一个方法或一个对象可以作为其他方法或对象的模板。从某种意义上说,这是继承的泛化,因为并非原始方法或对象的所有属性都需要传输到新实体;相反,你可以选择重载属性。


多态性的特殊之处在于,即使源代码中两个实体相互依赖,被调用的实体的工作方式也更像插件。这使得开发人员的工作更加轻松,因为他们不必担心运行时的依赖关系。


值得一提的是,继承和多态性并不是面向对象编程所独有的。真正的不同之处在于封装数据片段和属于它们的方法。在计算资源比今天稀缺的时代,这真的是一个天才的想法。


面向对象编程中的五大问题


面向对象编程一经问世,它就改变了开发人员看待代码的方式。在 20 世纪 80 年代以前盛行的过程式编程,是非常面向机器的。开发人员需要对计算机如何工作有相当多的了解,才能写出好代码。


通过封装数据和方法,面向对象编程使软件开发更加以人为本。它符合人类的直觉,方法drive()属于数据组car,但不属于组teddybear


继承出现的时候,那也是直观的。Hyundai汽车是car的一个子群,有着相同的属性,这是完全合理的,但PooTheBear却并非如此。


这听起来像是一台强大的机器。然而,问题是,只知道面向对象编程的程序员会将这种思维方式强加于他们所做的每件事上。就像人们看到处都是钉子一样,因为他们只有一把锤子。正如我们将在下面看到的,当你的工具箱中只有一把锤子的时候,这可能会导致致命的问题。


香蕉大猩猩丛林问题


假设你正在设置一个新程序,并且你正在考虑设计一个新的类。然后,你回想起为另一个项目创建的一个简洁的小类,你意识到它对你目前正在尝试做的事情来说是非常完美的。


没问题!你可以在新项目中重用旧项目中的类。


除了这个类实际上可能是另一个类的子类外,所以现在还需要包含父类。然后,你意识到父类也依赖于其他类,如此一来,最终会包含大量的代码。


Erlang 的创造者 Joe Armstrong 曾说过一句名言


面向对象编程的问题是,默认带有环境。你只想要一个香蕉,但是得到了一只拿着香蕉的大猩猩,甚至还有整个丛林。


这几乎说明了一切。重用类是很好的做法。事实上,它可以成为面向对象编程的一个主要优点。


但不要走极端。有时候,为了避免 DRY(don’t repeat yourself 不要重复自己),你最好编写一个新的类,而不是包含大量的依赖项。


脆弱的基类问题


假设你已经成功地为新代码重用了来自另一个项目的一个类。如果基类发生变化,会发生什么情况?


它有可能会破坏你的整个代码。甚至你可能连碰都没有碰过。但是,有一天你的项目工作得很顺利,但第二天就不这样了,因为有人更改了基类中的一个小细节,而这个细节最终对你的项目非常重要。


使用继承的次数越多,可能需要进行的维护就越多。因此,尽管重用代码在短期内看起来非常有效,但从长远来看,它可能会付出高昂的代价。


钻石问题


继承是可爱的小东西,我们可以把一个类的属性转移给其他类。但如果你想混合两个不同类的属性呢?


嗯,你做不到。至少不会是以一种优雅的方式。例如,以类Copier为例。(我从 Charles Scalfani 的文章《再见,面向对象编程》(Goodbye, Object Oriented Programming)中借用了这个例子,以及一些关于这里提出的问题的信息)复印机扫描文档内容并将其打印在控制上。那么它应该是Scanner的子类,还是Printer的子类呢?


根本没有好的答案。即使这个问题不会破坏你的代码,它也会经常出现,令人沮丧。


层次问题


在钻石问题中,问题是Copier是哪个类的子类。但我骗了你——因为有一个很好的解决方案。假设Copier是父类,ScannerPrinter是仅继承属性子集的子类。问题解决!


这很好。但是,如果你的Copier只是黑白复印机,而你的Printer能处理彩色,那怎么办呢?从这个意义上说,Printer不就是Copier的泛化吗?如果Printer连接到 WiFi,但Copier没有连接,又该怎么办?


在类上堆积的属性越多,建立适当的层次结构就越困难。实际上,你要处理的是一组属性,其中Copier共享Printer的一些属性,但不是所有属性,反之亦然。如果你试图将它固定到层次结构中,而你有一个庞大而复杂的项目,这可能会导致异常混乱的灾难。


引用问题


你可能会说,好吧,那我们就只做面向对象的编程,不带层次结构。相反,我们可以使用属性集群,并根据需要继承、扩展或覆盖属性。当然,这可能会有点混乱,但它将是手头问题的准确描述。


只有一个问题。封装的全部意义在于保证数据片段之间的安全,从而使计算更加高效。如果没有严格的层次结构,这是行不通的。


考虑如果一个对象A通过另一个对象B交互来覆盖层次结构会发什么。AB有什么关系并不重要,重要的是B不是直接的父类。那么A就必须包含对B的私有引用。否则,它将无法交互。


但是,如果A包含B的子类也拥有的信息,那么这些信息就可以在多个地方被修改。因此,关于B的信息已不再安全,并且封装也被破坏了。


尽管许多面向对象的程序员使用这种架构来构建程序,但这不是面向对象编程。只是一团糟。


单一范式的危险


这五个问题的共同之处在于,它们在不是最佳解决方案的地方实现了继承。由于继承甚至没有包含在面向对象编程的原始形式中,我不会将这些问题称为面向对象的固有问题。它们只是一个教条走得太远的例子。


但是,不仅仅是面向对象编程可能做过头了。在纯函数式编程中,在屏幕上处理用户输入或打印消息是极其困难的。对于这些目的,面向对象或过程化编程要更好一些。


尽管如此,仍有一些开发人员试图将这些东西作为纯函数来实现,并将他们的代码扩展到几十行,以至于没有人能够理解。使用另一种范式,他们可以轻松地将代码简化为几行可读的代码。


范式有点像宗教。它们在适度的情况下是好的:可以说,耶稣、穆罕默德和佛陀都说过一些很酷的话。但是如果你跟随他们到最后一个小细节,你可能最终会让自己和周围人们的生活变得相当悲惨。


编程范式也是如此。毫无疑问,函数式编程越来越受欢迎,而面向对象编程在过去几年来受到了一些严厉的批评


了解新的编程范式并在适当的时候使用它们是有意义的。如果说,面向对象编程是让开发人员无论走到哪里都能看到钉子和锤子,那这是不是将锤子扔出窗外的理由呢?不是的。你可以在工具箱中添加一把螺丝刀,或者一把小刀、一把剪刀,然后根据手头的问题来选择工具。


函数式和面向对象的程序员一样,不要将你的范式当做宗教来对待。它们只是工具,都有自己的用途。你使用什么工具应该只取决于你正在解决什么问题。


最大问题是:我们是否正处于一场新革命的风口浪尖?


归根结底,关于函数式编程与面向对象编程的争论(不可否认,争论是相当激烈的)归结为这样一个问题:面向对象编程的时代会不会走到尽头?


在函数式编程通常是更有效的选择的情况下,出现了越来越多的问题。请想一想数据分析、机器学习和并行编程。你越是深入这些领域,就会越喜欢函数式编程。


但是如果你看一下现状,面向对象的程序员有很多工作机会,而函数式程序员只有寥寥可数的工作机会。这并不意味着如果你喜欢后者就找不到工作;现在函数式开发人员仍然相当稀缺。


最有可能的情况是,面向对象编程将再持续十年左右。当然,先锋派是函数式编程,但这并不意味着你应该抛弃面向对象编程。面向对象编程仍然是令人难以置信的好东西。


因此,在接下来的几年,不要将面向对象编程从你的工具箱扔出去。但要确保它不是你唯一的工具。


作者介绍:


Rhea Moutafis,正在攻读暗物质物理学博士学位。热爱艺术、音乐及美好事物。


原文链接:


https://towardsdatascience.com/object-oriented-programming-is-dead-wait-really-db1f1f05cc44


2020 年 9 月 30 日 22:404457
用户头像
刘燕 InfoQ记者

发布了 484 篇内容, 共 150.7 次阅读, 收获喜欢 872 次。

关注

评论 18 条评论

发布
用户头像
从写面向对象到写面向过程,对于大型工程项目,简直是灾难
2020 年 10 月 13 日 15:55
回复
用户头像
原文标题是「Object-oriented programming is dead. Wait, really?」,InfoQ是个技术媒体,请不要学某些新媒体用夸张的标题博眼球……
2020 年 10 月 12 日 14:39
回复
用户头像
脑残标题党,无病呻吟
2020 年 10 月 08 日 17:09
回复
用户头像
前端代表框架react,核心理念就是函数式编程
2020 年 10 月 07 日 16:04
回复
你可以看看如果要写一个react组件应该怎么定义
2020 年 10 月 12 日 10:09
回复
你是想说还要用class吗?听说过hooks吗
2020 年 10 月 26 日 18:32
回复
举一个简单的例子:如果要用Java开发一个项目,肯定是基于Class的,但是在解析一些数据的时候,我可以基于表达式或者函数的形式,这里类是主食还是函数式是主食?
ReactHooks是官方的一种设计理念,取决于你如何用它。 在面对大型项目(无论是前端还是后端)构建的时候,以类核心为思想的建模方式,是符合人的心智模型,不是说函数式不符合心智模型. 在前端大型项目中, Class是主食,React Hook是辅食,小甜点,会给你意想不到的惊喜
2020 年 10 月 28 日 10:31
回复
用户头像
大部分的开发人员 连面向对象都没实现过 既没有享受到面向对象的好处 何谈解决OO带来的问题
2020 年 10 月 04 日 11:45
回复
用户头像
这篇文章的作者稍微有点脑残,但凡稍有规模化的项目,都是基于类这个概念的
2020 年 10 月 04 日 10:06
回复
并非“都”,只能说绝大部分。要知道,OOP 也不只有 class-based 一种
2020 年 10 月 07 日 22:51
回复
请问你的实体类有行为吗?请问你用UML去做系统分析设计吗?如果你写的都是定义个结构体做CRUD你就别扯OO,OOA、OOD、OOP的学问大的去了。
2020 年 10 月 09 日 02:49
回复
用户头像
合适的场景选择合适的工具很重要,go 会不会让新一代程序员对面向对象封装的思想再次丢失 ?
2020 年 10 月 03 日 06:14
回复
用户头像
用对的方法解决对的问题,不要老是搞这种非此即彼的暴论!
2020 年 10 月 01 日 14:29
回复
用户头像
这么一看go语言简直就是奔着oop的弊病去设计的
2020 年 10 月 01 日 03:58
回复
呵呵。
2020 年 10 月 01 日 23:22
回复
不知道有没有误伤友军,你这句话有歧义。
2020 年 10 月 01 日 23:24
回复
我的意思是“解决了一些传统oop语言里的问题”
2020 年 10 月 01 日 23:45
回复
查看更多回复
没有更多了
发现更多内容

ARTS打卡计划_第一周

叫不醒装睡的人

ARTS 打卡计划

体验一次简洁的代码

你当像鸟飞往你的山

Element-UI实战系列:Table+Pagination组件实现已选和全选功能

brave heart

Vue 前端 Element

ARTS 打卡 WEEK2

编程之心

ARTS 打卡计划

区块链技术大显身手,仅用20分钟就打完一场官司!

CECBC区块链专委会

CECBC 区块链技术 数字版权 存证

clang-format 使用与集成介绍

Geek_101627

2万字长文带你细细盘点五种负载均衡策略。

why技术

Java 负载均衡 源码分析 dubbo java面试

你会写测试用例吗

鱼贩

ARTS Week1

姜海天

Apache DolphinScheduler新特性与Roadmap路线

海豚调度

数据中台 大数据任务调度 工作流调度 海豚调度 数据湖调度

如何设置线程池参数?美团给出了一个让面试官虎躯一震的回答。

why技术

Java 源码分析 面试题 线程池

Mysql索引不会怎么办?6000字长文教会你

Super~琪琪

MySQL 数据库 sql 索引

【openlayers】在vue中使用ol

学习委员

Java html Vue 地图 openlayers

ARTS-1

你当像鸟飞往你的山

ARTS 打卡计划

那些会阻碍程序员成长的细节[2]

码闻强

程序员 程序人生

MAC OS 下 HomeBrew 使用

耳东

macos brew homebrew

愚蠢写作术(1):怎么让你的标题被读者忽视

史方远

个人成长 写作

如何使用 Apache CXF 快速实现一个 WebService

Rayjun

Java WebService CXF

[ARTS打卡] week 01

Mau

ARTS 打卡计划

ARTS打卡第一周

GKNick

时代在变,产品运营能力很重要

夜来妖

程序员人生 程序人生 产品经理 产品推荐 程序媛

ARTS week 2

刘昱

后疫情时代,区块链的发展迎来曙光!

CECBC区块链专委会

CECBC 区块链技术

ARTS-01

NIMO

ARTS 打卡计划 ARTS活动

John 易筋 ARTS打卡Week 02

John(易筋)

ARTS 打卡计划 ARTS活动 arts

深入计算机底层,从几本靠谱的书开始

HackMSF

计算机工作原理

【ARTS打卡】Week01

Rex

学习

ARTS week2

紫枫

ARTS 打卡计划

Java日志门面系统

泛泛之辈

Java 日志 slf4j

MySQL 可重复读,差点就我背上了一个 P0 事故!

楼下小黑哥

Java MySQL

我的编程之路 -6(新时代)

顿晓

android 编程之路 时代

NLP领域的2020年大事记及2021展望

NLP领域的2020年大事记及2021展望

面向对象编程已死?-InfoQ