5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

Posted at

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

●delegateとは

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

要は「作戦の名前

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

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

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

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

●delegteを使った実装方法

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

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

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

3.処理を任されるクラス(代理人)
💡 プロトコルで定義されたデリゲートメソッドを実装する。スクリーンショット 2023-04-01 9.53.41.png

■プロトコル(依頼書)の実装

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

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 メソッドではdelegatenilチェックを行い
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 = button1Button1クラスの
インスタンスに処理を任せるので、実行結果は以下となる

// 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

【入門】iOS delegateを図を用いてわかりやすく使い方を解説

[Swift] 意味不明の代表格、Delegateをマスターしよう!

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?