【Swift】Protocol の拡張について


# はじめに

Protocol を拡張するにあたって `extension` であったり、 `where` の絞りこみがよくわかっていなかったので整理してみました。
（Xcode:12.5.1）

# 基本的な使い方

例：Protocol で鳥が　「息をすること」 = `breathe()` と「飛ぶこと」 = `fly()` を表現します。

```swift
protocol BirdProtocol {
    func breathe()
    func fly()
}
```

```swift
class Crow: BirdProtocol {
    func breathe() {}

    func fly() {}
}
```

Playground で BirdProtocol を継承した Class を宣言すると勝手に上のように関数の定義をサポートしてくれます。

# 拡張方法

例：カモのように泳げる鳥もいるので、「泳げること」 = `swim()` も追加してあげます。

この場合に拡張する方法を 3 つ紹介したいと思います。

- 拡張方法その1
    - 複数の Protocol を継承させる ← **おすすめ**
- 拡張方法その2
    - 既存の Protocol を継承した Protocol を継承させる
- 拡張方法その3
    - 既存の Protocol を extension する

## 拡張方法その1: 複数の Protocol を継承させる

### Protocol の定義

```swift
protocol SwimAble {
    func swim()
}
```

### Class の定義

Class の定義の際に複数の Protocol を継承させることが可能です。

```swift
class KamoA: BirdProtocol, SwimAble {
    func breathe() {}

    func fly() {}

    func swim() {}
}
```

下記のように extension で分けて表現するとこともできます。

```swift
class KamoA: BirdProtocol {
    func breathe() {}
    
    func fly() {}
}

extension KamoA: SwimAble {
    func swim() {}
}
```

### 実行してみる

実際に `swim()` を実行する際には、以下のように Classを格納する変数の型の表現を気を付けてあげる必要があります。

```swift
// 例1
let birdA1 = KamoA() // 型指定なし
birdA1.swim() // 勝手にSwimAbleを適応してくれる

// 例2
let birdA2: BirdProtocol = KamoA() // BirdProtocolの型指定する
// birdA2.swim() // BirdProtocolにはswim()がないのでコンパイルエラーとなる!!

// 例3
let birdA3: BirdProtocol & SwimAble = KamoA() // 複数のProtocolを指定する場合はこう書く
birdA3.swim() // これならswim()できる
```

### 使い所

一番一般的な拡張のさせ方の認識です。
迷ったらこれでいいと思います。

## 拡張方法その2: 既存の Protocol を継承した Protocol を継承させる

既存の Protocol を継承した Protocol を作成します。

### Protocol の定義

```swift
protocol SwimAbleBird: BirdProtocol {
    func swim()
}
```

ちなみに、以下のように書くこともできます。
ただし、Protocol の宣言では `where` を使わず、extension 時に拡張したい範囲を絞るために `where` を用いる認識です。 

```swift
// こうはあまり書かないはず？
protocol SwimAbleBird where Self: BirdProtocol {
    func swim()
}
```

### Class の定義

`SwimAbleBird` を継承させるだけで、`BirdProtocol` の機能も保証してくれます。

```swift
class KamoB: SwimAbleBird {
    func breathe() {}

    func fly() {}

    func swim() {}
}
```

### 実行してみる

複数 Protocol を 継承した場合に比べて、型宣言するときに `BirdProtocol & SwimAble` のように表現しなくても、`SwimAbleBird` だけでシンプルに `swim()` を実行することができます。

```swift
// 例1
let birdB1 = KamoB()
birdB1.swim()

// 例2
let birdB2: SwimAbleBird = KamoB() // SwimAbleBirdの型を指定する
birdB2.swim() // SwimAbleBirdにはswim()がある
```

### 使い所

あきらかに特性が異なる Protocol を追加する場合に使う認識です。
なので、今回のような泳げる機能を追加するだけではケースでは使わない認識です。

例えば、今は鳥だけを扱っていますが、哺乳類などを扱う種類が増えていくと、どんどん Protocol を増やしていくことになってしまったりして、困ります。

## 拡張方法その3: 既存の Protocol を extension する

### Protocol の定義

```swift
protocol SwimAble {}

extension BirdProtocol where Self: SwimAble {
    // Protocol を extension する場合は {} の中身まで書かなければならない
    func swim() {
        fatalError("override required")
    }
}
```

### Class の定義

```swift
class KamoC: BirdProtocol, SwimAble {
    func breathe() {}
    
    func fly() {}
    
    func swim() {
        print("swim") // overrideする
    }
}
```

### 実行してみる

型指定しない場合のみしか、`KamoC` での override した `swim（）` しか使えず、`BirdProtocol & SwimAble` と型指定した場合は `KamoC` での override した `swim（）` は無視されるという謎の挙動になります。

```swift
// 例1
let birdC1 = KamoC()
birdC1.swim() // "swim"

// 例2
let birdC2: BirdProtocol = KamoC()
// birdC2.swim() // コンパイルエラー

// 例3
let birdC3: BirdProtocol & SwimAble = KamoC()
birdC3.swim() // 実行時エラー （ Fatal error: override required ） ← overrideできていない理由は不明
```

共通で `swim（）` 処理を使うのであれば `extension BirdProtocol where Self: SwimAble` 時に実態の `swim（）` 処理を書けば問題ないが、Class 側で個別に実装したい時は、このように思わぬ事故が発生しそう。。。

### 使い所

個別の Class では実装させないような場合に用いるイメージです。（以下の機能制限のような例。）

# 【応用】 機能制限

例：ダチョウやペンギンのように飛べない鳥がいるので、それを `BirdProtocol` を extension させて表現します。

ポイントは `where Self: FlyUnable` とすることで、他の `BirdProtocol` を継承した Class には影響させないというところです。

```swift
protocol FlyUnable {}

extension BirdProtocol where Self: FlyUnable {
    func fly() {
        fatalError("Can't fly.")
    }
}

class DachoA: BirdProtocol, FlyUnable {
    func breathe() {}
}

let birdA = DachoA()
birdA.breathe()
birdA.fly() // 実行時エラーが起こる
```

下記のように `BirdProtocol` を継承させた `FlyUnableBird` を定義することでも表現可能です。

```swift
protocol FlyUnableBird: BirdProtocol {}

extension BirdProtocol where Self: FlyUnableBird {
    func fly() {
        fatalError("Can't fly.")
    }
}

class DachoB: FlyUnableBird {
    func breathe() {}
}

let birdB = DachoB()
birdB.breathe()
birdB.fly() // 実行時エラーが起こる
```

# 最後に

なるべく細かく Protocol で機能を分割していくのが Swift っぽいのかもしれません。