15

据我了解,OOP 是大型项目最常用的范例。我还知道大型系统的一些较小的子集使用其他范例(例如 SQL,它是声明性的),并且我还意识到在较低级别的计算 OOP 是不可行的。但在我看来,通常更高级别的解决方案几乎总是以 OOP 方式组合在一起。

是否存在真正的非 OOP 范式实际上是大规模解决方案的更好选择的场景?还是这些天闻所未闻?

自从我开始学习 CS 以来,我就一直想知道这一点;很容易让人觉得 OOP 是某种编程的必杀技,永远不会被超越。

4

12 回答 12

10

在我看来,OOP 被如此广泛使用的原因并不是因为它是适合这项工作的工具。我认为更多的是可以以客户理解的方式向客户描述解决方案。

汽车是具有发动机的车辆。这就是编程和现实世界的结合!

很难理解任何可以如此优雅地适应编程和现实世界的东西。

于 2009-06-03T14:17:44.297 回答
7

Linux 是一个非常非 OOP 的大型项目。而且它也不会从中获得很多好处。

我认为 OOP 对它有很好的影响,因为它与良好的编程实践相关联,如封装、数据隐藏、代码重用、模块化等。但这些优点绝不是 OOP 独有的。

于 2009-06-03T14:28:24.867 回答
4

您可能会看一下由 Joe Armstrong 编写的 Erlang。

维基百科:

“Erlang 是一种通用并发编程语言和运行时系统。Erlang 的顺序子集是一种函数式语言,具有严格的求值、单一赋值和动态类型。”

乔·阿姆斯特朗:

“因为面向对象语言的问题在于,他们拥有随身携带的所有这些隐式环境。你想要一根香蕉,但你得到的是一只拿着香蕉和整个丛林的大猩猩。”</p>

于 2010-04-08T14:28:58.930 回答
3

OOP 的承诺是代码重用和更容易维护。我不确定它是否已交付。我认为 dot net 之类的东西与我们过去从各个供应商那里获得的 C 库非常相似。如果需要,您可以调用该代码重用。至于维护坏代码就是坏代码。OOP 没有帮助。

于 2009-06-03T14:33:01.737 回答
2

我是 OOP 的最大粉丝,我每天都在练习 OOP。这是编写代码最自然的方式,因为它类似于现实生活。

