it-swarm.cn

面向对象编程的优点

注意:这个问题是我几个月前写的 博客文章 的摘录。在Programmers.SE上的 comment 中放置指向博客的链接后,有人要求我在此处发布问题,以便他们回答。该帖子是我最受欢迎的帖子,因为人们似乎在Google中输入“我没有面向对象的编程” 很多。请随时在此处回答,或在Wordpress上发表评论。

什么是面向对象的编程?没有人给我满意的答案。我觉得您不会从一个鼻子朝天说“对象”和“面向对象”的人那里得到一个很好的定义。从一个只做面向对象编程的人那里也不会得到一个好的定义。没有人同时了解过程式程序设计和面向对象程序设计,我从未对一种面向对象程序的实际作用有一致的认识。

有人可以给我他们关于面向对象编程优势的想法吗?

35
Joel J. Adamson

将软件视为计算机内部存在的机器或装配线。一些原材料和组件被送入机器,然后按照一系列程序将它们加工成某种最终产品。设置程序以特定顺序对某些原材料或组件执行特定操作,以达到一组特定的参数(例如,时间,温度,距离等)。如果要执行的操作细节不正确,或者机器的传感器未正确校准,或者某些原材料或组件不在预期的质量标准之内,则可能会改变操作的结果,并且产品不会合格如预期的那样。

这样的机器在操作和输入可接受方面非常严格。机器既不会质疑设计人员的智能,也不会质疑其当前的操作环境。只要有针对性,它将继续遵循程序。即使原材料或组件的变化可能对以后的操作产生重大影响,机器仍会执行其程序。需要对过程进行审查,以查看对程序进行哪些更改才能补偿并产生所需的结果。更改产品的设计或配置可能还需要对执行的操作或其顺序进行重大更改。尽管负责生产的人员很快就学会了尽可能多地隔离操作以减少操作之间的不良影响的重要性,但人们还是对处理过程中所处的条件成分做出了很多假设。直到最终产品在某些不同的操作环境中由用户掌握之前,可能无法检测到的假设。

这就是过程编程的样子。

面向对象提供的是一种消除组件条件假设的方法。因此,要在该组件上执行的操作以及如何将其集成到最终产品中。换句话说,OOP就像处理一些特定组件的过程详细信息,然后将其交给较小的机器来做。较大的机器负责该过程,告诉特定于组件的机器可以完成预期的操作,但是将步骤的详细信息留给特定于组件的机器来处理。

关于面向对象相对于非面向对象软件的优势:

  • 特定于组件的行为-详细介绍如何处理特定组件,由较小的特定于组件的机器负责确保在处理组件的任何时间,其机器都将适当地处理;
  • 多态表达式-因为特定于组件的机器执行针对其特定组件的量身定制的操作,所以发送到不同机器的同一消息的行为可能有所不同;
  • 类型抽象-通常,对于几种不同类型的组件,将相同的词汇表用于其计算机执行的操作通常是有意义的;
  • 关注点分离-将特定于组件的详细信息留给他们的机器意味着过程机器仅需要处理对其过程和管理过程所需的数据的更一般,更大的关注;另外,它不太可能受到其他组件更改的影响;
  • 适应性-只需更改所使用的组件或将其提供给另一台加工机器,即可将专注于其专业领域的组件调整为不可预见的使用;
  • 代码重用-狭窄的焦点和更大的适应性的组件可以通过频繁使用来利用其开发成本。
7
Huperniketes

在您的博客中,您似乎既熟悉命令式编程也喜欢函数式编程,并且熟悉面向对象编程所涉及的基本概念,但是您从未真正对它进行“点击”使它有用。我将尝试根据这些知识进行解释,并希望它对您有所帮助。

在其核心处,OOP是一种使用命令式范例的方法,它可以通过创建对问题域建模的“智能”数据结构来更好地管理高度复杂性。在(标准过程非对象中面向程序),您有两件事:变量和知道如何处理它们的代码,该代码从用户和其他各种来源获取输入,将其存储在变量中,对其进行操作并产生输出数据转到用户或其他各种位置。

面向对象编程是一种采用基本模式并以较小的比例重复该程序来简化程序的方法。就像程序是一个包含大量数据的代码,这些代码知道如何处理程序一样,每个对象都是与代码绑定的一小块数据,这些代码知道该如何处理程序。

通过将问题域分解为较小的部分,并确保将尽可能多的数据直接绑定到知道该如何处理的代码,您可以更轻松地对整个过程以及子过程进行推理。构成过程的问题。

通过将数据分组为对象类,您可以集中处理与该数据相关的代码,从而使相关代码更易于查找和调试。并且通过将数据封装在访问说明符后面,并仅通过方法(或属性,如果您的语言支持它们,则通过属性)对其进行访问,则可以大大降低数据损坏或违反不变性的可能性。

