在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 } var otherProperty: String { get set } 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]
指定animals為Flyable的array時,compiler會檢查array的值是否符合
這也類似有經驗的C/C++ programmer會將使用enum當function的參數,否則各參數都是int時,容易讓使用者誤用
protocol能幫我們減少無謂的bug及提高程式碼的可讀性