跳转到内容

面向对象编程/对象

来自维基教科书,开放的书籍,开放的世界

什么是对象

[编辑 | 编辑源代码]

简单来说,术语对象用于与现实世界中的对象,例如椅子或吉他,产生联系。只是对于软件来说,只使用了一些简化的抽象,这些抽象是专门为手头的任务而设计的。虽然真正的椅子是由原子和分子组成的,并且根据物理定律及其原子组成对周围环境做出反应,但“椅子对象”会根据您是编写游戏还是为家具店编写销售点系统而有很大差异。从椅子的角度来解决问题不如从问题的角度来解决问题有效率。

在本手册中使用的多数语言中,您会发现从技术角度来说(有点像吹口哨 9600 波特,我明白),对象是“类的实例”。太好了,那是什么意思呢?好吧,我们可以追溯到柏拉图及其柏拉图理念。如果您在观看《比尔和泰德的精彩冒险》上花费的时间比阅读柏拉图的时间更多,那么这个想法是椅子的概念是独立于任何特定椅子的一个实体。换句话说,您可以抽象地想到椅子,而不必想到任何特定的椅子。

在大多数 OOP 语言中,这种抽象的椅子概念被称为(来自分类),它是实际制造椅子的原型或蓝图。从蓝图中制作某物的行为通常称为实例化,而制作的东西既是对象,也是用作蓝图的实例。作为人类,我们通常倾向于反过来做——我们对遇到的对象进行分类。我们很容易识别我们遇到的像椅子的东西是椅子;这种分类是我们最初得到这个词的地方。

很容易陷入关于对象性的深奥哲学争论;在某些领域,例如知识表示计算本体论,这些争论非常重要。但是,对于计算机程序员来说,只需要弄清楚您的应用程序需要知道哪些关于椅子的信息以及需要用椅子做什么。这可能是一个足够困难的问题,通常没有必要使其变得更难!

如果您仍然对整个想法感到模糊,请考虑更技术的解释。结构体(结构)、记录、表格以及组织相关信息的其他方式早于面向对象编程。您可能熟悉以下Pascal 代码

 TYPE chair = RECORD
                model  : integer;
                weight : integer;
                height : integer;
                color  : COLOR;
              END;

这实际上并没有创建一个椅子变量,而是定义了在您创建椅子变量时它的外观。您可以继续创建椅子数组等等,正如我们希望您自己发现的那样,这种事情对于使程序易于理解是不可或缺的。面向对象编程想要推动这种优势,并尽可能地利用它来获得可理解性、正确性和简单性。

一项基本的问题解决技术是分而治之——您只需要弄清楚如何将问题划分为子问题。OOP 创新者意识到,我们已经找到了将问题划分的方法,并且它反映在我们组织数据的方式中,就像上面一样。如果您查看包含椅子 RECORD 的应用程序,您一定会发现很多用于处理椅子的代码。为什么还要费心去定义它呢?因此,如果您要从整个应用程序中提取所有这些代码并将其与椅子定义放在一起,那么推理就认为,您应该更容易确保

  • 所有椅子代码都是正确的
  • 所有椅子代码彼此一致
  • 没有重复的椅子代码
  • 总的来说,您的意大利面条代码更少,因为椅子代码不再与沙发代码等纠缠在一起

因此,您将椅子定义和从整个应用程序中提取的代码放在一起,并将其称为类。获取一个椅子变量并将其称为对象,然后您就可以开始执行了。

由于这应该是一本实用的书籍,让我们看一下我们的第一个代码示例,这次是 Python 代码

 class Chair:
    model = None
    height = None
    weight = None
    color = None
    def has_arms(self):
        return self.model % 2 # odd-numbered models have armrests

这看起来与 Pascal 示例并没有太大区别。唯一的区别是类 Chair 现在包含一个has_arms 方法。方法是在类中存在的函数,通常用于处理某些特定于类的的数据。希望其目的相当清楚,所以让我指出重要的部分:has_arms 是一个计算推断属性——此信息不是直接存储的。

在 Python 中,您可以这样使用此类

 c = Chair()
 c.model = 15
 c.height = 40
 c.weight = 10
 c.color  = 7

 if c.has_arms():
      do_something
 else:
      do_other_thing

在这里,“c”变量是“chair”类的实例,也称为椅子对象。我们像在 Pascal 中一样初始化属性(这将在稍后更改!),如果椅子有扶手,我们就执行一件事,如果没有扶手,我们就执行另一件事。但我们从未初始化“has_arms”属性或类似的东西。

由于目标之一是让您成为与语法无关的程序员,因此我们再次在 C++ 中展示了相同的示例

 class Chair
 {
 public:
     int model;
     int weight;
     int height;
     int color;
     bool has_arms()
     {
         return model % 2 ? true : false;
     }
 };
 Chair c;
 c.model = 15;
 c.height = 40;
 c.weight = 10;
 c.color  = 7;

 if (c.has_arms())
      do_something();
 else
      do_other_thing();

现在,我们只是想提一下,这仅仅是冰山一角,这个例子并不代表好的风格(在任何一种语言中)。创建功能如此少的对象似乎没有多大意义,您可以轻松地生成不需要新类的等效代码

struct Chair
{
    int model;
    int weight;
    int height;
    int color;
} c;

Chair c = {15, 40, 10, 7 };

if (c.model % 2)
     do_something();
else
     do_other_thing();

本节的目的是帮助您理解这些术语;我们将在封装、多态性、继承等部分深入探讨其优势。但是,让我们说,虽然“低级”方式看起来可能更短更简单,但面向对象的优势随着程序大小和复杂性的增加而增加。这并不奇怪,因为 OOP 就是为此而设计的,但这确实使简单的示例难以找到。所以请耐心等待。

状态机

[编辑 | 编辑源代码]

如果您不熟悉状态机,我们不会深入探讨它们,但我们强烈建议您进一步研究它们。这有利于您的思维过程的清晰度。严格地说,状态机是相当复杂的数学结构,使用数学符号可以推导出许多有趣的结论。但这不是我们的兴趣所在。我们的兴趣是双重的

  • 一方面,状态机提供了一种清晰的图形化方式来说明可能复杂的应用程序逻辑。首先,确定您的可能状态是什么。例如,服务器可以是 IDLE、CONNECTED、BUSY、ERROR 等。然后,定义所有可能的输入,即客户端可能发送的消息,并为每个状态图绘制服务器对每个输入的响应。当您考虑在各种状态下对意外消息的反应时,这有助于促进彻底性。
  • 另一方面,状态机是一种标准工具,具有相对标准的符号。有一些工具和库会根据某些规范状态机格式作为输入自动生成代码。这实际上是正则表达式处理器所做的。

在面向对象编程中,“对象”有时是状态机的实现。状态机是行为实体(服务器、正则表达式处理器、图形引擎和虚拟机)的更好的概念基础。

简单对象和行为实体之间的一个基本区别是,简单对象不会根据其值改变其行为。事实上,我们倾向于将其称为而不是状态。简单对象往往保存在集合中并被搜索,传递到函数和从函数返回,并且通常像值一样起作用。字符串和矩形是经典的例子。稍后,我们将讨论消息传递作为方法调用的抽象,在简单对象的上下文中,它可能看起来有点奇怪。

如果您仍然难以理解对象的 concept,请下载并使用BlueJ,大多数人在使用该 IDE 几小时内就会“明白”。

华夏公益教科书