面向對象程序設計

物件導向程式設計（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)