并且通过使用继承和多态性,您可以重用先前存在的类,对其进行自定义以满足您的特定需求,而无需修改原始类或从头开始重写所有内容。 (如果可以避免的话,这是 您永远不应该做的事情 。)请小心了解您的基础对象,否则可能会得到 (袋鼠杀手 )。

对我而言,这些是面向对象编程的基本原理:通过创建对象类,继承和多态性来实现复杂性管理,代码集中化和改进的问题域建模,并通过使用封装和增强功能来提高安全性而不会牺牲功能或控制力属性。我希望这可以帮助您理解为什么许多程序员认为它有用。

编辑:在评论中回应乔尔的问题,

您能否解释一下“面向对象程序”包含的内容(除了您所概述的这些奇特的定义之外),它与命令式程序根本不同?您如何“使球滚动”?

这里有一个免责声明。我的“面向对象程序”模型基本上是Delphi模型,它与C#/。NET模型非常相似,因为它们是由前Delphi团队成员创建的。我在这里所说的可能不适用于其他OO)语言。

面向对象程序是其中所有逻辑都围绕对象构造的程序。当然,这必须在某个地方引导。典型的Delphi程序包含初始化代码,该代码创建一个名为Application的单例对象。在程序开始时,它调用Application.Initialize,然后调用Application.CreateForm表示要从头开始加载到内存中的每种表格,然后Application.Run,,它将在屏幕上显示主要表单,并启动输入/事件循环,该循环构成了任何交互式计算机程序的核心。

应用程序和表单将轮询来自操作系统的传入事件,并将其转换为对对象的方法调用。很常见的一件事是使用事件处理程序,即.NET中的“代理”。一个对象的方法说:“执行X和Y,但还要检查是否已分配了此特定事件处理程序,如果已分配则调用它。”事件处理程序是方法指针(一种非常简单的闭包,其中包含对方法的引用和对对象实例的引用),用于扩展对象的行为。例如,如果我的表单上有一个按钮对象,我将通过附加一个OnClick事件处理程序来自定义其行为,该事件处理程序会导致其他对象在单击按钮时执行一个方法。

因此,在面向对象的程序中,大多数工作是通过定义具有一定职责的对象并将它们链接在一起来完成的,无论是通过方法指针还是通过一个对象直接调用在另一个对象的公共接口中定义的方法。 (现在,我们回到封装了。)这是一个想法,在我上大学OOP)之前,我没有返回的概念。

46
Mason Wheeler

我认为OOP基本上只是一个名称,就像我以前一样,这是您可能会一直想做的事情。

早在我还是婴儿程序员的时候,即使在Fortran中,也有一个指向子例程的指针。能够将指向子例程的指针作为另一个子例程的参数传递是非常有用的。

然后,接下来真正有用的是将指向子例程的指针存储在数据结构的记录内。这样,您可能会说记录“知道”如何对其自身进行操作。

我不确定他们是否曾经将其内置到Fortran中,但是在C及其后代中很容易做到。

因此,在下面,这是一个简单而有用的想法,您可能会被诱惑去做自己的事,并且即使有人将它变成了一个充满令人恐惧的流行词的庞大潮流,但使用更新的语言更容易做到。

6
Mike Dunlavey

有各种各样的OO系统,很难获得每个人都同意的定义。而不是试图展示Java的OO与Common相似)。在LISP对象系统中,我将逐步介绍一些更常规的内容。

假设您有许多作为分散数据存在的对象。例如,点可能是X,Y和Z数组中的元素。为了考虑一个点本身,将所有数据汇总到一个C struct中是很有意义的。

现在,对于任何数据对象,我们都将数据汇总在一起。但是,在程序程序中,代码是分散的。假设我们正在处理几何形状。有一个绘制形状的大功能,它需要了解所有形状。有一个很大的功能可以查找区域,另一个功能可以查找周边。圆的代码分散在多个函数中,为了添加另一种形状,我们需要知道要更改的函数。在面向对象的系统中,我们将函数收集到与数据相同的事物中(class)。因此,如果我们要查看所有的圆圈代码,它就在Circle定义中,并且如果我们想添加一个Quartercircle,我们只需编写其类,就可以得到代码。

这样做的一个好处是我们可以维护类不变式,这对于类的每个成员都是正确的。通过限制类外的代码不直接与类数据成员混淆,我们获得了可以在一个地方更改类数据的所有代码,并且可以确认它不会做任何棘手的事情(例如,一个三角形带有一条腿)超过其他两个的总和)。这意味着我们可以依靠该类每个成员的某些属性,而不必每次使用时都检查对象是否健全。

主要好处来自继承和多态性。通过将所有这些不同形状定义为称为Shape的类的子类,我们可以使代码操纵Shapes,并且形状子对象的工作是执行操纵所要求的操作。这意味着当我们添加新形状或改进旧形状的行为时,我们无需触摸经过测试的旧代码。我们自动拥有可以直接利用新代码的旧代码。不必使控制代码知道所有可能的形状,而不必维护知道所有可能的形状的函数,我们只需要处理形状及其属性,同时保持Shape子类即可。这简化了控制代码。

