首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

面向对象编程的兴衰

作为一种程序规划思想,OOP 在开始诞生之际就收到了广阔开发者的喜爱。可是在技术革新日益实践过程中,不少人发现面向目标的规划会使代码复杂化,难以了解并且难以测验,对此,后来有网友更是将 OOP 称之为是反模块化、反并行的,然后开启了一波又一波的吐槽模式。而论 OOP 在各个范畴中的应用时,其是否真的有幻想中那么糟糕?接下来,本文将带领咱们一读 OOP 的兴衰成长史。

作者 | Talin

译者 | 王艳妮,责编 | 屠敏

以下为译文:

不,面向目标编程(OOP)并没有消亡。但它远没有曾经那么流行了。

我记住其时在90年代,关于面向目标编程的教科书和计算机科学课程许多。其时那就是“风口”,下一波潮流。假如你没有以那种办法编程,你就不是一个优秀的程序员,或许至少是可悲地落后于时代发展潮流了。

其时,CS专业的学生以十分严厉和教条化的办法学习OOP。从业者们不仅被鼓舞以目标和类的形式构建他们的应用程序,乃至被以为应该依据目标和类来考虑问题空间。这样的做法被称为“面向目标的分析和规划”。

但是,随着时间的推动,人们开始意识到严厉的面向目标办法会带来许多问题。这些问题往往会使代码复杂化,难以了解并且难以测验。

事实证明,OOP在某些问题范畴的确比其他办法更出色。例如,OOP依然是构建用户界面(窗口和按钮)的最自然的办法。可是,企图使面向目标习惯联系数据库一直以来都简直是一场灾难。

以下是我所观察到的一些问题:

“鸭嘴兽”效应

实际国际并不总是能被整洁地区分为具有清晰特点定义的类(class)。例如,假设你创立了一个代表动物王国的类层次结构。现在,有爬虫类——冷血,有鳞片,卵生等等。还有哺乳动物——温血,有绒毛,胎生。以及鸟类,两栖动物,无脊椎动物等等

然后鸭嘴兽出现了,它好像不适合你的任何类别。你该怎么做呢?你是创立一个全新的类别呢,仍是重新考虑整个分类方案?这两种办法在作业量和程序复杂性方面都会产生巨大的本钱。

(感谢Anselm Hook创造了“鸭嘴兽效应”一词。)

深层次结构

我记住我在谷歌作业时,其时咱们有一个JavaScript库叫goog.ui,它被用于创立根据Web的用户界面。以下是此库中某个UI组件的承继层次结构示例:

class ToolbarColorMenuButton* inheritsfrom ColorMenuButton * inheritsfrom MenuButton * inheritsfrom Button * inheritsfrom Control * inheritsfrom Component * inheritsfrom EventTarget * inheritsfrom Disposable * inheritsfromObject

九个级别的类。太多了。

但状况会变得更糟。

许多高级类被只与少数子类相关的办法和特点“污染”。例如,“Control”类有超越90种办法(method)。它具有设置状况的办法,即便特定的子类是无状况的; 它有增加和删去子元素的办法,即便对control来说子元素没有意义。

这种复杂性的一个重要原因是,该库的作者企图安排组件的不同方面——例如组件是按钮仍是滑块,或许它是否有颜色——并经过将它们放入类的不同层次来完成这一点。

但实际上,这些不同方面彼此之间无关。咖啡杯是赤色的,和它是由陶瓷制成的,这是两个独立的特性。将赤色咖啡杯划入“赤色物品”类别,仍是将其放入“陶瓷制品”乃至“家居用品”类别中都是同样正确的。任何一个挑选都是恣意的,由于类别是由人头脑中的反映和社会结构决议的。

在Google作业的最后几年里,我创立了一个名为“Quantum Wiz”的新用户界面东西包,旨在代替goog.ui。咱们选用的规矩之一(以典型的Google风格,以方程式编写)是:

composition > inheritance

用直白的英语解说的话,这说的是:

“更偏向于选用组合的思路——也就是说,能够用更小的构建块来组装组件的功用——而不是承继作为代码重用的手段。”

因而,举个栗子,假如按钮有颜色,你将选用常规的“Button”目标(object)并向其增加“Color”方面(aspect),作为特点或子目标,而不是创立一个新的“Color Button”类。

作为这项使命的成果,新东西包的类层次结构相对较浅,假如我没记错的话,只要两三个级别。并且更简单了解和运用,也更强壮。

(感谢Malte Ubl向我介绍了组合大于承继的概念。)

目标不是实在的

Buckminster Fuller曾经说过:“事物并不存在”。他的意思是,事物之间的差异主要由人的成见导致。

例如,我坐的沙发是由分子力束缚在一起的原子的集合。但是,这些原子也会遭到房间内其他物体的影响——地毯,茶几,乃至是房间内的空气。沙发自身由各种部件组成——织物,木材,金属弹簧等等——它们也遭到分子力的约束。那么沙发是一个目标,仍是许多目标?也许世上只要一个目标——即咱们地点的这个宇宙。

由于人类的视觉和触觉在很大程度上只对外表特点有响应——比方颜色和质感——咱们倾向于根据外表现象对国际进行分类。相反,幻想一下,假如咱们能够直接感知周围物体内的分子组成。咱们可能会看到一个“铜”目标,代表着房屋中的一切布线和管道,一个“氮”目标,代表着房间的气体,一个“水”目标,一个“木头”目标,等等。

Fuller的观点是,咱们将国际“解析”为离散的“事物”的才能是恣意的,这更多地反映了咱们的人类心思而不是物理实际。

