面向对象编程是复杂性的解决方案吗?[关闭]


18

您是否认为面向对象编程是解决复杂性的一种方法。为什么?这个话题可能有点争议,但是我打算从这里的专家那里了解“为什么”的答案!


15
艰苦的论文作业?; p
glenatron

1
这个问题没有道理,因为无法解决复杂性问题。有一些处理复杂性(特别是固有的,不可约的复杂性)的策略。但是解决方案,没有。这就像在寻找一种将一英里物理距离设为零的解决方案。
luis.espinal,2010年

1
面向对象编程是用于管理复杂性的工具。
罗伯特·哈维

问题:您可以使用OOP编写复杂的问题吗?–回答:可以。如果使用OOP,它将很复杂。
mouviciel 2014年

“这可能是有争议的”使其成为讨论项目,而不是技术性问题……
jwenting 2014年

Answers:


24

无法解决复杂性。

在《神话中的月人》中,弗雷德·布鲁克斯(Fred Brooks)讨论了编程偶然性和本质复杂性之间的区别。偶然的复杂性是由我们的工具和方法引起的,例如由于无法直接表达我们的想法而不得不用一种语言编写和测试其他代码,以及诸如此类的事情。新的方法和技术可以减少意外的复杂性。我可以比25年前更快,更好地编写程序,因为我拥有更好的语言和工具。

本质上的复杂性来自于这样一个事实,即我们试图进行编程的工作本质上是复杂的,并且存在着不可减少的复杂性。在本文中,“基本”是指“与事物的本质有关”,而不是“非常必要”。

因此,他声称没有灵丹妙药,编写软件将继续困难。

我强烈建议您阅读他的书:特别是,我建议纪念Silver周年纪念版,并附上一篇文章“ No Silver Bullet”。在那篇文章中,他回顾了所提出的复杂性解决方案并考虑了其影响。(他发现最有效的方法是收缩包装软件-一次编写复杂的东西,然后销售成千上万本。)

现在,如果正确完成,面向对象的编程将通过创建抽象并隐藏复杂性来提供帮助。类的对象具有一定的定义行为,我们可以从中推断出这些行为,而不必关心实现的复杂性。正确编写的类之间的耦合度很低,并且分而治之是解决复杂性的极好方法。它们还具有很高的内聚性,因为它们是彼此非常紧密相关的一组功能和数据。


3
20周年纪念版还包含“'No Silver Bullet'Refired”,他将OOP称为“黄铜子弹”,并说:“ ...一个非常有希望的概念。”
杰里·科芬

18

我希望您很快就会得到一些更好的答案,但这是一个简单的答案:

OOP通过更接近我们对世界上所有其他事物建模的方式对软件进行建模,从而有助于*增强复杂性。通常,比起想象一系列例程和数据结构来完成相同的事情,对球形对象与墙对象的交互进行成像要容易得多,因为它更接近我们与现实世界的交互方式。

*因为没有什么可以解决复杂性


4
这就是OOP在理论上的工作方式。实际上,在编写实际软件时,OOP示例(例如吠叫的狗,飞翔的鸭子和作为卡车的卡车)开始崩溃,因为实际的程序对象总是比这更抽象。
罗伯特·哈维

8
@Robert:我并不是说您经常在OOP中对真实世界的对象进行建模-只是更容易从对象的角度来考虑大多数编程(即使是套接字代理和模型外观对象),因为这就是我们在现实生活中观察了狗和鸭的世界。
Fishtoaster

2
不,这是对OOP的普遍误解。您不应该基于现实对象对程序建模。在现实生活中,没有像BufferReader这样的东西。
哈森

1
@Hasen j:是的,这就是我在评论中澄清的内容。
Fishtoaster


15

我认为OOP的当前主流定义不是管理复杂性的好方法。

如果您回到其根源,我相信艾伦·凯(Alan Kay)受“ lisp”的影响很大。

由于Lisp并未因主流采用而受到破坏,因此它可能设法保留了其核心价值。因此,我认为研究一下Lisp如何解决这个复杂性问题可能会给我们一些启示,我们可以以此为基础来判断OOP在处理复杂性方面有多有用。

如果您看一下SICP的“第3a讲:Henderson Escher示例”的结尾,Hal Abelson提出,不是通过将任务分解为较小的子任务,而是通过创建抽象层来管理复杂性。在最高级别上,您可以通过对较低抽象级别的解决方案来表达对复杂问题的解决方案。

我认为OOP最初旨在作为创建这些抽象层的机制。

不幸的是,如今,OOP被用来编写意大利面条代码/结构。

我将举一个例子:FPS多人游戏。

在最高级别,该游戏的工作原理是让一群玩家在地图上奔跑,并使用武器互相射击。

