从真实世界中理解面向对象编程(OOP)
1. 介绍
面向对象编程试图像我们的大脑一样对世界进行建模。
本文中,我们将探索此建模背后的核心概念。
2. 我们如何看待这个世界
人脑不会按原样处理世界:它简化了世界。否则,它将无法处理每秒接收到的大量信息。当然,我们如何简化世界很重要:我们考虑重要的事情而忽略其他事情。
换言之,我们在脑海中创造了一个世界模型:
3. 用类建模
这个建模过程是基于发现事物之间的相似之处。当我们的大脑发现两个项目看起来足够相似时,它会将它们归为同一类别,否则归为不同类别。
例如,狗的相似性足以将它们归入同一类别。(事实上,“狗”这个词本身就是一个类别。我们稍后会回到这个话题。)我们知道狗有重量和颜色等属性,它可以做一些事情,例如吠叫和摇尾巴:
狗、猫和桌子在术语上是相似的,通常它们都有四条腿。但我们不会将一张桌子归入同一类别,包括狗和猫。然而,狗和猫非常相似,我们可以将它们归为一类:哺乳动物或动物:
我们还有许多其他类别,这取决于我们希望类别有多广泛。我们甚至可以想象出一个类别,桌子也可以放在其中:四足物:
这样的分门别类有一个更科学的名字:分类。因此,我们更喜欢将这些类别称为类。
4. 对象:真实世界中的类
正如我们提到的,类代表了我们用类标记的事物之间的相似性。
这些类或模型在现实世界中并不存在。它们是对现实的抽象,只存在于我们的脑海中:
我们从未见过我们称之为“狗”的东西。是的,这听起来很荒谬,但我们需要理解其中的区别。当我们说“我今天看到一只狗”这句话只是“我今天看见一种生物,我把它归类为狗”的简化版本:
现实中存在的(或至少存在的)是叫 Lassie 或 Laika 的狗。它们是一团毛绒绒的球,我们可以抚摸它们。
我们看不到狗类本身,而是现实世界中狗类的实例。我们经常将这些实例称为对象。
5. 封装:数据和行为
狗可大可小。它们可能是灰色或棕色的。此外,他们知道自己的名字。这些是狗的属性——我们所知道的数据。每只狗都有这些特性;因此,我们将这些分配给狗类本身。但每只狗对这些属性都有不同的值。
我们不一定知道这些属性的值,但我们可以获得它们,因为我们知道它们的存在。例如,我们可以观察它们的颜色,或者询问它们的主人以了解它们的名字。
编程时,我们将这些数据保存到字段中:
除此之外,对象还有行为。狗可以改变自己的状态(坐着或躺着)、与其他物体互动(拿球)或与周围的环境互动(通过滑稽的行为让周围的人都微笑)。
在编程中,我们用方法表示这些行为。
我们在类中定义字段和方法。因此,所有实例都将具有这些属性和行为。更重要的是,我们将这些看似无关的概念(数据和行为)打包成一个实体。换句话说,我们封装了数据和对其进行操作的行为:
这很有道理:我们向狗发出命令让它坐下,然后它就坐下了。我们不会把狗放入一个设备里,然后让设备生成一条坐着的狗。相反,我们将行动与数据结合起来,因为这就是世界的运作方式。
6. 关联的力量
对象本身可以做很多事情。但当他们合作时,他们可以做更多的时区。这种情况称之为关联。合作对象远多于对象的总和。
例如,当主人扔球时,球会飞走。但狗会追着球跑,抓住它,然后把它带回主人身边。如果没有任何参与者,主人、狗和球之间的这种小互动是不可能的。
这些关联可以有多种形式。有关更多详细信息,请访问我们关于继承、组合、聚合、关联以及它们之间差异的文章。
一个有趣的事实:在编程中,我们将一些关系建模为数据。
7. 可见性:隐藏其他人不应该看到的东西
对象可能包含我们不想看到或不应该看到的数据和行为。这些细节是对象内部工作的一部分。在编程中,我们称之为实现细节。
例如,我们看不到狗的器官,因为狗的身体隐藏了它们。我们不知道他们的存在或他们的工作。
这是一件好事,因为在大多数情况下,这些事情太复杂了,我们无法理解。我们通常不想知道如何通过狗的静脉泵血,并通过它的神经引导电流来让它奔跑。我们想要一个简单的东西:一只会跑的狗。
狗使用了一个简单的方案:它向外界发布一组定义明确的行为,并将其他细节保密。狗不希望外界有能力干预它的内部状态。这将使我们有可能对狗有太多的控制,这是我们想要避免的。权力越大,责任越大:
在编程中,一个对象的字段和方法可以被其他对象独立访问。例如,在 Java 中,我们使用访问修饰符来控制它们。
8. 状态处理:可变或不可变
对象可以是可变的,也可以是不可变的,这取决于它们是否可以分别更改其内部状态(数据)。
例如,狗是可变的,因为它们的体重会根据吃多少而变化。相比之下,钞票是不可变的。它从打印机中出来后,其货币和值不会改变。10 美元的钞票永远不会变成 5 欧元的钞票。
在大多数编程语言中,对象默认是可变的。我们需要通过额外的工作来使它们不可变。
9. 引用事物
有时我们想指定我们正在谈论的对象。例如,我们可能有多只狗。为了避免混淆,我们为它们命名并直呼其名。此外,他们可以有很多名字,例如昵称。然而,狗会回应它们的名字。我们通过狗的名字来指代或指出一只特定的狗。
当我们谈论一只狗时,我们会在句子的中间提到它。例如,当一个家庭有一只狗,有人说,“我要遛狗”,每个人都知道她指的是哪只狗。但不幸的是,狗的寿命比人类短,这意味着狗可能会死,家人可能会有一只新狗。当这种情况发生时,同样的话“我要遛狗”,狗指向了不同的对象。
它是同一个变量,但它引用了不同的对象。变量的值可以更改:
在编程中,我们将对象存储在内存中。但我们无法直接访问它们;我们需要一种方法来引用对象所在的特定内存地址。为此,我们使用变量,这些变量是对内存部分的引用。这些变量有名称,因此我们可以了解它们所持有的对象。
10. T不变的事物
生活中有不变的东西。当我们提及它们时,时间无关紧要;它们总是意味着同样的事情。
例如,当我们说地球时,它总是指同一颗行星。当然,总有一天,太阳会膨胀、爆炸并摧毁地球,但不会有另一颗行星取代它。地球是我们赖以生存的岩石球,没有别的。
在编程中,我们不能给常数赋值。取决于编程语言的不同,常数取值的确切时间可能会发生变化。
11. 常量 vs. 不可变项
综上:变量指向对象。这里有两件事可以改变:对象的内部状态和变量指向的对象本身。
不变性指的是对象的内部状态。Final 或 nonfinal 是指我们是否可以更改我们引用的对象。
这些东西是独立的。我们可以有四种组合。
12. 小结
面向对象编程之所以如此有效,是因为它试图用与我们大脑相同的建模技术来模拟世界。
本文中,我们介绍了这些概念,并用简单的现实例子进行了演示。