不过,我意识到 OOP 的虚拟化可能会导致性能问题。当然,这取决于您的设计、您选择的语言和平台(例如,用基于垃圾收集的语言(如 Java 或 C#)编写的系统可能比用 C++ 编写的系统性能更差)。

我想在实时系统中,过程编程可能更合适。

于 2009-06-03T14:21:43.070 回答
2

请注意,并非所有声称是 OOP 的项目实际上都是 OOP。有时大部分代码都是程序性的,或者数据模型是贫乏的,等等......

于 2009-06-03T14:27:26.180 回答
1

看到这个这个。显然,您可以将 C# 与五种不同的编程范例一起使用,C++ 与三种等。

软件构建与基础物理学不同。物理学努力使用可能受到新实验数据和/或理论挑战的范式来描述现实。物理学是一门寻找“真理”的科学,而软件构建却没有。

软件建设是一门生意。你需要有生产力,即实现一些有人愿意花钱的目标。使用范式是因为它们对于有效地生产软件很有用。你不需要每个人都同意。如果我做 OOP 并且它对我来说效果很好,我不在乎如果我有时间和金钱来学习“新”范式是否可能对我有用 20%,然后重新考虑整个软件结构。我从头开始工作并重新设计它。

此外,您可能使用了另一种范式,我仍然会很高兴,就像我可以通过经营日本料理餐厅赚钱而您可以在隔壁的墨西哥料理餐厅赚钱一样。我不需要和你讨论日本菜是否比墨西哥菜好。

于 2009-06-04T10:24:24.520 回答
1

我怀疑 OOP 很快就会消失,它太适合我们的问题和心理模型了。

不过,我们开始看到的是多范式方法,将声明性和功能性的想法结合到面向对象的设计中。大多数较新的 JVM 语言(JavaFX、Scala、Clojure 等)以及 .net 平台上的 LINQ 和 F# 都是一个很好的例子。

需要注意的是,我在这里不是在谈论替换 OO,而是在谈论补充它。

  • JavaFX 表明,声明式解决方案超越了 SQL 和 XSLT,还可用于绑定 GUI 中可视组件之间的属性和事件

  • 对于容错和高并发系统,函数式编程非常适合,正如 Ericsson AXD301(使用 Erlang 编程)所证明的那样

所以......随着并发变得越来越重要和 FP 变得越来越流行,我想不支持这种范式的语言会受到影响。这包括许多当前流行的,例如 C++、Java 和 Ruby,尽管 JavaScript 应该可以很好地应对。

于 2010-05-26T09:14:27.310 回答
1

Zyx,你写道,“大多数系统都使用关系数据库......”

恐怕没有这样的事。关系模型明年将有 40 年历史,至今仍未实施。我认为您的意思是“SQL 数据库”。您应该阅读 Fabian Pascal 的任何内容,以了解关系 dbms 和 SQL dbms 之间的区别。

“ ...通常选择关系模型是因为它很受欢迎,”

没错,它很受欢迎。

“ ...工具的可用性,”

唉,没有必要的主要工具:关系模型的实现。

“ 支持,”

是的,关系模型有很好的支持,我敢肯定,但它完全不受 dbms 实现的支持。

“以及关系模型实际上是一个数学概念的事实,”

是的,它是一个数学概念,但是,它没有被实施,它在很大程度上仅限于象牙塔。弦理论也是一个数学概念,但我不会用它来实现系统。

事实上,尽管它是一个数学概念,但它肯定不是一门科学(如计算机科学),因为它缺乏任何科学的第一个要求:它是可证伪的:没有实现关系 dbms 来检查它的索赔。

是纯蛇油。

“……与面向对象相反。”

与OOP相反,关系模型从未实现过。

购买一本关于 SQL 的书并获得生产力。

将关系模型留给非生产性的理论家。

于 2009-06-04T10:10:38.867 回答
0

使用 OOP 使代码更易于管理(如修改/更新/添加新功能)和理解。对于较大的项目尤其如此。因为模块/对象封装了它们的数据和对该数据的操作,所以更容易理解功能和全局。

OOP 的好处是更容易(与其他开发人员/管理人员/客户)讨论 LogManager 或 OrderManager,每个都包含特定的功能,然后描述“将数据转储到文件中的一组方法”和“方法跟踪订单详细信息”。

所以我认为 OOP 对大型项目很有帮助,但总会有新的概念出现,所以在未来继续寻找新的东西,评估并保留有用的东西。

于 2009-06-03T14:18:39.847 回答
0

人们喜欢将各种事物视为“对象”并对其进行分类,因此毫无疑问,OOP 如此受欢迎。但是,在某些领域 OOP 并没有获得更大的普及。大多数系统使用关系数据库而不是客观的。即使第二个模型拥有一些显着的记录并且对于某些类型的任务更好,但由于其受欢迎程度、工具的可用性、支持以及关系模型实际上是一个数学概念这一事实,关系模型还是被异常选择了。哎呀。

我从未见过 OOP 的另一个领域是软件构建过程。所有的配置和制作脚本都是程序化的,部分原因是 shell 语言缺乏对 OOP 的支持,部分原因是 OOP 对于此类任务来说太复杂了。

于 2009-06-03T15:37:51.757 回答
0

我的观点有点争议,但我认为 OOP,至少是现在流行的一种,对于在我的特定领域(VFX,在场景组织和应用方面有点相似)生产最大规模的软件没有帮助状态为游戏)。我发现它在中小规模上非常有用。在这里我必须要小心一点,因为我过去曾邀请过一些暴徒,但我应该承认这是我在特定领域的狭隘经验。

我经常发现的困难是,如果您拥有所有这些封装数据的小型具体对象,那么它们现在都希望彼此交谈。它们之间的交互可能会变得非常复杂,就像这样(在跨越数千个对象的实际应用程序中要复杂得多):

在此处输入图像描述

这不是与耦合直接相关的依赖图,而是“交互图”。可以有抽象来将这些具体对象彼此分离。Foo可能不会直接交谈Bar。相反,它可能会通过IBar或类似的方式与它交谈。该图仍将连接Foo到,Bar因为尽管已解耦,但它们仍会相互通信。

构成它们自己的小生态系统的中小型对象之间的所有这些通信,如果应用于我领域中大型代码库的整个规模,可能会变得非常难以维护。而且它变得如此难以维护,因为很难推断对象之间的所有这些交互会发生什么,比如副作用。

相反,我发现有用的是将整个代码库组织成完全独立的、庞大的子系统,这些子系统可以访问一个中央“数据库”。然后每个子系统输入和输出数据。其他一些子系统可能会访问相同的数据,但没有任何一个系统直接相互通信。

在此处输入图像描述

... 或这个:

在此处输入图像描述

...并且每个单独的系统不再尝试封装状态。它不会试图成为自己的生态系统。相反,它在中央数据库中读取和写入数据。

当然在每个子系统的实现中,他们可能会使用一些对象来帮助实现它们。这就是我发现 OOP 在这些子系统的实现中非常有用的地方。但是这些子系统中的每一个都构成了一个相对中小型的项目,不是太大,而且我发现OOP非常有用。

最少知识的“流水线编程”

这允许每个子系统只专注于做自己的事情,而几乎不知道外部世界正在发生什么。专注于物理的开发人员可以坐下来使用物理子系统,对软件的工作原理知之甚少,除了有一个中央数据库,他可以从中检索运动组件(只是数据)之类的东西,并通过将物理应用于该数据来转换它们。这让他的工作变得非常简单,这样他就可以做他最擅长的事情,而对其他所有事情的运作方式知之甚少。输入中央数据并输出中央数据:这是每个子系统必须正确执行的所有操作,其他一切才能正常工作。这是我在我的领域中发现的最接近“流水线编程”的东西

由于每个子系统的关注范围很窄,测试仍然非常简单。我们不再使用依赖注入模拟具体对象,而是生成与特定系统相关的最少量数据并测试特定系统是否为给定输入提供正确的输出。由于要测试的系统如此之少(只有几十个可以组成一个复杂的软件),它还大大减少了所需的测试数量。

打破封装

然后,该系统变成一个相当扁平的管道,通过独立的子系统转换中央应用程序状态,这些子系统实际上不知道彼此的存在。有时可能会将一个中心事件推送到另一个系统处理的数据库,但另一个系统仍然不知道该事件来自何处。我发现这是至少在我的领域中解决复杂性的关键,而且它是通过实体组件系统有效地实现的。

然而,它类似于更接近大规模程序或函数式编程的东西,以解耦所有这些子系统并让它们在对外部世界了解最少的情况下工作,因为我们打破封装以实现这一点并避免要求系统与每个其他。当您放大时,您可能会发现您的对象份额被用于实现这些子系统中的任何一个,但在最广泛的范围内,这些系统类似于 OOP 以外的东西。

全球数据

我不得不承认,起初我对将 ECS 应用到我所在领域的建筑设计非常犹豫,因为首先,据我所知,在流行的商业竞争对手(3DS Max、SoftImage 等)中还没有这样做过,其次,它看起来像是一大堆全球可访问的数据。

然而,我发现这不是一个大问题。我们仍然可以非常有效地维护不变量,甚至可能比以前更好。原因在于 ECS 将所有内容组织成系统和组件的方式。您可以放心,音频系统不会尝试改变运动组件,例如,即使在最骇人听闻的情况下也不会。即使团队协调不善,ECS 也不太可能退化为您无法推断哪些系统访问哪个组件的情况,因为这在纸面上相当明显,并且几乎没有任何理由可以访问某个系统一个不合适的组件。

相反,它通常消除了许多以前对 hacky 事物的诱惑,因为数据是开放的,因为在我们以前的代码库中在松散的协调和紧缩时间下完成的许多 hacky 事情是在仓促尝试 X 射线抽象并尝试访问对象生态系统的内部。由于人们匆忙试图获取并使用他们想要访问的数据进行操作,抽象开始变得泄漏。他们基本上是在尝试访问导致界面设计迅速退化的数据。

由于系统的组织方式,仍然有一些类似于封装的东西,因为通常只有一个系统修改特定类型的组件(在某些特殊情况下是两个)。但是他们不拥有该数据,他们不提供检索该数据的功能。系统不相互交谈。它们都通过中央 ECS 数据库进行操作(这是必须注入所有这些系统的唯一依赖项)。

灵活性和可扩展性

这已经在关于实体组件系统的外部资源中得到广泛讨论,但它们在适应全新的设计理念方面非常灵活,即使是打破概念的想法,例如对哺乳动物、昆虫和植物等生物的建议豆芽在阳光下一下子就叶子了。

原因之一是因为没有要打破的中心抽象。如果您需要更多数据,或者只是创建一个将植物、哺乳动物和昆虫所需的组件串在一起的实体,您可以引入一些新组件。设计用于处理昆虫、哺乳动物和植物组件的系统然后会自动将其拾取,您无需更改任何内容即可获得所需的行为,只需添加一行代码以使用新的组件组合实例化实体。当您需要全新的功能时,您只需添加新系统或修改现有系统。

我在其他地方没有发现这么多讨论的是,即使在没有我们未能预料到的破坏概念的设计更改的情况下,这在多大程度上简化了维护。即使忽略 ECS 的灵活性,当您的代码库达到一定规模时,它确实可以简化事情。

将对象转化为数据

在之前的 OOP 繁重的代码库中,我看到维护代码库更接近上面的第一张图的难度,所需的代码量激增,因为Car该图中的类比:

在此处输入图像描述

...必须构建为实现多个接口的完全独立的子类型(类)。因此,我们在系统中拥有爆炸性数量的对象:点光源与定向光源的单独对象,鱼眼相机与另一个对象的单独对象等等。我们有数千个对象以无穷无尽的组合实现了几十个抽象接口。

当我将它与 ECS 进行比较时,它只需要数百个,并且我们能够在使用一小部分代码之前做完全相同的事情,因为这将类比Car实体变成了不再需要其类的东西。它变成了一个简单的组件数据集合,作为一种Entity类型的通用实例。

OOP 替代方案

因此,在某些情况下,在设计的最广泛级别上过度应用 OOP 会开始真正降低可维护性。在您的系统最广泛的鸟瞰图上,它可以帮助将其展平,而不是尝试将其建模得如此“深入”,使对象与对象交互的对象与对象交互,无论多么抽象。

比较我过去和现在使用的两个系统,新的系统具有更多功能,但需要数十万 LOC。前者需要超过 2000 万个 LOC。当然,这不是最公平的比较,因为前者有巨大的遗产,但如果你从两个系统中分一杯羹,这两个系统在功能上完全相等,没有遗留包袱(至少与我们可能得到的差不多), ECS 只需要一小部分代码来做同样的事情,部分原因是它通过将它们转化为原始数据(组件)的集合(实体)来显着减少系统中的类数量,并使用庞大的系统来处理它们一船的小/中型物体。

是否存在真正的非 OOP 范式实际上是大规模解决方案的更好选择的场景?还是这些天闻所未闻?

这远非闻所未闻。例如,我在上面描述的系统广泛用于游戏中。这在我的领域非常罕见(我的领域中的大多数架构都是类似于 COM 的纯接口,这就是我过去从事的架构类型),但我发现凝视游戏玩家在什么时候做什么设计一个建筑让世界变得与众不同,因为它能够创造出在它不断发展的过程中仍然非常容易理解的东西。

也就是说,有些人认为 ECS 本身就是一种面向对象的编程。如果是这样,它就不像我们大多数人会想到的那种 OOP,因为数据(组成它们的组件和实体)和功能(系统)是分开的。它需要在广泛的系统级别放弃封装,这通常被认为是 OOP 最基本的方面之一。

高级编码

但在我看来,通常更高级别的解决方案几乎总是以 OOP 方式组合在一起。

如果您可以将应用程序与非常高级的代码拼凑在一起,那么就您的团队必须维护的代码而言,它的规模往往相当小或中等,并且可能可以使用 OOP 非常有效地组装。

在我的 VFX 领域中,我们经常需要做一些相对较低级别的事情,比如光线追踪、图像处理、网格处理、流体动力学等,并且不能仅仅将这些从第三方产品拼凑起来,因为我们实际上是在竞争更多的是我们可以在底层做些什么(用户对尖端的、有竞争力的生产渲染改进比更好的 GUI 更兴奋)。因此,可能会有很多代码,从非常低级的位和字节混洗到脚本编写者通过嵌入式脚本语言编写的非常高级的代码。

通信网络

但是对于任何类型的应用程序(高级或低级或组合)都有足够大的规模,它围绕着一个非常复杂的中央应用程序状态,我发现尝试封装所有内容不再有用成物体。这样做往往会增加复杂性,并且由于所有事物之间发生的交互量成倍增加,因此难以推理正在发生的事情。如果在足够大的规模上没有一个断点,我们不再将每个事物建模为必须相互通信的封装生态系统,那么推理数千个生态系统相互通信就不再那么容易了。即使每一个都是单独的简单,作为一个整体考虑的一切都可能开始超过头脑,如果您尝试仅围绕 OOP 原则来设计整个大型系统,我们通常必须投入大量的精力来进行更改、添加新功能和调试等等。它可以帮助至少在某些域中以某种规模摆脱封装。

到那时,让一个物理系统封装它自己的数据(否则很多东西可能想要与它对话并检索该数据以及使用适当的输入数据对其进行初始化),它就不再那么有用了,这就是我发现通过 ECS 进行的这种替代方案非常有用,因为它将类比物理系统和所有此类庞大的系统变成了“中央数据库转换器”或“输出新内容的中央数据库阅读器”,现在它们可以相互忽略。然后,每个系统开始更像是扁平管道中的进程,而不是在非常复杂的通信图中形成节点的对象。

于 2018-02-06T22:51:06.207 回答