在下一个较低的层次上,我们必须谈论地图,武器和玩家。也许我们可以将它们视为在游戏世界中互动的物理对象。

在下一个较低的层次上,我们可以讨论对象如何进行物理交互(运动,碰撞等)。

等等等等。

这意味着(并且我引用SICP ..)的意思是,在每一层,我们不仅解决了特定的特定问题,而且还解决了一类属于我们所关注的问题的问题。重新尝试解决。因此,如果问题的描述有很小的变化,则可能只需要解决方案中的很小的变化即可。

因此,使用OOP的明智方法是在每个抽象级别创建抽象层,您可以使用直接在下面的级别中的“对象”来解决当前的问题。

这是我在演讲中引用的内容:http : //www.youtube.com/watch?v=CYtrfncMqHQ


5
像许多工具一样,OOP在明智而周到地使用时非常有效,而在滥用时却没有那么有效。
罗伯特·哈维

1
+1代表“抽象层”-很好的解释。OOP还允许您一次将看到的对象的范围限制为一个对象,这使人们更容易可视化设计和交互。
Michael K

1
我发现这样一个概念,即一个理论概念不会“被主流采用所破坏”,它将使其更好地管理现实世界项目中的复杂性……这很奇怪。
Michael Borgwardt

@MichaelBorgwardt,主流采用会破坏一个有用的想法,因为主流中的许多人都不了解这个想法的全部内容,因此,当您尝试搜索OOP是什么时,会看到各种人的各种误解。LISP仍然使用,但只有少数人使用,因此原来的想法不太可能因尖头的头发的老板不了解其全部内容而遭受腐败。
哈森

@hasen j:OTOH相应,从未见过主流采用的“原始思想” 更有可能是在现实世界中行不通的象牙色的东西。缺乏普及肯定不是支持任何事物的明确点。
Michael Borgwardt

10

和往常一样,我不同意所有人。OOP并没有提供用于管理复杂性的工具,而是创建了大量的复杂性,因为它是一个不充分的数学上虚假的范例。它使程序员无休止地尝试用OOP建模无法用OOP建模的事物。

在我看来,这里的开创性工作是迈耶的面向对象软件构造。它详细介绍了一组要求,包括我认为至关重要的要求:开放式封闭原则。这表示事物必须同时可以扩展但必须关闭才能使用。

Meyer着手从这些要求中得出对象定向,如Eiffel所体现。封装提供了封闭,继承的开放性,而提到的“事物”就是类。

我认为这项工作是一门很好的科学,因为梅耶证明是错误的,并且由于他的工作质量,有可能查明错误并加以纠正。

错误是使类或类型成为模块化的单位。这是错误的,事实证明是这样。甚至Meyer也意识到了这个问题(称为协方差问题),即OOP无法处理高于1的Arity关系(即,OOP在属性上工作正常,但在二元关系上失败)。在埃菲尔铁塔中,此问题导致类型系统不正确!

解决方案很明确。模块化单位必须大于单个类型。它必须包含几种类型,以及与它们相关的方法。

毫不奇怪,这种抽象模型得到抽象的数学理论(即类别理论)的支持:类型是类别的对象,方法(函数)是箭头。

有了这个模型,几个一组函数可以知道类型。该表示形式是对公众隐藏的,因此这是强制性的,但是我们使用模块而不是类。

标准元语言(SML)和Ocaml直接基于此模型。Ocaml还具有类和OOP:它不是没有用的,因为OOP可以让您分派属性,也就是动态绑定。但是,大多数现实世界中的问题都涉及到关系,因此在Ocaml中很少使用类并不奇怪。

毫不奇怪,在C ++ Standard模板库中几乎没有使用继承。

一个简单的事实是,OOP没有为您提供处理复杂性的正确工具,甚至没有为您提供处理真正简单问题的工具,相反,它使两代程序员产生了误解和困惑。自从C,Fortran和Cobol开始厌倦以来,OOP远没有帮助,它是编程中最糟糕的事情。


试图创建另一个帐户来支持您更多;)
Marco Mariani

您提出了许多断言。也许您可以提供用于支持断言的参数,也可以引用参数。像这样,并不能完全支持您的各种观点(从某种意义上说,“ OO有问题;这是它需要做的”):lucacardelli.name/Papers/BadPropertiesOfOO.pdf
Frank Shearar

3
顺便说一句,说“ foo是最邪恶和最糟糕的事情……”积极地腐蚀了您可能要提出的任何论点。
2011年

6

面向对象编程的起源可以追溯到1960年代。随着硬件和软件变得越来越复杂,可管理性常常成为人们关注的问题。研究人员研究了保持软件质量的方法,并开发了面向对象的编程,部分是通过高度强调来解决常见问题离散的,可重复使用的编程逻辑单元。

