【Swift】今更ながらDelegate Patternについてまとめてみた

`UIKit`を覚え始めた時、意味がまるで分からないまま放置していた **`Delegate`**。
今更ながらその正体が何なのかまとめてみた。

# ●delegateとは

**デザインパターン**の一つ。

要は「**作戦の名前**」

つまり「**Delegate**」じゃなくて
 「**長距離索敵陣形**」とか「**ウォールマリア奪還作戦**」でも良いわけだ。

オブジェクト指向型言語は、クラスを定義し、そのクラスを実体化した
オブジェクト同士を相互作用させて動作させる
どのように設計したら　再利用性の高いクラスを作れるか？

⇨ **「デザインパターンを使う」**

`delegate` は、英語で`人に任せる` という意味を持っており
「**あるクラスから他のクラスに処理を任せる**」というデザインパターン。
処理の流れは決まっているけど条件によって処理内容を変えたい、
ということが簡単に実現できる

# ●delegteを使った実装方法

`delegate`には、**3つ**の役割が必要になる

1.**プロトコル（ざっくりとした依頼書）**
💡 任せる処理をメソッドとして定義。`デリゲートメソッド`と呼ばれる。

2.**処理を任せるクラス（依頼人）**
💡  `デリゲートメソッド`を使用し、処理の流れを記述する。

3.**処理を任されるクラス（代理人）**
💡 プロトコルで定義された`デリゲートメソッド`を実装する。![スクリーンショット 2023-04-01 9.53.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2633746/5d58f48b-bb22-d157-ad9b-bd5b515c18ad.png)


## ■プロトコル（依頼書）の実装

このプロトコルでは
「オーディオを再生する」「プレイリストをシャッフルする」
というメソッドを定義する。処理はない。

```swift
protocol ButtonDelegate {
	func play()
	func enableShuffle()
}
```

## ■処理を任せる側（依頼人）の実装

`delegate` プロパティを持っておく（代理人を受け付ける）
`push` メソッドで`デリゲートメソッド`を実行する（処理内容は問わない）

```swift
class AudioButton {
	var delegate: ButtonDelegate? = nil
	
	func push() {
		print("Button Pushed")
		
		if let dg = self.delegate {
			dg.enableShuffle()
			dg.play()
		}	else {
			print("Didn't Work")
		}
	}
}
```

変数`delegate` には、実行する時にプロトコルを実装したクラスのインスタンスを設定
`push` メソッドでは`delegate`の`nil`チェックを行い
`nil` でなければ**デリゲートメソッド**を実行する

## ■処理を任される側（代理人）の実装

`デリゲートメソッド`を実装する

```swift
class Button1: ButtonDelegate {
	func play() {
		print("Played Audio1")
	}
	func enableShuffle() {
		print("Shuffle Enabled")
	}
}
```

## ■実行

`AudioButton` クラス（依頼人）に、
どのクラス（代理人）に処理を任せるかを宣言する。

```swift
let audioButton = AudioButton()
let button1 = Button1()

audioButton.delegate = button1
audioButton.push()
```

`audioButton.delegate = button1` で`Button1`クラスの
インスタンスに処理を任せるので、実行結果は以下となる

```swift
// Button Didn't Pushed
// Shuffle Enabled
// Played Audio1
```

コード全文

```swift
protocol ButtonDelegate {
	func play()
	func enableShuffle()
}

// 依頼人
class AudioButton {
	var delegate: ButtonDelegate? = nil
	
	func push() {
		print("Button Pushed")
		
		if let dg = self.delegate {
			dg.enableShuffle()
			dg.play()
		}	else {
			print("Button Didn't Pushed")
		}
	}
}

// 代理人
class Button1: ButtonDelegate {
	func play() {
		print("Played Audio1")
	}
	func enableShuffle() {
		print("Shuffle Enabled")
	}
}

let audioButton = AudioButton()
let button1 = Button1()

audioButton.delegate = button1
audioButton.push()

// -------------------------------------------
// Button Didn't Pushed
// Shuffle Enabled
// Played Audio1
// -------------------------------------------
```

