昨天我們學會了用 閉包(Closure) 讓「功能」變得靈活。今天,我們要讓「資料型別」本身也變得同樣靈活!
想像一下,我們要如何保證 一隻鳥 和 一架飛機 這兩種截然不同的東西,都一定擁有fly()
這個方法?答案就是讓它們簽署同一份「行為契約」。
在 Swift 中,這份契約就是 協定(Protocol)。它定義了一套所有型別都可以承諾遵守的規範,是實現彈性、模組化設計,以及Delegate
等重要模式的基礎。準備好來當個制定規則的架構師了嗎?
is
和 as?
檢查遵循狀態。首先,我們定義一份「飛行合約」,要求所有會飛的東西,都必須有名字和飛行方法:
// 定義一份「可飛行」的協定
protocol Flyable {
// 要求:必須有一個可讀取的「名字」屬性
var name: String { get }
// 要求:必須有一個「飛行」的方法
func fly()
}
現在,我們讓 Bird
(結構)和 Plane
(類別)這兩個完全不同的型別,都來簽署並遵守這份合約:
struct Bird: Flyable {
var name: String
func fly() {
print("鳥 \(name) 正在展翅高飛。")
}
}
class Plane: Flyable {
var name: String
init(name: String) {
self.name = name
}
func fly() {
print("飛機 \(name) 正在引擎全開地飛行。")
}
}
協定最酷的地方,就是可以當作一個「通用型別」來使用。我們可以建立一個只認識 Flyable
的函式,它不在乎傳進來的是鳥還是飛機。
let sparrow = Bird(name: "麻雀")
let boeing747 = Plane(name: "波音747")
// 這個函式只認得「會飛的東西」,不關心具體型別
func letItFly(flyer: Flyable) {
print("準備起飛...")
flyer.fly()
}
letItFly(flyer: sparrow) // 傳入 Bird
letItFly(flyer: boeing747) // 傳入 Plane
假設我們希望所有會飛的東西,都能「降落」。我們可以透過擴展協定,直接為所有遵循者加上預設的降落功能:
extension Flyable {
func land() {
print("\(name) 降落了。")
}
}
// 現在不管是鳥還是飛機,都會自動獲得 land() 方法!
sparrow.land() // 印出 麻雀 降落了。
boeing747.land() // 印出 波音747 降落了。
委任模式是協定最重要的應用,需要一個完整的範例來展示「委託方」和「受委託方」的互動:
protocol TaskDelegate {
func didFinishTask(taskName: String) // 任務完成後要回報
}
// 2. 建立「工人」(委託方),他身上有個「監工」的屬性
class Worker {
var delegate: TaskDelegate? // 監工是誰還不確定,所以是 Optional
func doTask() {
print("工人:開始執行任務...")
// ...執行任務中...
print("工人:任務完成!")
// 透過合約,向監工回報
delegate?.didFinishTask(taskName: "清洗窗戶")
}
}
// 3. 建立「經理」(受委託方),他同意遵守「任務合約」
class Manager: TaskDelegate {
func didFinishTask(taskName: String) {
print("經理:收到回報,\(taskName) 任務已完成。做得很好!")
}
}
// --- 實際應用 ---
let worker = Worker()
let manager = Manager()
// 把經理指派為工人的監工
worker.delegate = manager
// 工人開始工作,完成後會自動向他的監工(經理)回報
worker.doTask()
協定能繼承其他協定,組合更多要求。
使用 SomeProtocol & AnotherProtocol
表示同時遵循多個協定:
protocol SomeProtocol {}
protocol AnotherProtocol {}
protocol CombinedProtocol: SomeProtocol, AnotherProtocol {
// 繼承多個協定要求
}
有時候,我們雖然知道一個物件是Flyable
,但可能想進一步確認它到底是Bird
還是Plane
,並存取其特有的屬性。這時就可以使用is
和as?
:
// 建立一個包含不同型別的 Flyable 陣列
let flyers: [Flyable] = [Bird(name: "老鷹"), Plane(name: "F-22猛禽戰鬥機")]
for flyer in flyers {
// 使用 is 來檢查型別
if flyer is Bird {
print("\(flyer.name) 是一隻鳥。") // 正確寫法
} else if flyer is Plane {
print("\(flyer.name) 是一架飛機。") // 正確寫法
}
// 使用 as? 來嘗試安全轉換型別
if let plane = flyer as? Plane {
print("確認過眼神,\(plane.name) 是一架飛機沒錯。")
}
}
今天我們深入探索了 Swift 的核心靈魂:協定(Protocol)!我們學會了協定如何作為一份「行為契約」,只定義規範而不提供實作,並讓任何class
、struct
或enum
都能遵循它。
在此基礎上,我們解鎖了協定的強大應用:將它作為型別來提升程式彈性、透過擴展提供預設實作來減少重複的程式碼,以及應用在 委任模式(Delegation) 上來降低程式碼的耦合度。
最重要的是,你理解了協定的核心思想:「專注於行為而非實作細節」,這將幫助你打造出更靈活、更易於維護的程式架構。
這一天終於來了! 經過 14 天的語法修煉,我們終於要告別黑底白字的 Playground,正式踏上打造真實 App 畫面的旅程!
明天,我們將正式啟動 Xcode 的 Storyboard,你將學會如何像設計師一樣,用滑鼠拖曳出按鈕與標籤,並見證最神奇的一刻:透過 IBOutlet 將畫面元件與你的程式碼串連起來。
這將是你第一次讓程式碼真正「看得見」、能與使用者「互動」。準備好打造你的第一個 iOS App 了嗎?
敬請期待《Day 15|Xcode 正式啟程:從語法邁向實作,建立你的第一個 iOS App!》