因此,与传统模型相反,面向对象的程序可以看作是交互对象的集合,在常规模型中,程序被视为要执行的任务(子例程)的列表。在OOP中,每个对象都能够接收消息,处理数据并将消息发送到其他对象。每个对象都可以看作是具有不同角色或职责的独立“机器”。这些对象上的动作(或“方法”)与对象本身紧密相关。

http://zh.wikipedia.org/wiki/面向对象的编程

关注点的这种分离以及对象定向的其他功能(例如多态性,继承,消息传递,解耦和封装)提供了一个逻辑和概念上的框架,通过该框架可以高效地管理大型程序的复杂性。


2

软件开发有多种复杂性。在编程级别,OOP试图通过使用对象和类对问题域进行建模来解决复杂性。一位著名的大师说,解决问题只是表示问题,因此解决方案就是表示本身。因此,通过使用类进行抽象,使用访问修饰符和方法进行封装,用于指定关系和重用的继承,在类之间建立关系和协作时的组合,将多态性作为简化确定相似对象中不同行为的手段,可以管理复杂性。

还有其他管理软件复杂性的方法,例如逻辑(Prolog)和功能(Haskell)编程。

在比编程更高的水平上,我们需要设计模式和原理来指导OOP。因此,OOP在较低的(编码)级别上管理复杂性,而这些设计方法和原理等方法则在较高的(系统和应用程序)级别上指导解决方案的设计,并使软件开发和处理复杂性更易于管理。

要回答您的问题,是的,OOP只是解决许多其他解决方案中的复杂性的解决方案。这是一个较低级别的解决方案。我们需要设计模式和原则来指导更高级别的OOP。


3
在说我们“需要”设计模式时,我会非常小心。它们是良好的OO设计的自然产物-不应该用来驱动它。“哦,我们没有这种模式,所以我们不能这样做。” 它们是传达设计的一种方式。
Michael K

2

面向对象的编程可以管理基本和可选的复杂性,但不会降低两者。

我更喜欢Eric Steven Raymond在Unix编程艺术中提供的定义,因为它在基本,可选和偶然的复杂性之间进行了描述。 http://www.faqs.org/docs/artu/ch13s01.html#id2964646

OOP对本质或可选的复杂性没有任何作用,它们是程序要求的函数。它可能会影响意外的复杂性,因为有时可以使用OOP创建更优雅的设计。但是,有时使用OOP时设计会更糟。


2

复杂的问题无法通过技术简化,只能通过技术进行管理

OOP是解决问题的技术,概念和方法。

OOP为您提供了用于执行设计的工具,可以使管理复杂性变得更加容易,但是您也可以很容易地通过糟糕的设计来增加复杂性。换句话说,如果使用不当,可能会导致技术上的复杂性。

请记住,还有许多其他方面将决定项目的成功程度(即项目管理风格,问题定义,变更管理等)。您使用的技术仅在帮助您解决问题方面有多大关系。

最后,面向对象的编程不能成为解决复杂性; 它只是管理它的工具。(如果使用正确)


+1最后,面向对象的编程不能解决复杂性问题。它只是管理它的工具。(如果使用正确)
Karthik Sreenivasan'2

2

在许多情况下,面向对象(按常规使用)是一个有用的工具,但它不足以解决复杂性。

特别是,它通常会增加很多“ 附带的复杂性 ”。例子包括实现继承的复杂性,需要提供大量的“标准功能”信息(如equals()和hashCode()等)。Stuart Halloway对该主题进行了很好的介绍:“ 简单性并非易事”

大多数语言中的对象还倾向于封装很多可变状态 -在并发世界中,这种状态越来越开始看起来像是一个糟糕的设计决策。Rich Hickey的一段有趣的视频再次探讨了对象身份和状态之间的区别,以及将二者融合在一起可能是一个错误。


1

面向对象编程是表示问题的一种方式,仅此而已。就其本身而言,它并不比任何其他编程范例都复杂。一个设计良好的OOP系统可以管理并降低复杂性,但是设计一个比所需复杂得多的系统并阻碍所有工作也很容易。

正如C ++经常提到的那样,OOP为您提供了足够的绳索来吊死自己。


但是,任何强大的语言或范例都是如此。您是否想要一根太短而无法吊起的绳索?您将永远不会上山!
Michael K

@Michael,比其他人更多。但从本质上讲,是的。没有万能的灵丹妙药,总有退缩到您使用的任何语言或范例。
Dominique McDonnell

1

我认为是的,只是因为它允许您将复杂性分割成小的自包含的“构建块”,以隐藏细节,然后使用它们逐步逐步创建所需的功能。

分而治之。


1

OOP是一种解决方案。

管理复杂性的最佳方法是创建抽象。如果我可以将数据转换为有用的集合,并且具有可操作的可识别功能,则可以开始将这些集合视为离散的“事物”。这是类和方法的基础。在这方面,正确设计的OOP可以帮助管理复杂性。

