面向对象程序设计

物件导向程式设计（Object-oriented programming，缩写：OOP）是种具有物件概念的程式设计典范，同时也是一种程式开发的抽象方针。它可能包含资料、特性、程式码与方法。对象则指的是类别（class）的实例。它将对象作为程序的基本单元，将程序和数据封装其中，以提高软件的重用性、灵活性和扩展性，物件里的程序可以访问及经常修改物件相关联的资料。在物件导向程式编程里，计算机程序会被设计成彼此相关的物件。

面向对象程序设计可以看作一种在程序中包含各种独立而又互相调用的对象的思想，这与传统的思想刚好相反：传统的程序设计主张将程序看作一系列函数的集合，或者直接就是一系列对电脑下达的指令。面向对象程序设计中的每一个对象都应该能够接受数据、处理数据并将数据传达给其它对象，因此它们都可以被看作一个小型的“机器”，即对象。目前已经被证实的是，面向对象程序设计推广了程序的灵活性和可维护性，并且在大型项目设计中广为应用。此外，支持者声称面向对象程序设计要比以往的做法更加便于学习，因为它能够让人们更简单地设计并维护程序，使得程序更加便于分析、设计、理解。反对者在某些领域对此予以否认。

当我们提到面向对象的时候，它不仅指一种程序设计方法。它更多意义上是一种程序开发方式。在这一方面，我们必须了解更多关于面向对象系统分析和面向对象设计（Object Oriented Design，简称OOD）方面的知识。许多流行的程式语言是物件导向的，它们的风格就是会透由物件来创出实例。

重要的物件导向程式语言包含Common Lisp、Python、C++、Objective-C、Smalltalk、Delphi、Java、Swift、C#、Perl、Ruby、JavaScript 与 PHP等。

## 特征

物件导向程式编程的定义是使用“物件”来做设计，但并非所有的程式语言都直接支援“物件导向程式编程”相关技术与结构。对于OOP的准确定义及其本意存在着不少争论。通常，OOP被理解为一种将程序分解为封装数据及相关操作的模块而进行的编程方式。有别于其它编程方式，OOP中的与某数据类型相关的一系列操作都被有机地封装到该数据类型当中，而非散放于其外，因而OOP中的数据类型不仅有着状态，还有着相关的行为。

面向对象的构建要素由三个部分组成，实例、类和元数据，元数据会存储构建所需要的一切信息。从关系上看，类与实例为一对多关系，类和元数据为一对一关系，实例会指向类，类会指向元数据，这种关系最终构成了所有面向对象的基类(object)。

OOP理论，及与之同名的OOP实践相结合创造出了新的一个编程架构；OOP思想被广泛认为是非常有用的，以致一套新的编程范型被创造了出来。（其它的编程范型例如函数式编程或过程式编程专注于程序运行的过程，而逻辑编程专注于引发程序代码执行的断言）。对面向模拟系统的语言（如：SIMULA 67）的研究及对高可靠性系统架构（如：高性能操作系统和CPU的架构）的研究最终导致了OOP的诞生。其中由Deborah J. Armstrong进行的长达40年之久的计算机著作调查中，显示出了一系列面向对象程序设计的基本理论。物件导向程式特征被条列如下

### 分享非物件导向程式前身语言

物件导向程式设计通常共享高阶编程语言的低阶功能。可用于建构一个程序的基本工具包括：

- 变数：能储存一些内建型态的资讯如整数与字元，也有些是资料结构像是字串、串列与杂凑表等包含内建或复合的变数如指标。
- 程序：也称为函式、方法或例程，是指输入资料产生输出结果，现代语言还包含结构化编程结构如程式循环与条件。

### 类与对象

支持面向对象编程语言通常利用继承其他类达到代码重用和可扩展性的特性。而类有两个主要的概念：

- 类（Class）：定义了一件事物的抽象特点。类的定义包含了数据的形式以及对数据的操作。
- 对象（Object）：是类的实例（Instance）。

