UIKit
を覚え始めた時、意味がまるで分からないまま放置していた Delegate
。
今更ながらその正体が何なのかまとめてみた。
●delegateとは
デザインパターンの一つ。
要は「作戦の名前」
つまり「Delegate」じゃなくて
「長距離索敵陣形」とか「ウォールマリア奪還作戦」でも良いわけだ。
オブジェクト指向型言語は、クラスを定義し、そのクラスを実体化した
オブジェクト同士を相互作用させて動作させる
どのように設計したら 再利用性の高いクラスを作れるか?
⇨ 「デザインパターンを使う」
delegate
は、英語で人に任せる
という意味を持っており
「あるクラスから他のクラスに処理を任せる」というデザインパターン。
処理の流れは決まっているけど条件によって処理内容を変えたい、
ということが簡単に実現できる
●delegteを使った実装方法
delegate
には、3つの役割が必要になる
1.プロトコル(ざっくりとした依頼書)
💡 任せる処理をメソッドとして定義。デリゲートメソッド
と呼ばれる。
2.処理を任せるクラス(依頼人)
💡 デリゲートメソッド
を使用し、処理の流れを記述する。
3.処理を任されるクラス(代理人)
💡 プロトコルで定義されたデリゲートメソッド
を実装する。
■プロトコル(依頼書)の実装
このプロトコルでは
「オーディオを再生する」「プレイリストをシャッフルする」
というメソッドを定義する。処理はない。
protocol ButtonDelegate {
func play()
func enableShuffle()
}
■処理を任せる側(依頼人)の実装
delegate
プロパティを持っておく(代理人を受け付ける)
push
メソッドでデリゲートメソッド
を実行する(処理内容は問わない)
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
でなければデリゲートメソッドを実行する
■処理を任される側(代理人)の実装
デリゲートメソッド
を実装する
class Button1: ButtonDelegate {
func play() {
print("Played Audio1")
}
func enableShuffle() {
print("Shuffle Enabled")
}
}
■実行
AudioButton
クラス(依頼人)に、
どのクラス(代理人)に処理を任せるかを宣言する。
let audioButton = AudioButton()
let button1 = Button1()
audioButton.delegate = button1
audioButton.push()
audioButton.delegate = button1
でButton1
クラスの
インスタンスに処理を任せるので、実行結果は以下となる
// Button Didn't Pushed
// Shuffle Enabled
// Played Audio1
コード全文
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
にしておけば、
必要な時に必要なデリゲートメソッドのみを呼ぶことができる
// 依頼書
@objc protocol ButtonDelegate: AnyObject {
@objc optional func play()
@objc optional func stop()
@objc optional func enableShuffle()
@objc optional func disableShuffle()
}
依頼人AudioButton
に分岐を追加する
// 依頼人
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")
}
}
}
新たなデリゲートメソッドを実装する
// 代理人
class Button2: ButtonDelegate {
func stop() {
print("Stoped Audio2")
}
func enableShuffle() {
print("Shuffle Enabled")
}
}
新しく作ったデリゲートメソッドに処理を任せるようにする
let audioButton = AudioButton()
let button2 = Button2()
audioButton.delegate = button2
audioButton.push()
実行結果
// Button Pushed
// Shuffle Disabled
// Stoped Audio2
コード全文
// 依頼書
@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)の使い方を理解しよう! | 侍エンジニアブログ
【swift初心者〜中級者向け】なぜ・いつ delegate を使うのか - Qiita