一路走来,有人决定我们可以使用OOP来解决代码重用问题。我的意思是,为什么要重新发明轮子?如果其他人为解决这个问题做了很多工作,请利用他们所做的工作,添加您特定项目所需的调整,瞧!您已经创建了一个功能强大,复杂的应用程序,而您只需要很少的工作。OO程序员可以是非常有生产力的程序员。

最终结果是,现代的OO程序员最终成为了“巫师的徒弟”,他们将一堆大而笨拙的库与几行“胶水”绑在一起,并得到了有用的东西。排序 金田 大多数时候。将该库与该库一起使用是否有潜在的副作用?也许。但是谁有时间真正去研究那些库中包含的代码呢?尤其是在图书馆不断发展的时候。结果是,我们最终得到了膨胀的应用程序,程序员需要从该库中获取一些类和方法,但是该应用程序必须承担所有其他不需要的东西。

最终结果是,最终您将获得比所需复杂得多的复杂性。

您想要分离功能的另一种处理复杂性的机制。您希望所有数据访问功能都集中在一个地方。您希望所有用户界面功能都集中在一个地方。您希望将所有控制器都放在一个地方。因此,您可以创建不同的类来管理功能的不同部分。到目前为止,一切都很好。并在一定程度上扩展;您的数据访问技能开发人员可以编写这些类,您的用户界面人员可以编写用户界面类,等等。一切都很好。

直到您必须维护别人写的东西。

是的,很高兴知道所有数据访问功能都在这里。但是什么叫他们呢?

该方法正在该类上调用该方法。但是,当我查看类定义时,没有使用该名称的方法。哦,那是继承自继承链中一层或两层的东西。等一下; 该类实现了接口?有多少不同的类实现该接口?而且我们正在使用一些复杂的运行时系统(Spring,我正在看你)在运行时将类的实例“连接”在一起?在哪里可以使用实现该接口的任何类?

您最终会得到许多小的,离散的方法,这些方法可以完成精确的工作。但这在另一个类中称为那个。在另一个类中,哪个调用了那个。在另一个类中,哪个调用了那个。在另一个类中调用哪个。它返回特定类型的结果。您必须在上面调用方法来执行特定操作。它返回另一种类型的结果。等等。

有一个术语:意大利面代码。

最后,您得到了一个非常复杂的系统,仅需编写代码即可。因此,像Visual Studio,Eclipse和NetBeans这样的IDE。所有这些都有明显的学习曲线。实际上,它们中的许多人都可以封装/聚集由不同小组开发的多个工具,每个小组都有其自己的OWN学习曲线。

这是在管理复杂性吗?

调试代码的难度是编写代码的两倍。祝您调试这些东西好运。特别是如果它使用多个库,则在运行时使用某种依赖项注入系统将它们“连接”在一起。

总结:OOP提供了看似有希望的工具来帮助管理复杂性。现实情况是,生成的代码往往令人肿(因为您不能仅从所有这些链接的库中提取所需的代码),并且您需要复杂的工具来浏览代码。迅速成为维护的噩梦。

恕我直言,这是净亏损;它增加了比消除的复杂性。如果没有它,它确实可以使您完成极其困难的工作,甚至可能做不到。但是任何大型项目都会迅速演变为难以维护的混乱局面。

如果您已经知道它是如何工作的,并且可以记住,您可能有机会对其进行维护。

请记住要应用伊格森定律:您自己的任何代码(六个月内都没有看过)也可能由其他人编写。


0

在某种程度上...

为什么?因为它促进了非常逻辑的模块化。至少与程序编程相比,程序编程很容易编写大量的意大利面条代码。


0

面向对象程序设计似乎可以帮助我们处理复杂性的原因是,它迫使我们以特定的方式而不是多种方式来编写代码。面向任务的编程更加直观,这就是为什么编程以这种方式开始的原因。面向对象需要进行培训和实践才能有效地理解和使用,但是通过将编程约束到某个路径中,可以使受过培训的人员有效地维护所编写的代码。

它比任何其他方法都没有逻辑或现实世界,它只是通过相似的视角集中解决问题的一种方式。许多技术专业都使用非直觉的刚性方法范式来处理其任务的复杂性。

处理复杂性的第三种方法是函数式编程,将来可能还会有其他新方法。


1
评论已删除;尽量让他们保持文明。
Fishtoaster

0

我认为这更像是一种可维护性的解决方案,因为作为程序员,您应该将方法放在有数据的位置,从而创建应用程序的对象模型。

是的,它还为复杂性提供了解决方案,它提供了一个模型,使您可以自然地“查看”代码,因为这些对象具有属性和可能的​​操作

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.