【プログラミング初心者】Swift基礎~プロトコル~

#はじめに
今回はオブジェクト指向の考え方の１つであるポリモーフィズムの実現方法について紹介します。
ポリモーフィズムがどういうものかは[こちら](https://qiita.com/euJcIKfcqwnzDui/items/3a442bdee2996654dd97#%E3%83%9D%E3%83%AA%E3%83%A2%E3%83%BC%E3%83%95%E3%82%A3%E3%82%BA%E3%83%A0)で簡単に説明しています。

Swiftでポリモーフィズムを実現するには**プロトコル(protocol)**という機能を使います。
他言語でいうところの**インターフェース(interface)**に該当します。

#プロトコル
##プロトコルとは
プロトコルとは取り決め、約束事というような意味を持ちます。
Swiftのプロトコルとは、こんなメソッド、プロパティを必ず持つということを強制する技術です。

クラスや構造体では作成されたオブジェクトが持つプロパティやメソッドの具体的な実装が記述されています。

一方でプロトコルには具体的な処理の実装はなく、実装すべきプロパティ、メソッドが記述されているだけです。

クラスや構造体がオブジェクトの設計書とするならばプロトコルは仕様書といったところでしょうか。

まだピンとこないかと思いますが実装方法を見ていきましょう。

##プロトコル定義
プロトコルの定義方法は以下となります。

```swift
protocol プロトコル名 {
    var プロパティ名: 型名 { get set }

    func メソッド名(引数名: 型名)
}
```
クラス定義などと似ていますが、まずプロパティに`{ get set }`とあります。
`{ get set }`とした場合、そのプロパティが読み書き可能ということを表します。
`{ get }`の場合は読み込み専用ということを表します。

またメソッドがありますが具体的な処理が書かれていません。

このように実装すべきプロパティとメソッドを定義します。

プロトコルはこのように具体的な実装はない抽象的な定義です。
そのためクラスなどとは違いプロトコルからオブジェクトを作成することはできません。
プロトコルはクラスで継承させて使います。

実装例を見ていきます。
動物プロトコルを作ります。動物は必ず鳴くものとし`bark()`メソッドを持っているとします。

```AnimalProtocol.swift
protocol AnimalProtocol {
    /// 鳴く
    func bark()
}
```
これで`AnimalProtocol`を作成できました。

ここから動物プロトコルを継承した犬クラスを作成してみます。
一度以下のように実装してみてください。

```swift:Dog.swift
class Dog: AnimalProtocol {
    
}
```
これで犬クラスは作成されましたが
*Type 'Dog' does not conform to protocol 'AnimalProtocol'*
というエラーが発生したかと思います。
犬クラスは動物プロトコルに準拠していませんという意味です。
つまりプロトコルとして定義されているものが実装されていませんと怒られています。
このようにして実装を強制します。

では`bark()`メソッドを実装します。

```swift:Dog.swift
class Dog: AnimalProtocol {
    func bark() {
        print("ワン")
    }
}
```
これでエラーが消えました。
あとはいつも通りクラスからインスタンスを作成してあげるだけです。

```swift
let dog = Dog()
dog.bark()
```
プロパティもプロトコルに追加してあげます。

```AnimalProtocol.swift
protocol AnimalProtocol {
    /// 名前
    var name: String { get set }
    
    /// 鳴く
    func bark()
}
```
すると`Dog`に`name`プロパティを追加してあげる必要があります。

```swift:Dog.swift
class Dog: AnimalProtocol {
    var name: String
    
    init(name: String) {
        self.name = name
    }
    
    func bark() {
        print("ワン")
    }
}
```
```swift
let dog = Dog(name: "ポチ")
print(dog.name) // ポチ
```
これでプロパティもプロトコルに追加することができました。

基本的な定義の方法はこれだけです。

##プロトコルのメリット
さてこれで定義することはできましたがあまりメリットを感じないどころか、無駄な実装が増えたように感じます。
実際１つのクラスだけではメリットはあまりありません。

では動物プロトコルに準拠した猫クラスを追加してみましょう。

```swift:Cat.swift
class Cat: AnimalProtocol {
    var name: String
    
    init(name: String) {
        self.name = name
    }
    
    func bark() {
        print("にゃー")
    }
}
```

さらに人間クラスを作成し、ペットというプロパティを持っているとします。
人間クラスは`strokePet()`という撫でるメソッドを持ち、呼び出すとペットが鳴くという実装をします。
その場合以下のような実装になります。

```swift:Human.swift
class Human {
    var pet: AnimalProtocol
    
    init(pet: AnimalProtocol) {
        self.pet = pet
    }
    
    func strokePet() {
        self.pet.bark()
    }
}
```
すると以下のようになります。

```swift
print("---人間のペットが犬の場合---")
let human1 = Human(pet: Dog(name: "ポチ"))
human1.strokePet()

print("---人間のペットが猫の場合---")
let human2 = Human(pet: Cat(name: "タマ"))
human2.strokePet()
```
```:実行結果
---人間のペットが犬の場合---
ワン
---人間のペットが猫の場合---
にゃー
```
この結果が当たり前のように感じるかもしれませんが、クラスのみを扱ってきた今までとは以下の部分が決定的に異なります。

```swift
let human1 = Human(pet: Dog(name: "ポチ"))
let human2 = Human(pet: Cat(name: "タマ"))
```
`human1`と`human2`を比較すると初期化で与えられているオブジェクトのクラスが異なります。
今まではクラスに一致するオブジェクトしか引数に指定できませんでした。
これは人間クラスの初期化で`init(pet: AnimalProtocol)`と引数にクラスではなくプロトコルを指定しているためです。
このように`AnimalProtocol`に準拠したオブジェクトであればどんなオブジェクトを入れることができます。

また`stroke()`では

```swift
func strokePet() {
    self.pet.bark()
}
```
という呼び出しをしています。

`self.pet`は犬なのか猫なのかは人間クラスにはわかりません。
ですが`pet`は`AnimalProtocol`に準拠したオブジェクトなので`bark()`の実装を強制されていることはわかっているので犬なのか猫なのかを意識することなく呼び出すことができます。

このように人間クラスの実装を変えることなく具体的な処理の内容を変更することができるのがプロトコルのメリットであり、オブジェクト指向のポリモーフィズムという考え方です。

また、鳥クラスを追加したいとします。
この鳥クラスも`AnimalProtocol`に準拠させることで`Human`クラスの実装を変えることなく扱うことができます。

```swift:Bird.swift
class Bird: AnimalProtocol {
    var name: String
    
    init(name: String) {
        self.name = name
    }
    
    func bark() {
        print("チュン")
    }
}
```
```swift
let human3 = Human(pet: Bird(name: "鳥"))
human3.strokePet()
```
```:実行結果
チュン
```
このようにプロトコルとして扱うと呼び出し元は具体的なクラスを意識する必要がなくなるため変更に強いコードとなります。

##プロトコルを使わない場合は？
補足としてプロトコルを使わない場合の実装例を挙げます。

```swift:Dog.swift
class Dog {
    var name: String
    
    init(name: String) {
        self.name = name
    }
    
    func bark() {
        print("ワン")
    }
}
```
```Cat.swift
class Cat {
    var name: String
    
    init(name: String) {
        self.name = name
    }
    
    func bark() {
        print("にゃー")
    }
}
```
```Human.swift
class Human {
    var dog: Dog?
    var cat: Cat?
    
    init(pet: Any) {
        if let dog = pet as? Dog {
            self.dog = dog
        }
        if let cat = pet as? Cat {
            self.cat = cat
        }
    }
    
    func strokePet() {
        self.dog?.bark()
        self.cat?.bark()
    }
}
```
```swift
print("---人間のペットが犬の場合---")
let human1 = Human(pet: Dog(name: "ポチ"))
human1.strokePet()

print("---人間のペットが猫の場合---")
let human2 = Human(pet: Cat(name: "タマ"))
human2.strokePet()
```
このように人間クラスは具体的なクラスを指定した`dog`と`cat`を持つ必要があります。
またイニシャライザでは`pet`をキャストして犬なら犬、猫なら猫それぞれ判定してプロパティに入れる必要があります。
(`Any`は全ての型を表します。)

`strokePet`ではそれぞれがオプショナルとして定義しているのでnilではないオブジェクトのみ実行されるというような構成になっています。

さて、これはパッと見ただけであまり綺麗なコードとは思えません。
綺麗さだけならまだしも、さらに鳥クラスを追加するとなると人間クラスの実装も変更する必要があります。

このことからもプロトコルを使って実装した方がいいことがわかります。

#最後に
今回はプロトコルの実装方法について紹介しました。
少し難しかったり、どういったタイミングで実際に使えばいいのかイメージしづらいかったかもしれません。
このプロトコルをどう使っていくのかというところは正直かなり難しく、かなり経験が必要になるのかなと思います。
iOSアプリではプロトコルを使ったデリゲートという考えが多く使われています。
こちらは別途紹介しますが基本的な使い方とデリゲートを押さえておけばいいかと思います。

今回の内容は以上です。
本記事とは別でプログラミング未経験からiOSアプリ開発が行えるようになることを目的とした記事を連載しています。
連載は以下にまとめていますのでそちらも是非もご覧ください。
http://naoyalog.com/