我们在这里有几个优势。由于我们具有类不变式,因此我们可以像处理内置类型一样来推理较大的数据对象,这意味着我们通常可以将复杂的概念分解为更简单的概念。由于圆形代码主要包含在Circle中,因此我们增加了位置。由于没有在不同位置的几个不同函数中分散的圆的概念,因此例程之间的耦合较少,也不必担心保持它们同步。由于类实际上是类型,因此我们可以利用现有的类型系统来捕获对类的不兼容使用。

5
David Thornley

OO有许多不同的定义,是的。我相信您可以自己找到很多。我个人喜欢 Rees Re:OO 作为理解它们的一种方式。我想自从您引用Paul Graham以来,您已经读过了。 (我向有兴趣的人推荐它。)我将或多或少地采用Java这里的定义{1,2,3,7,8,9}。

OO的实用性问题,特别是我的实现方式,应该用几千行代码来提供更大的答案(部分原因是不只是一堆断言)。但是,这是该假设文档的摘要。

我不认为OO)在小规模上非常有用,例如大约几百行。特别是OO不受良好功能影响的语言倾向于这使得使用任何类型的集合或需要许多数据类型的任何事物来完成简单的事情都非常痛苦。这就是大多数设计模式都在起作用的地方; 它们是基于底层功能的创可贴)语言

在大约一千行处,要跟踪所有操作和数据结构以及它们之间的关系变得越来越困难。在这一点上,它有助于找到一种显式组织数据结构和操作,绘制模块边界和定义职责的方法,并在尝试对它们进行编程时提供一种方便的方式来理解这些定义。

Java-ish OO是碰巧赢得了流行竞赛的这些问题的中途解决方案。因为这是Java扩展功能不足的语言所产生的问题后,它似乎开始看起来像是一种万物的魔术解决方案,而不仅仅是保持井井有条的方式。卡在C++中,否则(像我一样,每天都在C#中工作)使用OO,但不要为此而兴奋不已。

3
Jesse Millikan

OOP =数据结构+消息传递+继承,所有这些都是编程模型中的逻辑演变。

(程序员)可以在大约90秒内了解OOP(有关链接,请参阅我的个人资料)。概念非常简单。

如何应用是另一回事。仅仅因为您知道如何挥动锤子并不意味着您知道如何设计和建造房屋。 ;-)

1
Steven A. Lowe

OOP尝试根据对象及其之间的交互对现实世界的概念进行建模。作为人类,我们倾向于按照物体来处理世界。这个世界充满了具有某些属性的对象,并且可以做某些事情,例如与其他对象进行交互。 OOP可以用类似的术语对世界建模。例如,

  • 人是对象。一个人具有一些属性,例如年龄和性别。一个人可以做的事情:吃饭,睡觉,开车。
  • 汽车也是一个对象(尽管类型不同)。它还具有诸如品牌,型号和年份的属性。汽车可以做的事情:移动。

但是汽车不能自行行驶,它需要一个人来驱动-对象之间的交互。

1
ysolik

因为您了解结构,也了解函数指针,并且了解具有函数指针的结构,所以从您的角度来看,我将面向对象的编程定义为简单的“编程,大量使用具有函数指针的结构”。它仍然是传统意义上的编程-全部是数据,并且代码对数据起作用。区别仅在于如何定义所有这些信息以及您如何定义这些信息。

也许过于简单化是传统编程是“带有某些数据结构的代码”,而面向对象的编程是“带有某些代码的数据结构”。两者仍然具有数据结构,并且仍然具有代码。因此,面向对象的编程无非就是预先定义数据类型,并为它们如何通过功能集进行通信而实施合同。

正如您所观察到的,存在大量的应用程序类别,对于实现解决方案而言,这并不是一个很好的方法。您似乎生活在一个主要由此类应用程序组成的世界中。在您的博客文章中,您提到了“ 99瓶啤酒”问题的实现(您的“最喜欢的编程展示”)。 99瓶啤酒当然是该类别的一部分。通过查看99瓶啤酒的实现来尝试理解面向对象的编程,有点像通过查看树屋来理解高层建筑。即使是建造得很好的树屋也只能教给您很多东西。

TL; DR:OO编程类似于传统编程,只是您将更多的精力放在预先定义数据结构上,并且这些数据结构通过函数指针相互通信。

0
Bryan Oakley

我最初的理解是:

在进行面向对象的编程之前,您有 结构化编程 。一切都围绕该过程。您必须问自己的第一个问题是“我想对这些信息做什么?”。

使用面向对象的编程,它以数据为中心。您必须问自己的第一个问题是“我需要处理的女巫信息?”。这使抽象变得容易。

0
DavRob60

不久前我写了一篇博客文章,您可能会发现有帮助: 程序与OOP解释

0
VirtuosiMedia