其中类（Class）定义了一件事物的抽象特点。类的定义包含了数据的形式以及对数据的操作。举例来说，“犬”这个类会包含犬的一切基础特征，即所有“犬”都共有的特征或行为，例如它的品种、毛皮颜色和吠叫的能力。类可以为程序提供模版和结构。一个类的方法和属性被称为“成员”。

我们来看一段伪代码：

```
类'''犬'''
开始
　　公有成员:
　　　　吠():
　　　　毛色:
　　私有成员:
　　　　品种:
结束
```

在这串代码中，我们声明了一个类，这个类具有一些犬的基本特征。关于公有成员和私有成员，请参见下面的继承性的内容。

对象（Object）是类的实例。物件有时会对应到现实世界中的事物，举例来说，一个图形程式可能有圆形、矩形与画面等物件，一个线上购物系统可能有购物车、顾客与产品等类别。有时对象会表示更抽象的实体，比如一个被开启的档案或是一个提供美国惯用量测转换的服务。每个对象就是一个特定类别的实例（例如，名称是“李华”的物件可能是类别雇员的一个实例）。程序在面向对象编程当中被视为方法，变数被视为成员或属性。例如，“犬”这个类列举犬的特点，从而使这个类定义了世界上所有的犬。而大黄这个对象则是一条具体的犬，它的属性也是具体的。犬有毛色，而大黄的毛色是黄色的。因此，大黄就是犬这个类的一个实例。一个具体对象属性的值被称作它的“状态”。（系统给对象分配内存空间，而不会给类分配内存空间。这很好理解，类是抽象的，系统不可能给抽象的东西分配空间，而对象则是具体的。）

假设我们已经在上面定义了犬这个类，我们就可以用这个类来定义对象：

```
定义'''大黄'''是'''犬'''
大黄.毛色 : 黄
大黄.吠()
```

我们无法让犬这个类去吠，但是我们可以让对象“大黄”去吠，正如狗可以吠，但没有具体的狗就无法吠。

类和对象就好比是“实型”和“1.23”，“实型”是一种数据的类型，而“1.23”是一个真正的“实数”（即对象）。所有的“实数”都具有“实型”所描诉的特征，如“实数的大小”，系统则分配内存给“实数”存储具体的数值。

### 动态配置与讯息传递机制

定义上动态配置是指方法会随着实例动态的改变。而讯息传递机制（Message Passing）是指一个物件通过接受讯息、处理讯息、传出讯息或使用其他类别的方法来实作一定功能。如：大黄可以通过吠引起人的注意，从而导致一系列的事发生。

### 封装性

具备封装性（Encapsulation）的物件导向程式设计隐藏了某一方法的具体执行步骤，取而代之的是通过讯息传递机制传送讯息给它。封装是通过限制只有特定类别的物件可以存取这一特定类别的成员，而它们通常利用界面实作讯息的传入传出。举个例子，接口能确保幼犬这一特征只能被赋予犬这一类。通常来说，成员会依它们的存取权限被分为3种：公有成员、私有成员以及保护成员。有些语言更进一步：Java可以限制同一包内不同类别的存取；C#和VB.NET保留了为类别的成员聚集准备的关键字：internal（C#）和Friend（VB.NET）；Eiffel语言则可以让使用者指定哪个类别可以存取所有成员。

因此，举例来说，“犬”这个类有“吠”的方法，这一方法定义了犬具体该通过什么方法吠。但是，大黄的朋友并不知道它到底是如何吠的。

从实例来看：

```
/* 一个面向过程的程序会这样写： */
定义'''大黄'''
大黄.定音(442)
大黄.吸气()
大黄.吐气()

/* 而当狗的吠叫被封装到类中，任何人都可以简单地使用： */
定义'''大黄'''是'''犬'''
大黄.吠()
```

### 继承

继承性（Inheritance）是指，在某种情况下，一个类会有“子类”。子类比原本的类（称为父类）要更加具体化。例如，“犬”这个类可能会有它的子类“中华田园犬”和“牧羊犬”。在这种情况下，“大黄”可能就是中华田园犬的一个实例。子类会继承父类的属性 (计算机科学)和行为，并且也可包含它们自己的。我们假设“犬”这个类有一个方法（行为）叫做“吠”和一个属性叫做“毛色”。它的子类（前例中的中华田园犬和牧羊犬）会继承这些成员。这意味着程序员只需要将相同的代码写一次。

