什麼是依賴? 什麼是依賴反轉原則? (二)

Kash Yang
5 min readJun 24, 2023

--

上一篇提到了什麼是依賴,這篇要聊一下學習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改寫了上一篇的前半部範例,相信大家對抽象化已經有了基本的認識,接下來多加練習就能多掌握一點囉。

不過我知道大家一定不會這樣就滿意,下一篇我們來進入主題:什麼是依賴反轉原則

--

--