# ●delegateを使うメリット

`delegate`を使うメリットは、処理を任せる側は実際に処理をするのは
**どのクラスなのか、どんな処理をするのか、ということを意識しなくていい点。**

上の例で、ボタンを押したときに違う処理がしたいのであれば、
**処理を任されるクラスだけ**を新しく作ればOK。

**デリゲートメソッド**を`optional` にしておけば、
必要な時に必要な**デリゲートメソッド**のみを呼ぶことができる

```swift
// 依頼書
@objc protocol ButtonDelegate: AnyObject {
    @objc optional func play()
    @objc optional func stop()
    @objc optional func enableShuffle()
    @objc optional func disableShuffle()
}
```

依頼人`AudioButton`に分岐を追加する

```swift
// 依頼人
class AudioButton {
    var delegate: ButtonDelegate? = nil
    
    func push() {
        print("Button Pushed")
        
        if let dg = self.delegate {
            if dg is Button1 {
                dg.enableShuffle?()
                dg.play?()
            } else if dg is Button2  {
                dg.disableShuffle?()
                dg.stop?()
            }
        }    else {
            print("Button Didn't Pushed")
        }
    }
}
```

新たな**デリゲートメソッド**を実装する

```swift
// 代理人
class Button2: ButtonDelegate {
	func stop() {
		print("Stoped Audio2")
	}
	func enableShuffle() {
		print("Shuffle Enabled")
	}
}
```

新しく作った**デリゲートメソッド**に処理を任せるようにする

```swift
let audioButton = AudioButton()
let button2 = Button2()

audioButton.delegate = button2
audioButton.push()
```

実行結果

```swift
// Button Pushed
// Shuffle Disabled
// Stoped Audio2
```

 コード全文

```swift
// 依頼書
@objc protocol ButtonDelegate: AnyObject {
    @objc optional func play()
    @objc optional func stop()
    @objc optional func enableShuffle()
    @objc optional func disableShuffle()
}

// 依頼人
class AudioButton {
    var delegate: ButtonDelegate? = nil
    
    func push() {
        print("Button Pushed")
        
        if let dg = self.delegate {
            if dg is Button1 {
                dg.enableShuffle?()
                dg.play?()
            } else if dg is Button2  {
                dg.disableShuffle?()
                dg.stop?()
            }
        }    else {
            print("Button Didn't Pushed")
        }
    }
}

// 代理人1
class Button1: ButtonDelegate {
    func play() {
        print("Played Audio1")
    }
    func enableShuffle() {
        print("Shuffle Enabled")
    }
}

// 代理人2
class Button2: ButtonDelegate {
    func stop() {
        print("Stoped Audio1")
    }
    func disableShuffle() {
        print("Shuffle Disabled")
    }
}

let audioButton = AudioButton()

let button1 = Button1()
audioButton.delegate = button1
audioButton.push()

let button2 = Button2()
audioButton.delegate = button2
audioButton.push()
```

**プロトコルや処理を任せるクラスはそのまま再利用し、全く異なる処理を行うことができる！**

また、それぞれに分割して責任を持たせることができるので
テストが書きやすくなりそう

※参考文献

[【Swift入門】難解なデリゲート(delegate)の使い方を理解しよう！ | 侍エンジニアブログ](https://www.sejuku.net/blog/33867)

[【swift初心者〜中級者向け】なぜ・いつ delegate を使うのか - Qiita](https://qiita.com/s_higeru/items/d156d2dbc725b88c220e)

[【入門】iOS delegateを図を用いてわかりやすく使い方を解説](https://mo-gu-mo-gu.com/ios-delegate/)

[[Swift] 意味不明の代表格、Delegateをマスターしよう！](https://ryu-techblog.com/columns/swift-delegate)