Protocolとは
Protocolとはある機能を実現するclassやstructが必ず実装するべきメソッドやプロパティの名前や型を定義するものです。
他の言語いう抽象クラスやInterfaceにあたります。
protocol CanFly {
func fly() // 実際の処理は書かない
}
protocolを定義するときは、必要なメソッドやプロパティの名前や型を定義するのみで実際の処理は書きません。
どんなときに便利か
特定のclassやstructが持っているべき要件(このメソッドやプロパティを持っているべき)を適用したい場合に便利です。
例えばBirdクラスを作ります。鳥の特徴は空を飛べて、メスは卵を産みますね。
class Bird {
var isFemale = true
func layEgg() {
if isFemale {
print("トリは卵を産みました")
}
}
func fly() {
print("トリは翼で羽ばたいて空を飛びました")
}
}
次にEagleクラスとPenguinクラスを作りましょう。どちらも鳥の一種なのでBirdクラスを継承して定義してみます。
Eagleの特徴は上昇気流に利用して高く上がって滑空して移動すること、Penguinの特徴は泳ぐことですね。
ここでインスタンスを作って使えるメソッドを確認してみます。
class Eagle: Bird {
func soar() {
print("タカは上昇気流に乗って高く上がりました")
}
}
class Penguin: Bird {
func swim() {
print("ペンギンは翼で羽ばたいて泳ぎました")
}
}
let myEagle = Eagle()
myEagle.layEgg() // "トリは卵を産みました"
myEagle.fly() // "トリは翼で羽ばたいて空を飛びました"
myEagle.soar() // "タカは上昇気流に乗って高く上がりました"
let myPenguin = Penguin()
myEagle.layEgg() // "トリは卵を産みました"
myEagle.fly() // "トリは翼で羽ばたいて空を飛びました"
myEagle.swim() // "ペンギンは翼で羽ばたいて泳ぎました"
一見良さそうですが、ペンギンは空を飛ぶことはできませんのでこの実装だとおかしなことになってしまいます。
更に同じく空を飛ぶAirplaneクラスを作る場合はどうなるでしょう。Birdクラスがflyメソッドを持っていますが、翼で羽ばたくわけではないのでオーバーライドして少し内容を変えましょう。
class Airplane: Bird {
override func fly() {
print("飛行機はエンジンを使って空を飛びました")
}
}
let myAirplane = Aiplane()
myAirplane.layEgg() // "トリは卵を産みました"
myAirplane.fly() // "飛行機はエンジンを使って空を飛びました"
これもよく見るとBirdクラスが持っているlayEggメソッドも継承しているので飛行機が卵を産むことになっていますね。おかしいです。
それなら継承せずに必要なクラスにだけflyメソッドを定義すればよいのですが、プログラムの規模が大きくなってくると、複雑になりそうですし実装漏れも起きそうです。
Protocolを使って実装する
そんなときにProtocolを使うと実装が必要なメソッドやプロパティを定義しておけるので便利です。
例えば今までの例でいうとEagleとAirplaneはflyメソッドを持っている必要がありますが、Penguinは必要ないですね。
従ってEagleとAirplaneのみにCanFlyを適用します。
protocol CanFly {
func fly() // 必要なメソッドの名前を書くだけ、実装はclassやstructの定義時に書く
}
class Bird {
var isFemale = true
func layEgg() {
if isFemale {
print("トリは卵を産みました")
}
}
}
class Eagle: Bird, CanFly { // CanFlyを適用する
func fly() { // flyメソッドを実装しないとエラーになる
print("タカは翼で羽ばたいて空を飛びました")
}
func soar() {
print("タカは上昇気流に乗って高く上がりました")
}
}
class Penguin: Bird { // Penguinは飛べないのでCanFlyは適用しない
func swim() {
print("ペンギンは翼で羽ばたいて泳ぎました")
}
}
struct Airplane: CanFly { // CanFlyを適用する
func fly() { // flyメソッドを実装しないとエラーになる
print("飛行機はエンジンを使って空を飛びました")
}
}
こうするとペンギンが空を飛んだり、飛行機が卵を産んだりしなくて済みます。
因みに継承はclassではできてstructではできませんが、protocolの適用はclass/structどちらでもできるのが特徴です。
参考
https://docs.swift.org/swift-book/LanguageGuide/Protocols.html
https://www.udemy.com/course/ios-13-app-development-bootcamp/