上一篇提到了什麼是依賴,這篇要聊一下學習OOP的時候的一個重要觀念:抽象化,這往往是學習路程上的一大障礙,畢竟要具體的把抽象的概念理解清楚真的不太容易。
想想以前學的時候自己也不是很懂,只是知道有這個東西,也不常用,直到工作的時候用到才體會到他的好處。
不過寫這篇的時候突然發現上一篇用的是Swift,我如果用Swift講OOP,可能會被iOS工程師砲死,人家明明是POP (Protocol-Oriented Programming),我可不能隨便亂講。
雖然Protocol一樣能達到效果,不過我這一篇還是先換成Kotlin吧,最後再補上Swift的程式碼
何謂抽象化
起手式一樣,先看定義,Wiki説
在計算機科學中,抽象化(英語:Abstraction)是將資料與程序,以它的語意來呈現出它的外觀,但是隱藏起它的實作細節。抽象化是用來減少程式的複雜度,使得程式設計師可以專注在處理少數重要的部份。一個電腦系統可以分割成幾個抽象層(Abstraction layer),使得程式設計師可以將它們分開處理。
定義看完了,似乎有些好處,但好像有看沒有懂,不知道為什麼要用?也不知道要怎麼使用?什麼時候要用?
先別急,我們從定義中找出一些蛛絲馬跡,抽象化有幾個特色
- 只呈現出外觀,隱藏實作細節
- 可以減少程式複雜度
綜合以上兩點,其實抽象化就是透過只定義行為的方式,達到降低程式複雜度的目的,那為什麼能降低程式複雜度呢?
大家可以回想一下上一篇提到的
已知iOS Programmer以及開發電腦Macbook Pro存在依賴關係,但遇到了公司刪減預算要改為採買Macbook Air當作開發機的狀況
當你勉強的修改現有的程式的時候,程式開始變得有點複雜 (醜),如果未來公司改為採買Mac mini呢?或是哪天公司有錢的不得了要給Mac Pro呢 (有這種公司麻煩介紹給我)?
你會發現這樣做毫無彈性,硬加下去只會讓程式碼越來越不好維護,但…
如果能有個方法,可以一以貫之的表示開發機器是不是就解決了呢?
沒錯!這也就是抽象化的精華所在。
在範例中其實對開發者來說,買什麼開發機器不是很重要,不管買的是什麼機器,只要能跑Xcode能開發iOS App就可以了
在Kotlin中抽象化有兩種方法
- abstract class (抽象類別)
- interface (介面)
兩種都可以達到抽象化的目的,視情況而定要選擇哪一種,
通常這些物件除了需要抽象的行為之外,還具備有很多共同的特性以及行為,才會使用抽象類別,否則考量到Kotlin是沒辦法做多重繼承的,通常選interface比較不會出什麼意想不到的問題,不過這邊滿吃經驗的,只能多練習,找出自己與團隊的平衡點,來決定要用哪一種抽象方法
那我們來試著用interface改寫一下上一篇範例的依賴關係吧,先假設只要能跑Xcode就能寫iOS App的話
//定義一個XcodeRunnable的interface
interface XcodeRunnable {
fun build()
}
//Macbook Pro
class MacbookPro: XcodeRunnable {
override fun build() {
// build with Macbook Pro
}
}
//Macbook Air
class MacbookAir: XcodeRunnable {
override fun build() {
// build with Macbook Air
}
}
//Mac Mini
class MacMini: XcodeRunnable {
override fun build() {
// build with Mac Mini
}
}
class IOSProgrammer {
val mac: XcodeRunnable = MacbookPro() // MacbookAir(), MacMini(), ...
fun buildCode() {
mac.build()
}
}
現在的程式碼看起來舒服得多,那swift該怎麼用protocol改寫呢?
//定義一個XcodeRunnable的protocol
protocol XcodeRunnable {
func build()
}
//Macbook Pro
struct MacbookPro: XcodeRunnable {
func build() {
// build with Macbook Pro
}
}
//Macbook Air
struct MacbookAir: XcodeRunnable {
func build() {
// build with Macbook Air
}
}
//Mac Mini
struct MacMini: XcodeRunnable {
func build() {
// build with Mac Mini
}
}
struct IOSProgrammer {
let mac: XcodeRunnable = MacbookPro() // MacbookAir(), MacMini(), ...
func buildCode() {
mac.build()
}
}
事實上protocol在這個範例中的寫法還可以更好, 這邊只是先簡單演示了基本的抽象功能,其餘的部分,我們留在下一篇再來探討吧。
這一篇我們聊到了抽象化,以及他的好處,也用Kotlin改寫了上一篇的前半部範例,相信大家對抽象化已經有了基本的認識,接下來多加練習就能多掌握一點囉。
不過我知道大家一定不會這樣就滿意,下一篇我們來進入主題:什麼是依賴反轉原則