在伪代码中我们可以这样写：

```
类 中华田园犬 : 继承 犬
开始
    # 中华田园犬继承了犬类的所有方法
结束

# 创建对象
定义 大黄 是 中华田园犬

# 调用方法
大黄.吠()

/* 
注释：注意这里调用的是犬类的吠()方法。
*/
```

回到前面的例子，“中华田园犬”这个类可以继承“毛色”这个属性，并指定其为黄色。而“牧羊犬”则可以继承“吠”这个方法，并指定它的音调。子类也可以加入新的成员，例如，“牧羊犬”这个类可以加入一个方法叫做“放牧”。设若用“中华田园犬”这个类定义了一个实例“大黄”，那么大黄就不会放牧，因为这个方法是属于牧羊犬的，而非中华田园犬。事实上，我们可以把继承理解为“是”或“属于”。大黄“是”中华田园犬，中华田园犬“属于”犬类。因此，大黄既得到了中华田园犬的属性，又继承了犬的属性。

我们来看伪代码：

```
类 牧羊犬 : 继承 犬
开始
    公有成员:
        放牧()
结束

类 中华田园犬 : 继承 犬
开始
    # 中华田园犬类中没有放牧()方法
结束

# 创建对象
定义 大黄 是 中华田园犬

# 调用方法
大黄.放牧()

/* 
注释：错误：放牧()是牧羊犬的成员方法，中华田园犬没有这个方法。
*/
```

当一个类从多个父类继承时，我们称之为“多重继承”。如一只狗既是中华田园犬又是牧羊犬。多重继承并不总是被支持的，因为它很难理解，又很难被好好使用。

### 多型

多型（Polymorphism）是指由继承而产生的相关的不同的类，其对象对同一消息会做出不同的响应。例如，狗和鸡都有“叫”这一方法，但是调用狗的“叫”，狗会吠叫；调用鸡的“叫”，鸡则会啼叫。

我们将它体现在伪代码上：

```
类 犬
开始
    公有成员:
        叫()
        开始
            吠()
        结束
结束

类 鸡
开始
    公有成员:
        叫()
        开始
            啼()
        结束
结束

# 创建对象
定义 大黄 是 犬
定义 红帽 是 鸡

# 调用方法
大黄.叫()
红帽.叫()
```

这样，虽然同样是做出叫这一种行为，但大黄和红帽具体做出的表现方式将大不相同。多态性的概念可以用在运算符重载上，可以根据需求查看相关界面。

### 抽象性

抽象（Abstraction）是简化复杂的现实问题的途径，它可以为具体问题找到最恰当的类定义，并且可以在最恰当的继承级别解释问题。举例说明，大黄在大多数时候都被当作一条狗，但是如果想要让它做中华田园犬做的事，你完全可以调用中华田园犬的方法。如果狗这个类还有动物的父类，那么你完全可以视大黄为动物。

## 历史

面向对象程序设计的雏形，早在1960年代的Simula语言中即可发现，当时的程序设计领域正面临着一种危机：在软硬件环境逐渐复杂的情况下，软件如何得到良好的维护？面向对象程序设计在某种程度上通过强调可重复性解决了这一问题。20世纪70年代的Smalltalk语言在面向对象方面堪称经典——以至于30年后的今天依然将这一语言视为面向对象语言的基础。

计算机科学中对象和实例概念的最早萌芽可以追溯到麻省理工学院的PDP-1系统。这一系统大概是最早的基于容量架构（capability based architecture）的实际系统。另外1963年Ivan Sutherland的Sketchpad应用中也蕴含了同样的思想。对象作为编程实体最早是于1960年代由Simula 67语言引入思维。Simula这一语言是奥利-约翰·达尔和克利斯登·奈加特在奥斯陆的挪威计算中心为模拟环境而设计的。（据说，他们是为了模拟船只而设计的这种语言，并且对不同船只间属性的相互影响感兴趣。他们将不同的船只归纳为不同的类，而每一个对象，基于它的类，可以定义它自己的属性和行为。）这种办法是分析式程序的最早概念体现。在分析式程序中，我们将真实世界的对象映射到抽象的对象，这叫做“模拟”。Simula不仅引入了“类”的概念，还应用了实例这一思想——这可能是这些概念的最早应用。

