談談程式 1 — 物件導向程式設計:Object Oriented Programming (OOP)

這次不實做,來認識一下一些程式相關的概念。現在回頭看,驚覺我是個實作派,丟上來的文章都是作品或是步驟,都沒有好好研究或探討一些相關理論。

覺得是時候也該補足一些理論基礎,才不會在溝通上時產生障礙。而且我這個人最不會講話了,確實是該多讀一些文件來增加自己的詞彙量。

其實會想要開始閱讀文件還有一個原因,那就是面試!! 如果被問到一些專業術語,好一點聽得懂但回答上可能頻率不對,慘一點可能直接聽不懂。理論與實作並行,個人認為這樣才是面面俱到。

我們帥哥 Peter 依然很貼心的幫大家整理出初學者面試可能會需要的技術需求,這次就決定先從物件導向這塊研究起。

什麼是物件導向?

維基百科的描述

物件導向程式設計(英語:Object-oriented programming,縮寫:OOP)是種具有物件概念的程式設計典範,同時也是一種程式開發的抽象方針。它可能包含資料、特性、程式碼與方法。物件則指的是類別(class)的實例。它將物件作為程式的基本單元,將程式和資料封裝其中,以提高軟體的重用性、靈活性和擴充性,物件裡的程式可以存取及經常修改物件相關連的資料。在物件導向程式程式設計裡,電腦程式會被設計成彼此相關的物件

不要說以前的我看不懂了,現在的我去看這段文字還是需要去思考他到底在講什麼。

我的看法

在寫程式碼的過程我們用建造一個物體(物件)的方式整理我們的程式碼,意思就是把散亂的程式碼歸類在同一類的物件裡。想像我們今天在做一台車子,一定都是零件組成一塊賣出,總不能今天方向盤另外裝箱然後車子賣出去給別人吧。

剛剛提到兩個重要的觀念 — 類、物件。這兩個就是物件導向的理念核心。

類及物件 class & object

如果常常寫 code 的話,尤其是 swift,會發現很多東西都是由 class 去宣告的,甚至可以自己定義一個 class。 這個 class 就是 類,依我的解讀是,class 就是整理資料及功能的區塊,而物件就是從 class 實例化出來的產物。

網路上可以看到很多都是用 藍圖 形容 類、產品 形容 物件。這確實是一個很好理解的方式,也確實按照這樣的想法去寫程式會更簡單易懂。我來用機器人做解釋吧。

類即藍圖、產物及物件

今天有個工廠要設計一批機器人,他們需要知道通常一般的機器人會需要什麼樣的性能,這邊的性能就是類裡面的屬性(變數常數)及方法(功能)。

假設一般的機器人要有晶片、頭、身體等屬性,且要有移動的功能,可以這樣寫:

class 機器人 {
var 晶片
var 頭部
var 身體
var 手
func 移動()
}

這樣子就完成了藍圖(類)的設計,但光是設計藍圖還不夠,因為目前只是把抽象的想法呈現在紙上,我們還要把它做出來,這個做出來的動作就是實例化

let robot1 = 機器人()

可以發現只需要一個等號我們就可以從剛剛設計藍圖產生(實例化)出一個扎扎實實的機器人 — robot1。這個 robot1 就是所謂的物件。因為東西已經做出來了,我們可以對這個機器人發號施令叫他移動

robot1.移動()

我們的機器人剛剛在移動了!

三大特性

簡單介紹物件導向的概念後,來看看他又包含了哪些特性吧

封裝 (Encapsulation)

剛剛維基百科有提到:類將物件作為程式的基本單元,將程式和資料封裝其中。什麼是封裝,試想一下,今天有個類別是人,那我們會把屬性心臟放在哪呢,總不會把它放在外面任人踐踏吧。

var 心臟

這個是沒封裝的心臟,誰都拿得到,太可怕了。

class Human {
var 心臟
}

這是封裝過後的心臟,是不是看起來安全多了。

封裝後的特徵就是外界的事物看不到物件裡面的特徵,必須透過特定窗口才能取得該物件的資料。什麼意思,看以下

class Human {
var 心臟
func 借別人看心臟(){
print(心臟)
}
}
let person = Human()

目前只有創造一個人出來,但光是這樣是看不到他的心臟的,除非去問這個人願不願意給你看

person.借別人看心臟()

這樣才有機會看到心臟,且只要呼叫即可,不用再在乎內部是如何運作。

class Human {
var 心臟
}
class Monster {
var 心臟
}

封裝也可以防止其他類輕易得到自己的屬性

繼承 (Inheritance)

子女可以繼承家人財產,類也是可以繼承。在父類別宣告資料屬性及功能,繼承該類別的類為子類別,子類別除了擁有附類別的屬性及方法外,也能在宣告自己的屬性及方法。什麼意思,看以下

class 老爸 {
var 一百億元
func 經營大公司()
}

今天老爸有一百億元,還有可以經營大公司的能力。

class 兒子 : 老爸 {
var 帥氣的臉龐
func 跑趴()
}

今天契約寫好兒子可以繼承老爸的一切,除了兒子原有的帥氣臉龐跟跑趴功能,兒子也是有一百億元跟經營大公司的能力的。

多型 (Polymorphism)

多型跟繼承有些相關,且又跟其中的方法(功能)較有關係。簡單來說,就是一樣的功能,呈現不一樣的做法。

其中又分多載(overload)及覆寫 (override)

  • 多載 (overload)

在同一個類當中,存在著相同名稱的方法,但其參數名稱、型態、排列不同

class 泡茶機器人{
func 泡茶(型態:英式) {
print("倒入少量茶葉, 只沖一次")
}
func 泡茶(型態:中式) {
print("倒入多量茶葉,重複沖泡")
}
func 泡茶(型態:英式, 水容量:毫升) {
print("倒入少量茶葉, 及\(水容量)毫升的水,只沖一次")
}
}
let 機器人 = 泡茶機器人()
機器人.泡茶(型態:英式) //倒入少量茶葉, 只沖一次
機器人.泡茶(型態:中式) //倒入多量茶葉,重複沖泡
機器人.泡茶(型態:英式, 水容量:25) //倒入少量茶葉, 及25毫升的水,只沖一次

可以看到在泡茶機器人裡,同樣都是泡茶,但因為參數的型態不同或是數量不同而呈現不一樣的結果。

  • 覆寫(override)

意指子類別可以覆寫父類別的方法,但改別方法裡面的內容。

class 變色龍 {
func 變色() {
print("變紅色")
}
}
class 木頭上的變色龍:變色龍 {
func 變色() {
print("變咖啡色")
}
}
class 沙灘的變色龍:變色龍 {
func 變色() {
print("變黃色")
}
}
let 變色龍1 = 變色龍()
let 變色龍2 = 木頭上的變色龍()
let 變色龍3 = 沙灘的變色龍()
變色龍1.變色() //變紅色
變色龍2.變色() //變咖啡色
變色龍3.變色() //變黃色

結語

程式的寫法一直在演化,搞不好以後又有人創造出更優化的整理模式。OOP不見得是最好的但益處多多,舉凡可讀性倍增、利於維修、使程式碼更安全等就足以證明 OOP 的強大。善用他人創造的工具去打造更好的事物,我想這就是人類為什麼可以一直進步的其中原因吧。

--

--