关注前沿技术,分享热点话题,QCon全球软件开发大会三站同启,重磅回归!立即查看 了解详情

面向对象编程已死?

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:40 4097
用户头像
刘燕 InfoQ记者

发布了 395 篇内容, 共 130.7 次阅读, 收获喜欢 627 次。

关注

评论 16 条评论

发布
用户头像
从写面向对象到写面向过程,对于大型工程项目,简直是灾难
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
回复
用户头像
大部分的开发人员 连面向对象都没实现过 既没有享受到面向对象的好处 何谈解决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
回复
mellon 回复 mellon
不知道有没有误伤友军,你这句话有歧义。
2020 年 10 月 01 日 23:24
回复
我的意思是“解决了一些传统oop语言里的问题”
2020 年 10 月 01 日 23:45
回复
查看更多回复
没有更多评论了
发现更多内容

手把手带你玩转 openEuler | 如何安装 openEuler

openEuler

Linux 开源 操作系统 openEuler

合约跟单平台搭建,交易所跟单软件开发商

135深圳3055源中瑞8032

netfilter/iptables 原理

为为

Service Mesh Linux Kenel

教育场景方案升级| 打通业务前后端,少量开发快速上线(一):互动小班

ZEGO即构

在线教育 低代码

惊险的B站Java后端岗面试之旅,复盘面试经历及面试真题

Geek_71bb95

Java 程序员 面试 编程语言

详细讲解:python中的lambda与sorted函数

计算机与AI

Python

面向对象编程会被抛弃吗?这五大问题不容忽视

Java架构师迁哥

区块链USDT支付开发方案,USDT跨境支付搭建

135深圳3055源中瑞8032

解释一下==和equals的区别,你以为就这么简单?那你就草率了

小Q

Java 学习 架构 面试 基础

BATJ内部Java求职面试宝典,尤其应届生如果还没有学过那后悔去吧,也许你已经错过N多家大厂offer;

Java架构师迁哥

推荐一款MySQL开源客户端,免费+跨平台+使用便捷!

王磊

MySQL

容器技术为什么会这么流行

架构师修行之路

Docker 分布式 微服务 容器化

随机森林原理介绍与适用情况(综述篇)

计算机与AI

数据挖掘 机器学习 数据科学 随机森林 集成学习

腾讯技术官又曝神作,两份堪称‘千古绝唱’操作系统笔记现已被全网疯传

云流

编程 操作系统 计算机

Kubeless 函数部署遇到了问题,如何 Debug? | 玩转 Kubeless

donghui2020

Serverless kubeless

Anaconda安装使用和akshare库使用

MySQL从删库到跑路

Python 数据分析 Windows 10 Anaconda akshare

远程触发Jenkins的Pipeline任务的并发问题处理

小Q

Java 学习 编程 架构 并发

Python 疑难问题:[] 与 list() 哪个快?为什么快?快多少呢?

Python猫

Python 学习 编程 程序员

数字货币交易系统定制开发,区块链交易所

135深圳3055源中瑞8032

UBBF2020:智能联接,共创行业价值新增长

DT极客

图解 K8S 源码 - QoS 篇

郭旭东

Kubernetes Kubernetes源码

甲方日常 30

大橘子

工作 随笔杂谈 日常 心情

Tensorflow2.0安装使用

MySQL从删库到跑路

人工智能 tensorflow Anaconda Jupyter Notebook

打通Docker镜像发布容器运行流程

架构师修行之路

Docker 容器 分布式 微服务

详细分析定制企业应用的价格

Philips

敏捷开发 快速开发

极客时间架构师培训 1 期 - 第 4 周总结

Kaven

技术心得丨一种有效攻击BERT等模型的方法

华为云开发者社区

机器学习 AI

区块链钱包开发需要注意哪些问题?区块链数字钱包搭建

135深圳3055源中瑞8032

【BAT面试通关手册】覆盖Java相关29个技能,学完之后吊打面试官!

Java成神之路

Java 阿里巴巴 程序员 面试 编程语言

springboot+Redis+Shiro+MyBatis炸翔版CMS开源系统(代码+视频)

周老师

Java 编程 程序员 架构 面试

Underlay网络:如何立住可靠又支持大规模无收敛的“人设”

华为云开发者社区

云服务 交换机

云原生来袭,企业上云如何平滑迁移增效避险?

云原生来袭,企业上云如何平滑迁移增效避险?

面向对象编程已死?-InfoQ