20世纪70年代施乐PARC研究所发明的Smalltalk语言将面向对象程序设计的概念定义为，在基础运算中，对对象和消息的广泛应用。Smalltalk的创建者深受Simula 67的主要思想影响，但Smalltalk中的对象是完全动态的——它们可以被创建、修改并销毁，这与Simula中的静态对象有所区别。此外，Smalltalk还引入了继承性的思想，它因此一举超越了不可创建实例的程序设计模型和不具备继承性的Simula。此外，Simula 67的思想亦被应用在许多不同的语言，如Lisp、Pascal。

面向对象程序设计在80年代成为了一种主导思想，这主要应归功于C++——C语言的扩充版。在图形用户界面（GUI）日渐崛起的情况下，面向对象程序设计很好地适应了潮流。GUI和面向对象程序设计的紧密关联在Mac OS X中可见一斑。Mac OS X是由Objective-C语言写成的，这一语言是一个仿Smalltalk的C语言扩充版。面向对象程序设计的思想也使事件处理式的程序设计更加广泛被应用（虽然这一概念并非仅存在于面向对象程序设计）。一种说法是，GUI的引入极大地推动了面向对象程序设计的发展。

苏黎世联邦理工学院的尼克劳斯·维尔特和他的同事们对抽象数据和模块化程序设计进行了研究。Modula-2将这些都包括了进去，而Oberon则包括了一种特殊的面向对象方法——不同于Smalltalk与C++。

面向对象的特性也被加入了当时较为流行的语言：Ada、BASIC、Lisp、Fortran、Pascal以及种种。由于这些语言最初并没有面向对象的设计，故而这种糅合常常会导致兼容性和维护性的问题。与之相反的是，“纯正的”面向对象语言却缺乏一些程序员们赖以生存的特性。在这一大环境下，开发新的语言成为了当务之急。作为先行者，Eiffel成功地解决了这些问题，并成为了当时较受欢迎的语言。

在过去的几年中，Java语言成为了广为应用的语言，除了它与C和C++语法上的近似性。Java的可移植性是它的成功中不可磨灭的一步，因为这一特性，已吸引了庞大的程序员群的投入。

在最近的计算机语言发展中，一些既支持面向对象程序设计，又支持面向过程程序设计的语言悄然浮出水面。它们中的佼佼者有Python、Ruby等等。

正如面向过程程序设计使得结构化程序设计的技术得以提升，现代的面向对象程序设计方法使得对设计模式的用途、契约式设计和建模语言（如UML）技术也得到了一定提升。

## 物件导向编程语言

支持部分或绝大部分面向对象特性的语言即可称为基于对象的或面向对象的语言。Simula (1967)被视为第一个具有物件导向特性的语言。早期，完全面向对象的语言主要包括Smalltalk等语言，目前较为流行的语言中有Java、C#、Eiffel等。随着软件工业的发展，比较早的程序导向的语言在近些年的发展中也纷纷吸收了许多物件导向的概念，比如C→C++，C→Objective-C，BASIC→Visual Basic→Visual Basic .NET，Pascal→Object Pascal，Ada→Ada95。“纯粹”的物件导向语言, 因为所有的东西都是由物件所组成，例如：Eiffel, Emerald, JADE, Obix, Ruby, Scala, Smalltalk, Self.

### 脚本中的OOP

Python和Ruby是建立在OOP原理的脚本语言，Perl和PHP亦分别在Perl 5和PHP 4时加入物件导向特性。

## 参见

- 一次且仅一次（once and only once，OAOO）
- Distributed Component Object Model
- UML

## 外部链接

- Computers/Programming/Methodologies/Object-Oriented
- Introduction to Object Oriented Programming Concepts (OOP) and More by L.W.C. Nirosh
- Discussion about the flaws of OOD
- OOP Concepts (Java Tutorials)