日语中有一句杂俳,大意是“看不懂药品的疗效说明书,反而让人觉得药很有效”。
面向对象也有类似之处。“封装、多态和继承三种结构”“现实世界和软件是无缝的”等,当第一次听到这些时,相信不少人都会觉得“虽然不是很明白,但好像很厉害的样子”。
今天就来通俗地给大家介绍面向对象编程(Object Oriented Programming,OOP)的基本结构。
OOP 具有之前的编程语言所没有的三种优良结构,分别是类、多态和继承(一般认为OOP的三大要素为封装、多态和继承,但由于类不只具有封装的作用,所以这里改为使用“类”)。
在 OOP 刚开始普及的 20 世纪 90 年代,它们经常被称为 OOP 的三大要素。
打个比方,那些让人无从着手、难以理解的程序就像是一个乱七八糟的房间。由于无法马上在这样的房间里找到需要的东西,所以我们很有可能会再次购买,或者即使将房间里的某一处整理干净,周围也依然是乱作一团。
如果要保持房间整洁,平时就要多加注意,此外还需要使用清理不必要物品(去除冗余)的吸尘器和规整必要物品(整理)的收纳架。
为了保持房间整洁,需要吸尘器和收纳架 ▲
OOP 的三种结构为程序员提供了去除冗余逻辑、进行整理的功能结构。类结构将紧密关联的子程序(函数)和全局变量汇总在一起,创建大粒度的软件构件。通过该结构,我们能够将之前分散的子程序和变量加以整理。多态和继承能够将公用子程序无法很好地处理的重复代码进行整合,彻底消除源代码的冗余。
接下来我们将使用简单的 Java 示例代码来介绍 OOP 的基本功能。
1.1
三大要素之一:类
这里,我们将类的功能总结为汇总、隐藏和“创建很多个”。
类的功能是汇总、隐藏和“创建很多个”。
1 | “汇总”子程序和变量。 |
2 | “隐藏”只在类内部使用的变量和子程序。 |
3 | 从一个类“创建很多个”实例。 |
类的功能之一:汇总
代码清单 1.1 中定义了 openFile(打开文件)、closeFile(关闭文件)和readFile(读取一个字符)这三个子程序及一个全局变量。下面我们使用类的功能来逐步修改该程序。
代码清单1.1采用结构化编程的文件访问处理
首先来看一下汇总功能。类能汇总变量和子程序。这里所说的变量是指 C 和 COBOL 等语言中的全局变量。OOP 中将由类汇总的子程序称为方法,将全局变量称为实例变量(又称为“属性”“字段”),之后我们会根据情况使用这些术语。
下面就让我们使用类来汇总代码清单1.1。
代码清单1.2 使用类进行汇总
打个比方,在收拾乱七八糟的屋子时,与其只准备一个大箱子,不如准备多个箱子分别存放衣服、CD、杂志、文具和小物件等,这样会更方便拿取物品,汇总功能也是同样的道理。
类的功能之一:汇总 ▲
汇总的效果并不只是减少整体构件的数量,子程序的名称也改变了。代码清单1.1中子程序的名称是openFile、closeFile、readFile,而在代码清单1.2中则去掉了File,名称分别为open、close、read,显然这种命名方式更轻松。在没有类的结构化语言中,所有子程序都必须命名为不同的名称,而类中存储的元素名称只要在类中不重复就可以。
举例来说,一个家庭中所有成员的名字应该都不相同,但是和姓氏不同的邻居家的家庭成员重名则没有关系。
< 类的功能之一:汇总 >
能够将紧密联系的(多个)子程序和(多个)全局变量汇总到一个类中。优点如下。
● 构件的数量会减少
● 方法(子程序)的命名变得轻松
● 方法(子程序)变得容易查找
类的功能之二:隐藏
接下来我们看一下隐藏功能。
在代码清单1.2 中,子程序和全局变量都汇总到了类中,但是在这种状态下,从类的外部仍然可以访问 fileNO 变量。TextFileReader 类 的 open、read 和 close 方法会访问 fileNO 变量,但其他处理则无须访问,因此最好限定为只有这三个方法能访问该变量。
OOP 具有将实例变量的访问范围仅限定在类中的功能。加上该限定后的代码如代码清单1.3 所示。
代码清单1.3 隐藏实例变量
代码清单1.2 与代码清单1.3 只存在细微的差别。后者在实例变量的声明之前添加了 private,这是一种隐藏结构,表示将 fileNO 变量隐藏起来,限定为只有类内部的方法才能访问 fileNO 变量, 如此一来该变量就不再是全局变量了。
类的功能之二:隐藏 ▲
除了隐藏变量和方法之外,OOP 中还具备显式公开的功能。由于 TextFileReader 类中的三个方法是提供给程序的其他部分使用的,所以我们将其声明为显式公开的方法。修改后的代码如代码清单1.4 所示。
代码清单1.4 公开类和方法
由于在类和方法的声明部分指定了public,所以从应用程序的任何位置都可以对其进行调用。
< 类的功能之二:隐藏 >
能对其他类隐藏类中定义的变量和方法(子程序)。这样一来,我们在写程序时就可以不使用全局变量了。
类的功能之三:创建很多个
使用传统的编程语言很难实现“创建很多个”的结构,可以说这是 OOP 特有的功能。
代码清单1.4是一个打开文件、读取字符,最后关闭文件的程序。当只有一个目标文件时,这是没有什么问题的,但如果应用程序要比较两个文件并显示其区别,情况会怎样呢?也就是说,需要同时打开多个文件并分别读取内容。
解决这一问题的关键就在于实例。
实例是类定义的实例变量所持有的内存区域。定义了类就可以在运行时创建多个实例,也就是说,能够确保多个内存区域。
类的功能之三:创建很多个 ▲
我们在前面介绍过,类能汇总实例变量和方法。不过,如果同时创建多个实例,那么在调用方法时就不知道到底哪个实例变量才是处理对象了, 因此 OOP 的方法调用代码的写法稍微有点特殊。
存储实例的变量名 . 方法名 ( 参数 )
下面我们就来介绍一下代码清单1.4 的程序的调用端是什么样子的。请大家看代码清单1.5。
代码清单1.5 “创建很多个”实例
这里,首先从 TextFileReader 类创建两个实例,并存储到 reader1 和 reader2 这两个变量中。之后的打开文件、读取字符及关闭文件等处理都是通过指定变量 reader1 和 reader2 来调用方法的。
像这样,通过“指定实例,调用方法”,就可以指定哪个实例变量是处理对象。
根据“创建很多个”的结构,类中方法的逻辑就变得简单了。
< 类的功能之三:创建很多个 >
一旦定义了类,在运行时就可以由此创建很多个实例。
这样一来,即使同时处理文件、字符串和顾客信息等多个同类信 息,也可以简单地实现该类内部的逻辑。
总结:
<OOP 的三大要素之一:类>
类是“汇总”“隐藏”和“创建很多个”的结构。
1 | “汇总”子程序和变量。 |
2 | “隐藏”只在类内部使用的变量和子程序。 |
3 | 从一个类“创建很多个”实例。 |
1.2
三大要素之二:实现调用端公用化的多态
接着我们来看一下三大要素中的第二个要素——多态(polymorphism)。顾名思义,多态具有“可变为各种状态”的含义。
简单地说,多态可以说是创建公用主程序的结构。公用主程序将被调用端的逻辑汇总为一个逻辑,而多态则相反,它统一调用端的逻辑。
多态的结构 ▲
<OOP 的三大要素之二:多态>
多态是统一调用子程序端的逻辑的结构,即创建公用主程序的结构。
大家可能会觉得“公用主程序”这样的说法有点陈旧,但绝不可小瞧多态。虽说多态只是实现了程序调用端的公用化,但其重要性绝不亚于前面提到的类。
在 OOP 出现之前,公用子程序就已经存在了,但公用主程序并没有出现。框架和类库等大型可重用构件群也正是因为多态的存在才成为可能。因此,将多态称为与子程序并列的两项重大发明也不为过。
1.3
三大要素之三:去除类的重复定义的继承
OOP 三大要素中的最后一个要素是继承。
简单地说,继承就是“将类的共同部分汇总到其他类中的结构”。通过利用该结构,我们可以创建一个公用类来汇总变量和方法,其他类则可以完全借用其定义。
继承的结构 ▲
在 OOP 之前的由子程序构成软件的编程环境中,我们会创建一个公用子程序来汇总重复的命令群。同理,在由类构成软件的 OOP 环境中,我们可以创建一个公用类来汇总变量和方法。
也就是说,不仅局限于通过前面 介绍的多态来统一调用端,而且还要汇总相似的类中的共同部分。这是一种通过尽可能多地提供功能来让编程变轻松的思想。
在使用继承的情况下,我们将想要共同使用的方法和实例变量定义在公用类中,并声明想要使用的类继承该公用类,这样就可以直接使用公用类中定义的内容。在OOP 中,该公用类称为超类,利用超类的类称为子类。
我们可以这样理解:继承是将类定义的共同部分汇总到另外一个类中,并去除重复代码的结构。
另外,声明继承也就是声明使用多态。因此,在声明继承的子类中,为了统一方法调用方式,继承的方法的参数和返回值类型必须与超类一致。
继承和多态 ▲
<OOP 的三大要素之三:继承>
继承是将类定义的共同部分汇总到另外一个类中,并去除重复代码的结构。
1.4
对三大要素的总结
整理一下可以得到这个表:
这三种结构并不是分别出现的,在最初的面向对象语言 Simula 67 中 就拥有这三种结构,真是让人惊叹。提起 1967 年,就不得不提到无 GOTO 编程,这真是不平凡的一年。OOP 可以看作结构化语言的发展形式,但考虑到它在那个时代就出现了,因此说是编程语言的突然变异也不为过。
——本文选自《面向对象是怎样工作的》
标签:变量,汇总,多态,面向对象,实例,OOP,讲解,通俗,子程序 From: https://blog.51cto.com/u_15767091/6563991