軟體設計

函式庫 vs. 框架,判準是「誰呼叫誰」(控制反轉)

我們稱 Alamofire 為函式庫,稱 SwiftUI 為框架。但兩者不都是「拿來使用別人寫的程式碼」嗎?到底有什麼不同?

閱讀 4 分鐘
函式庫 vs. 框架,判準是「誰呼叫誰」(控制反轉) 封面圖

我們稱 Alamofire 為函式庫,稱 SwiftUI 為框架。但兩者不都是「拿來使用別人寫的程式碼」嗎?到底有什麼不同?

大家常聽到的答案是「框架比較大」,但大小不是本質。小型框架和巨型函式庫都存在。

誰呼叫誰。

掌握這唯一的判準後,就能自然連結到控制反轉(IoC)這個重要概念。學習相依性注入時也會再次遇到它,所以在這裡打好基礎很有幫助。

以下是重點總結。

  1. 函式庫:我的程式碼在需要時呼叫的工具集合。控制權在我手上
  2. 框架:掌握流程並呼叫我的程式碼的骨架。控制權在框架手上
  3. 這種顛倒的呼叫方向稱為控制反轉
  4. 好萊塢原則:「別打電話給我們,我們會聯絡你」

函式庫:由我呼叫的工具

函式庫是一組事先實作特定功能的程式碼。何時使用、以什麼順序使用,完全由我的程式碼決定。

// 流程的主導權在我的程式碼
let json = try JSONDecoder().decode(User.self, from: data)
let hash = SHA256.hash(data: input)

程式的開始、結束與整體流程由我規劃,再在中途取用需要的工具。這就像從工具箱拿出螺絲起子使用;螺絲起子不會決定工作的順序。

Alamofire、Kingfisher 都屬於這一類。何時送出網路要求、何時載入圖片,由我決定。


框架:呼叫我的骨架

框架是預先建立應用程式整體結構與執行流程的骨架。我只要把程式碼填入骨架留下的空位。

以 iOS App 為例就很清楚。App 的起點、事件迴圈與畫面生命週期,全都由 UIKit、SwiftUI 掌握。我撰寫的 viewDidLoad、body、onAppear 等,是框架在指定時機呼叫的程式碼。

struct ProfileView: View {
    var body: some View {   // 不是由我呼叫.
        Text("Hello")       // SwiftUI在需要時呼叫
    }
}

我們不會親手呼叫 body。何時呼叫、呼叫幾次由 SwiftUI 決定。流程的主導權已經對調。

呼叫方向相反。這就是控制反轉。
呼叫方向相反。這就是控制反轉。

控制反轉,以及好萊塢原則

這種顛倒的關係有它的名稱:控制反轉(IoC)。

在一般程序式程式中,我的程式碼掌握控制流程並呼叫外部程式碼。在以框架為基礎的程式中,框架掌握流程,而我的程式碼會在註冊的位置被呼叫。控制權轉向相反方向,因此稱為「反轉」。

用風趣方式表達這件事的說法,就是好萊塢原則。

「別打電話給我們,我們會聯絡你。」— 別打電話來,我們會聯絡你。

參加試鏡的演員不會一直打電話給製作公司,而是獲選後由製作公司聯絡。框架與我的程式碼之間,正是這種關係。

代理模式也是相同的原理。實作 UITableViewDataSource 時,是我呼叫 cellForRowAt 嗎?不是。表格視圖有需要時會呼叫我。框架世界的語法已滲入程式碼的各個角落。


所以在實務上有什麼不同?

學習方式不同。函式庫只要找出「有哪些功能」即可,但框架必須先了解「何時會呼叫我」(生命週期與呼叫規約)。這就是學習 UIKit 要從生命週期開始的原因。

替換成本不同。函式庫只要修改呼叫處就能替換,但整份程式碼都建立在框架骨架上,因此替換實際上等於重寫。UIKit → SwiftUI 的轉換不是修改幾個函式就能完成,也正是這個原因。

測試策略不同。框架呼叫的程式碼很難脫離框架單獨執行。因此才會反覆建議將邏輯分離到不依賴框架的層(純 Swift)。

別打電話給我們,我們會聯絡你 — 好萊塢原則
別打電話給我們,我們會聯絡你 — 好萊塢原則

面試時用一句話說明

「函式庫是由我的程式碼呼叫的工具,而框架是掌握控制流程並呼叫我的程式碼的骨架。這種控制權方向的差異,稱為控制反轉。」

追問通常會依序是「請舉一個 IoC 的例子」(生命週期方法、代理),以及「這樣有什麼好處」(標準化流程,讓開發者專注於商業邏輯)。


總結

  • 區分標準不是大小,而是呼叫方向
  • 函式庫:我的程式碼在需要時呼叫的工具。控制權在我手上
  • 框架:掌握流程並在指定時機呼叫我的程式碼的骨架。控制權在框架手上
  • 這種顛倒的控制權就是控制反轉(IoC),別名是好萊塢原則
  • viewDidLoad、body、代理方法全都是「被呼叫」的程式碼
  • 實務差異:先學框架生命週期、替換困難,而分離邏輯是測試的核心