iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 26
0
Software Development

給我 30 天,給你一輩子:Swift 從零開始系列 第 26

Day 26 | Swift Protocol 與 Extension:Protocol 篇

  • 分享至 

  • twitterImage
  •  

Protocol 協定

Protocol 是 Swift 重要的核心功能之一,那什麼是 Protocol ?

Protocol 有點像是一個房子藍圖,藍圖裡規範了房子所需具備的特性,可能要有門、窗物、煙囪或是其他物品,等同於在協定中定義了可以使用的屬性或是方法,如果你實作了協定,你就得依照協定中所定義的屬性、方法,依照這個房子藍圖,建立裡面該有的東西,按圖施工。

Protocol 的語法

protocol 協定名稱 {
    ...
}

透過 protocol 關鍵字來建立一個協定,Protocol 可以被視為是一種型別,至於 Protocol 的 Naming Conventions 最主要的有幾個重點:

  • 協定名稱需要符合 UpperCamelCase 命名規則
    • CollectionStringProtocol ...
  • 假定描述某個特定集合,最好是定義為名詞
    • PeopleCarAnimals ...
  • 假定描述的是一種功能性,可以使用 ableible 或是 ing 作為後綴詞
    • EquatableComparableProgressReporting ...
  • 協定中的屬性或是方法等,需要符合 lowerCamelCase 命名規則
    • getResultisHittable ...

那要怎麼去實作特定的 Protocols?

Protocol 可以被任何已封裝的特定型別來採用,舉凡 Enumeration、Class 以及 Struct,都可以實作協定。


小知識 1:何謂封裝?

封裝 ( 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 有點像是一個房子藍圖,藍圖裡規範了房子所需具備的特性,可能要有門、窗物、煙囪或是其他物品,等同於在協定中定義了可以使用的屬性或是方法

在藍圖提到你可能要有個門,可能要有個窗戶,至於真正你要如何做出這個門,要使用什麼材質,要怎麼裝上去,這都是藍圖上不會提到的。

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 這個協定中定義了兩個屬性:namesex,因為 Andrew 遵循了 Person Protocol,所以就必須使用 namesex 屬性。

Protocol 的方法

在 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 中一樣可以定義建構式,不過也是不用加上 {}

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 會在下一篇來介紹。


上一篇
Day 25 | Swift Subscripts
下一篇
Day 27 | Swift Protocol 與 Extension:Extension 篇
系列文
給我 30 天,給你一輩子:Swift 從零開始30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言