Protocol 是 Swift 重要的核心功能之一,那什麼是 Protocol ?
Protocol 有點像是一個房子藍圖,藍圖裡規範了房子所需具備的特性,可能要有門、窗物、煙囪或是其他物品,等同於在協定中定義了可以使用的屬性或是方法,如果你實作了協定,你就得依照協定中所定義的屬性、方法,依照這個房子藍圖,建立裡面該有的東西,按圖施工。
protocol 協定名稱 {
...
}
透過 protocol
關鍵字來建立一個協定,Protocol 可以被視為是一種型別,至於 Protocol 的 Naming Conventions 最主要的有幾個重點:
Collection
、StringProtocol
...People
、Car
、Animals
...Equatable
、Comparable
、ProgressReporting
...getResult
、isHittable
...那要怎麼去實作特定的 Protocols?
Protocol 可以被任何已封裝的特定型別來採用,舉凡 Enumeration、Class 以及 Struct,都可以實作協定。
封裝 ( Encapsulation ),OOP 三大特性之一,透過封裝可以把一些實作細節包裝起來不讓外部知道,只需提供使用者需要知道的介面來讓外部存取。
聽完還是霧煞煞,舉個最經典的例子:電視遙控器。
我們在使用電視遙控器,只會看到有開關鍵、數字鍵以及其他功能鍵,然而我只需要知道我按下開關鍵,電視會被開啟或是關閉;按下數字鍵,可以轉換電視台,至於按下按鍵後,裡面電路板的運作原理,使用者不需要知道,透過封裝把元件及電路板隱藏起來,提供了幾個按鍵給使用者使用,這就是封裝。
就像《 Swift Class 與 Struct 快樂二選一:Class 篇 》,有一個汽車的類別,我們可以透過 engineStart()
來發動引擎,然而我們不必知道它是如何發動引擎。
至於要實作 Protocol 的方式跟 Class 繼承很像,可以說是一模一樣,就是使用 : Procotol
來實作協定,我們就以 struct 當作範例:
struct 結構名稱: 協定 {
...
}
而且這裡有個比較重要的是 Class 繼承只允許繼承一個父類別,但是這裏允許實作多個 Protocol:
struct 結構名稱: 協定1, 協定2, 協定3... {
...
}
如果是 Class
可以同時繼承以及實作 Protocols,但是有一個原則就是,先繼承後實作 Protocols,所以要繼承的父類別必須寫在第一個,後面才是要實作的 Protocols:
class 類別名稱: 父類別, 協定1, 協定2... {
...
}
Protocol 有點像是一個房子藍圖,藍圖裡規範了房子所需具備的特性,可能要有門、窗物、煙囪或是其他物品,等同於在協定中定義了可以使用的屬性或是方法
在藍圖提到你可能要有個門,可能要有個窗戶,至於真正你要如何做出這個門,要使用什麼材質,要怎麼裝上去,這都是藍圖上不會提到的。
Protocol 也是一樣的概念,不會去定義屬性的初始值,只需要定義好屬性的名稱及型別,還可以透過 get 和 set 來定義屬性的存取性。
protocol 協定名稱 {
var 唯讀變數: 型別 { get }
var 可讀寫變數: 型別 { get set }
}
protocol Person {
var name: String { get set }
var sex: String { get }
}
struct 人: Person {
var name: String
var sex: String { "male" }
}
let andrew = 人(name: "安竹皮皮")
print(andrew.name, andrew.sex)
Person 這個協定中定義了兩個屬性:name
和 sex
,因為 Andrew
遵循了 Person Protocol,所以就必須使用 name
及 sex
屬性。
在 Protocol 中除了可以定義屬性之外,當然也可以定義方法,在 Protocol 的方法一樣只需要定義方法名稱,不需要加上 {}
來定義好方法需要做的事情:
protocol 協定名稱 {
func 方法名稱 -> 型別
}
也可以指定回傳值的型別,來看一個例子:
protocol Propeller {
static func fly()
}
struct Person: Propeller {
static func fly() {
print("I can fly!")
}
}
Person.fly()
有一個 Propeller
Protocol,若要實作這個協定,就必須建立 fly()
的型別方法,也是可以使用 static 屬性或是方法,所以 Person 有了 Propeller 他就擁有 fly()
的技能,有點像模組的概念,我如果想要會飛,就加上一個會飛的模組,然而就可以飛了。
在 Protocol 中一樣可以定義建構式,不過也是不用加上 {}
:
protocol 協定 {
init(參數1: 型別, 參數2: 型別...)
}
但是在實作上就有些許的差別,如果實作某個特定 Protocol 其中的建構式,前面必須加上 required
的前綴字:
protocol Person {
var name: String { get set }
var sex: String { get set }
init(name: String, sex: String)
}
class 人: Person {
var name: String
var sex: String
required init(name: String, sex: String) {
self.name = name
self.sex = sex
}
}
let andrew = 人(name: "安竹皮皮", sex: "male")
print(andrew.name, andrew.sex)
如果在子類別中要修改父類別的建構子,同時又要實作協定中的建構子的話,這時候就是必須加上 required override
的前綴字:
protocol Person {
var name: String { get set }
var sex: String { get set }
init(name: String, sex: String)
}
class 人 {
var name: String
var sex: String
init(name: String, sex: String) {
self.name = name
self.sex = sex
}
}
class 亞洲人: 人, Person {
required override init(name: String, sex: String) {
super.init(name: name, sex: sex)
print("亞洲人 Instance 已建立")
}
}
let andrew = 亞洲人(name: "安竹", sex: "male")
// 亞洲人 Instance 已建立
print(andrew.name, andrew.sex)
// 安竹 male
以上就是 Protocol 基本介紹,但其實只有 Protocol 並不能使它成為 Swift 強大的功能之一,最方便的是擴展協定,可以依照不同的情況及使用方式,適度的調整,Extension
會在下一篇來介紹。