物件导向程式编程的定义是使用“物件”来做设计,但并非所有的程式语言都直接支援“物件导向程式编程”相关技术与结构。对于OOP的准确定义及其本意存在着不少争论。通常,OOP被理解为一种将程序分解为封装数据及相关操作的模块而进行的编程方式。有别于其它编程方式,OOP中的与某数据类型相关的一系列操作都被有机地封装到该数据类型当中,而非散放于其外,因而OOP中的数据类型不仅有着状态,还有着相关的行为。
面向对象的构建要素由三个部分组成,实例、类和元数据,元数据会存储构建所需要的一切信息。从关系上看,类与实例为一对多关系,类和元数据为一对一关系,实例会指向类,类会指向元数据,这种关系最终构成了所有面向对象的基类(object)。
OOP理论,及与之同名的OOP实践相结合创造出了新的一个编程架构;OOP思想被广泛认为是非常有用的,以致一套新的编程范型被创造了出来。(其它的编程范型例如函数式编程或过程式编程专注于程序运行的过程,而逻辑编程专注于引发程序代码执行的断言)。对面向模拟系统的语言(如:SIMULA 67)的研究及对高可靠性系统架构(如:高性能操作系统和CPU的架构)的研究最终导致了OOP的诞生。其中由Deborah J. Armstrong进行的长达40年之久的计算机著作调查中,显示出了一系列面向对象程序设计的基本理论。物件导向程式特征被条列如下[3][4][5][6]
分享非物件导向程式前身语言
编辑
物件导向程式设计通常共享高阶编程语言的低阶功能。可用于建构一个程序的基本工具包括:
变数:能储存一些内建型态的资讯如整数与字元,也有些是资料结构像是字串、串列与杂凑表等包含内建或复合的变数如指标。
程序:也称为函式、方法或例程,是指输入资料产生输出结果,现代语言还包含结构化编程结构如程式回圈与条件。
类与对象
编辑
支持面向对象编程语言通常利用继承其他类达到代码重用和可扩展性的特性。而类有两个主要的概念:
类(Class):定义了一件事物的抽象特点。类的定义包含了数据的形式以及对数据的操作。
对象(Object):是类的实例(Instance)。
其中类(Class)定义了一件事物的抽象特点。类的定义包含了数据的形式以及对数据的操作。举例来说,“犬”这个类会包含犬的一切基础特征,即所有“犬”都共有的特征或行为,例如它的品种、毛皮颜色和吠叫的能力。类可以为程序提供模版和结构。一个类的方法和属性被称为“成员”。
我们来看一段伪代码:
类犬
开始
公有成员:
吠():
毛色:
私有成员:
品种:
结束
在这串代码中,我们声明了一个类,这个类具有一些犬的基本特征。关于公有成员和私有成员,请参见下面的继承性的内容。
对象(Object)是类的实例。物件有时会对应到现实世界中的事物,举例来说,一个图形程式可能有圆形、矩形与画面等物件,一个线上购物系统可能有购物车、顾客与产品等类别[7]。有时对象会表示更抽象的实体,比如一个被开启的档案或是一个提供美国惯用量测转换的服务。每个对象就是一个特定类别的实例(例如,名称是“李华”的物件可能是类别雇员的一个实例)。程序在面向对象编程当中被视为方法,变数被视为成员或属性。例如,“犬”这个类列举犬的特点,从而使这个类定义了世界上所有的犬。而大黄这个对象则是一条具体的犬,它的属性也是具体的。犬有毛色,而大黄的毛色是黄色的。因此,大黄就是犬这个类的一个实例。一个具体对象属性的值被称作它的“状态”。(系统给对象分配内存空间,而不会给类分配内存空间。这很好理解,类是抽象的,系统不可能给抽象的东西分配空间,而对象则是具体的。)
假设我们已经在上面定义了犬这个类,我们就可以用这个类来定义对象:
定义大黄是犬
大黄.毛色 : 黄
大黄.吠()
我们无法让犬这个类去吠,但是我们可以让对象“大黄”去吠,正如狗可以吠,但没有具体的狗就无法吠。
类和对象就好比是“实型”和“1.23”,“实型”是一种数据的类型,而“1.23”是一个真正的“实数”(即对象)。所有的“实数”都具有“实型”所描诉的特征,如“实数的大小”,系统则分配内存给“实数”存储具体的数值。
动态配置与讯息传递机制
编辑
定义上动态配置是指方法会随著实例动态的改变。而讯息传递机制(Message Passing)是指一个物件通过接受讯息、处理讯息、传出讯息或使用其他类别的方法来实作一定功能。如:大黄可以通过吠引起人的注意,从而导致一系列的事发生。
封装性
编辑
具备封装性(Encapsulation)的物件导向程式设计隐藏了某一方法的具体执行步骤,取而代之的是通过讯息传递机制传送讯息给它。封装是通过限制只有特定类别的物件可以存取这一特定类别的成员,而它们通常利用介面实作讯息的传入传出。举个例子,接口能确保幼犬这一特征只能被赋予犬这一类。通常来说,成员会依它们的存取权限被分为3种:公有成员、私有成员以及保护成员。有些语言更进一步:Java可以限制同一包内不同类别的存取;C#和VB.NET保留了为类别的成员聚集准备的关键字:internal(C#)和Friend(VB.NET);Eiffel语言则可以让使用者指定哪个类别可以存取所有成员。
因此,举例来说,“犬”这个类有“吠()”的方法,这一方法定义了犬具体该通过什么方法吠。但是,大黄的朋友并不知道它到底是如何吠的。
从实例来看:
/* 一个面向过程的程序会这样写: */
定义大黄
大黄.定音(442)
大黄.吸气()
大黄.吐气()
/* 而当狗的吠叫被封装到类中,任何人都可以简单地使用: */
定义大黄是犬
大黄.吠()
继承
编辑
继承性(Inheritance)是指,在某种情况下,一个类会有“子类”。子类比原本的类(称为父类)要更加具体化。例如,“犬”这个类可能会有它的子类“中华田园犬”和“牧羊犬”。在这种情况下,“大黄”可能就是中华田园犬的一个实例。子类会继承父类的属性(英语:Attribute (computing))和行为,并且也可包含它们自己的。我们假设“犬”这个类有一个方法(行为)叫做“吠()”和一个属性叫做“毛色”。它的子类(前例中的中华田园犬和牧羊犬)会继承这些成员。这意味着程序员只需要将相同的代码写一次。
在伪代码中我们可以这样写:
类 中华田园犬 : 继承 犬
开始
# 中华田园犬继承了犬类的所有方法
结束
# 创建对象
定义 大黄 是 中华田园犬
# 调用方法
大黄.吠()
/*
注释:注意这里调用的是犬类的吠()方法。
*/
回到前面的例子,“中华田园犬”这个类可以继承“毛色”这个属性,并指定其为黄色。而“牧羊犬”则可以继承“吠()”这个方法,并指定它的音调。子类也可以加入新的成员,例如,“牧羊犬”这个类可以加入一个方法叫做“放牧()”。设若用“中华田园犬”这个类定义了一个实例“大黄”,那么大黄就不会放牧,因为这个方法是属于牧羊犬的,而非中华田园犬。事实上,我们可以把继承理解为“是”或“属于”。大黄“是”中华田园犬,中华田园犬“属于”犬类。因此,大黄既得到了中华田园犬的属性,又继承了犬的属性。
我们来看伪代码:
类 牧羊犬 : 继承 犬
开始
公有成员:
放牧()
结束
类 中华田园犬 : 继承 犬
开始
# 中华田园犬类中没有放牧()方法
结束
# 创建对象
定义 大黄 是 中华田园犬
# 调用方法
大黄.放牧()
/*
注释:错误:放牧()是牧羊犬的成员方法,中华田园犬没有这个方法。
*/
当一个类从多个父类继承时,我们称之为“多重继承”。如一只狗既是中华田园犬又是牧羊犬。多重继承并不总是被支持的,因为它很难理解,又很难被好好使用。
多型
编辑
多型(Polymorphism)是指由继承而产生的相关的不同的类,其对象对同一消息会做出不同的响应[8]。例如,狗和鸡都有“叫()”这一方法,但是调用狗的“叫()”,狗会吠叫;调用鸡的“叫()”,鸡则会啼叫。
我们将它体现在伪代码上:
类 犬
开始
公有成员:
叫()
开始
吠()
结束
结束
类 鸡
开始
公有成员:
叫()
开始
啼()
结束
结束
# 创建对象
定义 大黄 是 犬
定义 红帽 是 鸡
# 调用方法
大黄.叫()
红帽.叫()
这样,虽然同样是做出叫这一种行为,但大黄和红帽具体做出的表现方式将大不相同。多态性的概念可以用在运算符重载上,可以根据需求查看相关界面。
抽象性
编辑
抽象(Abstraction)是简化复杂的现实问题的途径,它可以为具体问题找到最恰当的类定义,并且可以在最恰当的继承级别解释问题。举例说明,大黄在大多数时候都被当作一条狗,但是如果想要让它做中华田园犬做的事,你完全可以调用中华田园犬的方法。如果狗这个类还有动物的父类,那么你完全可以视大黄为动物。