由于面向目标的承继触及将事物安排成类,所以它不能很好地模仿实际国际; 但它能很好地模仿人类考虑实际国际的办法。

办法(method)也不实在

我记住大约二十年前的一段小插曲,一位软件供应商的技术代表企图向我司的工程人员解说OOP。他企图争辩论,面向目标是一种模仿实际国际的办法,他给出的比如就像上面说的咖啡杯那种。他说杯子可能有个“drink()”的办法。

我记住的是,我对此有一个十分强烈的反响——我以为他所说的完全是胡言乱语。除了它的特定目的之外,一个物理目标能够有许多用途。我能够用咖啡杯作为镇纸或门挡; 这是否意味着它有一个“holdDownPapers()”或“keepDoorOpen()”办法?我能够将它用作武器,玩具或艺术品。我乃至能够将杯子碎成碎片,或将其研磨成粉末,并以创造性的办法运用其残余物。

(我以为这个可怜的家伙对我的辩驳感到吃惊。)

内部逻辑与外部逻辑

严厉的OOP风格的一个原则是,永久不可能从外部改动目标的内部状况。任何改动目标状况的事务或应用程序逻辑都有必要作为目标自身的办法完成。因而,举个栗子,假如要删去文本字段中的一切文本,则不能简单地进行:

textField.value = ""; // Settoemptystring

这将违反规矩。相反,你不得不这样:

textField.clear(); // Clear the content of the field

对于简单的操作,这没联系。可是这很简单过火,特别是正在进行的操作处理的是不同目标之间的复杂联系时。

有时候,假如算法独立于任何目标之外,反而更好。这真的是一个重要的问题:对于这个问题集,你更关心名词仍是动词?

下面这是一个详细的比如:最近我开始研讨编译器(编写编译器是我的一个爱好; 在我做游戏开发的时候,我发明晰许多脚本语言)。在曩昔,当我编写编译器时,我会选用十分严厉的OOP办法来规划内部数据结构。有各种类层次结构表示笼统语法树,表达式图,类型等。

一般,编译器经过一系列阶段或“传递”来处理这些数据结构,每一步的输出被送到下一步的输入中。

在曩昔,我倾向于依照引荐的OOP款式为每个操作中的每个目标设置一些逻辑。这带来不好的后果,当我增加更多步骤时,目标变得越来越复杂。

更糟糕的是,这使得给这些目标写单元测验反常困难。这些复杂的目标在被创立出来之前就需要许多的根底结构。这意味着为了测验一个目标,我有必要创立许多的脚手架来满足一切先决条件。

成果,我的测验覆盖率往往很差,由于编写测验是一项消耗许多精力的作业。

最近,我采取了另一种办法。在我最新的编译器中,一切这些内部数据结构都是“傻瓜型”的,意思是说它们所做的只是保存数据罢了,没有别的。用于操作和转换目标的一切代码都在这些目标的外部。

这对代码的安排有很大的优点。每个算法都集中在一个当地,而不是涣散在一堆源文件中。当我想测验一个特定的编译器操作时,我能够轻松地创立一些示例目标并将其供给给该操作。因而,我的测验写起来更简单了,所以我就能写更多的测验了,然后就能有比曾经更好的测验覆盖率了。

联系数据库

前面我提到过,以面向目标的办法处理联系数据库会有问题。目标联系映射(ORM)被一位谈论家称为计算机科学范畴的越战。(正告——那篇文章很长,很深奥,并且有点倾向性。)

我的大致感觉是,在处理大数据时,你不该将你的记录视为“目标”。联系数据库十分强壮,但它们供给的强壮功用并不是十分“类似目标”。我倾向于以为联系数据流更像流体,你能够运用代数运算的办法来区分,转换和组合数据。

(感谢Guido Van Rossum指出上面关于ORM的文章的链接。)

函数式编程

在曩昔十年左右的时间里,人们越来越关注函数式编程(FP)。与OOP一样,函数式编程不仅仅是单纯的一件事物,而是一套整体的风格上的原则。但是,它的关键是,尽管OOP专心于与目标进行交互或通讯,但在FP中,要点在于对它们的转换。这里的“转换”,意思是你获取一些目标,将它传递给一个函数,成果是一个全新的目标,代表着对输入内容所做的一些转换。原始目标要么被保留,要么被丢弃,但不会以任何办法被更改或修饰。

在我自己的编程过程中,我更喜爱“混合”办法,在某些当地运用FP技术,而在其他当地运用OOP技术。在处理某些问题上FP的确能大放异彩,但也有另一些问题上OOP是更明智的挑选。

我知道许多FP爱好者都热衷于“朴实”的函数式语言,其中一切目标都是不可变的,并且只能被转换,而不能被修改。但是,我发现朴实的办法倾向于把某些相对简单的编程实践变成谜题。我的意思是一些聪明的,但不那么显而易见的技巧,这能招引那种喜爱脑筋急转弯的人,可是对其他人来说却是完全无法了解的。

因而,我倾向于在合理的范围内运用FP,使阅览我代码的一般程序员都能了解。假如我想做任何抖机灵的事情,我会写一篇长篇谈论来解说我所做的事情,以及它是怎么work的(这满足了我夸耀的心思——我常常以为编程应该是一种表演艺术。)

所以,面向目标编程不再有昔日的光辉了。它依然是一个很好的东西,依然值得学习。但它已跌下神坛,你很难再看到有人能像 25 年前那样,以宗教般的狂热来吹捧它了。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20190813A09N8E00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券