Alex Liang

Swift Protocol 介紹

在design pattern中,interface是一個重要的概念。
它能夠將class之間共通但些許不同的行為提升到更高的層次,解決物件導向裡容易誤用繼承的問題
而Swift提供protocol做為interface和資料型態,讓物件導向設計變得更直觀

Declare

1
2
3
4
5
6
7
8
9
10
11
12
13
14
protocol ExampleProtocol {
var someProperty: String { get } // someProperty is gettable
var otherProperty: String { get set} // otherProperty is gettable and settable
func concatenate(x: String, y: String) -> String
}

class TestClass: ExampleProtocol {
var someProperty: String = "A test class"
var otherProperty: String

func concatenate(x: String, y: String) -> String {
return "\(x) \(y)"
}
}

protocol的property可宣告為gettable或settable,後者同時也可使用get
而function只需要宣告輸入參數和回傳型態,不需要實作

class一旦使用protocol,就必須提供其property和function的實作。

Usage

使用protocol在實務上有什麼好處呢?
假如我們有二個class,Employee和HourlyEmployee,分別為正職和計時人員。 以及計算員工薪資的function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Employee {
let name: String
let address: String

init(name: String, address: String) {
self.name = name
self.address = address
}

func pay() -> (salary: Double, bonus: Double) {
return (3000, 400)
}
}

class HourlyEmployee: Employ {
var hourlyWage: Double = 15.00
var hoursWorked: Double = 8

override pay() -> (salary: Double, bonus: Double) {
return (hourlyWage * hoursWorked, 0)
}
}

func payEmployee(employee: Employee) {
let paycheck = employee.pay()
}

假如之後程式修改時,不小心將HourlyEmployee的pay刪除,則呼叫payEmployee時會回傳Employee的結果
這在程式碼增長的情況下是很容易出現的bug

使用protocol可減少此類bug的發生

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
protocol Payable {
func pay() -> (salary: Double, bonus: Double)
}

class Employee: Payable {
//略
func pay() -> (salary: Double, bonus: Double) {
return (3000, 400)
}
}

class HourlyEmployee: Payable {
//略

override pay() -> (salary: Double, bonus: Double) {
return (hourlyWage * hoursWorked, 0)
}
}

func payEmployee(employee: Employee) {
let paycheck = employee.pay()
}

假如HourlyEmployee的pay function被刪,complier會提示error。

Types

protocol不只能當interface使用,也能做type結合array、dictionary使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
protocol Flyable {
func fly()
}

class Human {
let name: String
}

class Bird: Flyable {
let name: String

func fly() {
print("I believe I can fly!")
}
}

func makeActions(action: [Flyable]) {

}

let human = Human(name: "John")
let sparrow = Bird(name: "GOT")

let animals: [Flyable] = [human, sparrow] // Error, human is not Flyable

指定animals為Flyable的array時,compiler會檢查array的值是否符合
這也類似有經驗的C/C++ programmer會將使用enum當function的參數,否則各參數都是int時,容易讓使用者誤用
protocol能幫我們減少無謂的bug及提高程式碼的可讀性