面向对象
概述
面向对象,与之相对的是早期语言的面相过程。
如今的许多主流语言或多或少都是面向对象的语言。如果你觉得这很奇怪,或者对「对象到底有什么好的」这种事感到困惑,那么你需要阅读下面的这些文字。
在面向对象之前
我们使用伪代码来简单地描述以下情形。伪代码不是任何一门编程语言的合法代码,仅作演示使用。
编程题
我校正在举办一个学科组队竞赛,每个队伍的人数在3~6人之间,队伍的得分是队员得分的平均分。
请编写一个程序,依次接收每个队伍基本信息(队名、队伍编号、人数)的每名成员的得分,然后为接收到的所有队伍排序,按名词顺序输出队伍的基本信息与平均分。
输入格式:若共有N支队伍,则输入分为N行,在每一行内依次给出队名、编号与人数,然后是每名队员的得分。
输出格式:按照队伍名次排序自高到低输出N行,每行内依次输出名次、队名、编号与均分。
如果我们熟练掌握了一门不支持面向对象的语言,例如C语言,我们可能会写出类似下面的代码:
在线评测
定义 自定义数据结构 team 包括队伍的基本信息与平均分
定义 数组 teams
while 输入有效:
接收并处理一行输入
定义 _team = 队伍的基本信息与计算得到的平均分
teams.增加数据(_team)
teams.排序()
for teams中的每一个数据:
打印数据
这样写是完全没有问题的!C语言考试的编程题就该这样写。在这个程序的编写中,我们采取了面向过程的思维方式,在代码中描述了一个接收输入、计算排序、依次输出的连续的过程。
似乎也没什么问题,对吧?
在面向对象时
让我们首先思考,对象是什么东西?
在上面的编程题中,一支参赛队伍就可以是一个对象。每一支队伍都拥有它的队伍名称、队伍编号、队员数量等信息,这些就是每支队伍的属性。在面向对象地开发程序时,我们重点关注如何使用代码来描述对象。
对象不仅可以拥有属性,还可以拥有方法。例如说,参赛队伍拥有增加新队员、移除旧队员的方法(虽然题中没有体现),如果一支队伍的人员发生变动,则队伍属性也会发生变化。
支持面向对象的语言允许你定义新的类型来开始面向对象编程。下面仍然是伪代码:
定义 自定义类型 Team
定义 队伍名, 队伍编号, 队伍人数
定义 方法 初始化方法(或者被称为构造函数)(队伍名, 队伍编号, 队伍人数)
定义 方法 增加新队员(...) 无返回
定义 方法 移除旧队员(...) 无返回
然后,类似于定义一个字符串类型的变量,我们可以定义一个Team
类型的变量,称为将Team
实例化。
定义 team = Team(...)
// 有的语言可能需要 new 关键字才能实例化,这里不考虑这种差别
现在,team
对象是Team
类型的变量,也是Team
的一个实例。
初始化方法/构造函数
初始化方法是在类被实例化(也就是对象被创建)时运行的方法。原则上,每个类都需要构造方法。
初始化方法是一个函数,但你无法设置它的返回值,因为它必须返回对象本身。
定义 team = Team(...)
在我们实例化类型时,传入的参数实际上被传入到初始化方法中,也就是说我们实际上执行了初始化方法,并在执行后team
成为了Team
的实例,那么初始化方法只能返回刚刚建立的实例本身。
当然,你同样可以说初始化方法/构造函数不返回值,只要你理解结果如此即可。
继承
如果我们需要编写多个相似的类,比如宠物猫和宠物狗,它们高度相似,例如都有名字、性别、年龄、毛色等属性,都有睡觉、走路、跑步、恶龙咆哮等方法。
写重复的代码多没意思,我们不如只写一遍好了。
定义 自定义类型 CuteAnimal
定义 名字, 性别, 年龄, 毛色, ...
定义 方法 初始化方法(...)
定义 方法 睡觉() 无返回
定义 方法 走路() 无返回
定义 方法 跑步() 无返回
定义 方法 恶龙咆哮() 返回字符串
定义 自定义类型 CuteCat 继承 CuteAnimal
定义 方法 初始化方法(...)
定义 自定义类型 CuteDog 继承 CuteAnimal
定义 方法 初始化方法(...)
在上面的伪代码中,CuteCat
和CuteDog
都是CuteAnimal
的子类、派生类,而CuteAnimal
是基类、超类、父类。
继承有两种主要用处,上方是其中一种,即用来构建多个相似的类;另外一种用处是在不修改已有的类本身的情况下修改已有的类,请看下面的伪代码。
定义 自定义类型 CuteAnimal
定义 名字, 性别, 年龄, 毛色, ...
定义 方法 初始化方法(...)
定义 方法 睡觉() 无返回
定义 方法 走路() 无返回
定义 方法 跑步() 无返回
定义 方法 恶龙咆哮()
打印 嗷呜嗷呜
返回 嗷呜嗷呜
定义 自定义类型 CuteCuteAnimal 继承 CuteAnimal
定义 方法 初始化方法(...)
定义 方法 恶龙咆哮()
打印 呜哇呜哇
返回 呜哇呜哇
// 定义 cat = CuteAnimal(...)
定义 cat = CuteCuteAnimal(...)
cat.恶龙咆哮()
也可以在派生类中增加基类中没有的属性或方法。许多语言还要求在派生类中调用基类的初始化方法,不要忘记哦。