概要
今回はSwiftでdelegateを使ってみようということで記事を書かせていただきます.
環境
Swift 4.2
Amazonと551と私
デリゲートとはなんぞやの前に、まずあるストーリーを想像してみてください.
あなたは今 とある人間Aくんです.
Aは今 とてもつもなく551が食べたいです.
美味しそうですね...
しかし!!!
Aは今東京に住んでいます...
本場の肉まん551を食べるには大阪まで行かないといけません. (ほなイコカ)
そこで
Amazonに配達をお願いします
Amazonに頼めば仮に東京にいても美味しい551が食べられるのです.
つまり、自分がやりたくない処理を他の誰かにお願いし任せること(移譲)がdelegateの役割です.
では、delegateの解説を始めていきたいと思います.
delegateについて
ではdelegateとはなんぞやということで.
あるクラスから処理の一部を他のクラスに任せたり、他のクラスへメッセージを送る等の目的でよく使われるデザインパターン
引用:http://nukenuke.hatenablog.com/entry/2015/09/17/120749
とのことです.つまり処理をお願いすることです. 難しい言葉で言うと移譲です.残念ながら僕は最初その言葉を聞いてあまりピンと来ませんでした.
なんにせよ具体的な使い方を覚えたほうが早いと思うので先ほどの話を用いてコードを書いていきましょう
ButamanPersonInTokyoをA君として、Amazonクラスを用いて考えていきましょう.
ButamanPersonInTokyoでAmazonにgogoichiという変数をdelegateを用いて送ってみる(PlayGround)
STEP0.GogoichiクラスとGogoichiKindを実装
本題とは関係ないのでPlayGroundにコピペでOKです.
enum GogoichiKind: String {
case Butaman = "豚まん"
case Dango = "団子"
case Gyoza = "餃子"
}
class Gogoichi {
var price: Int {
switch kind {
case .Butaman:
return 170
case .Dango:
return 30
case .Gyoza:
return 30
}
}
var kind: GogoichiKind = .Butaman
}
STEP1. protocolの宣言
protocol SendGogoichi {
func send(gogoichi: Any) -> Void
}
ポイント. 呼ばれるメソッドはprotocolの中で定義のみしないといけない. (処理は書かないと言う意味です)
STEP2. ButamanPersonInTokyoの宣言. delegateの宣言
class ButamanPersonInTokyo {
var delegate: SendGogoichi?
}
ポイント. var delegate: SendGogoichi?を処理をお願いする側で宣言しておく
STEP3. Amazonの宣言. protocolを継承. 同時にプロトコルのメソッドを宣言.
class Amazon: SendGogoichi {
func send(gogoichi: Gogoichi) {
}
}
ポイント. SendGogoichiプロトコルを処理を任された側で継承しておく
delegateを設定する.
delegateを設定するとはどのクラスが処理をお願いしてどのクラスが処理を任されるのかを決めることです。
お願いされる側はprotocolを継承している方でした.Amazonの方です.
お願いをする側はdelegateを宣言している方でした.ButamanPersonInTokyoの方です.
delegateの宣言の仕方は
var delegate: SendGogoichi?
でした. このSendGogoichiはプロトコルである必要があります.
宣言したdelegateにお願い先を代入
delegate = Amazon()
これを書いてあげればいいのですが, delegateは代入してあげないと初期値がnilなのでOptional Chainingした時にメソッドが呼ばれません.
delegate: SendGogoichi? //nilが入っている
delegate?.send(gogoichi: gogoichi) //sendメソッドはdelegateがnilなので呼ばれない.
なので
delegate = Amazon()
delegate?.send(gogoichi: gogoichi)
のようにdelegate?.send(gogoichi: gogoichi)よりも前にdelegateに値をいれてあげる必要があります.
ButamanPersonInTokyoがAmazonにgogoichiをお願いするメソッドを書く.
func orderButaman() {
let gogoichi: Gogoichi = Gogoichi()
gogoichi.kind = .Butaman
delegate?.send(gogoichi: gogoichi) // ここで処理をお願いしている
}
このorderButamanメソッドでdelegate?.sendが呼ばれるという形になっています.
任される側のクラス(Amazon)で送信する処理を実装する.
func send(gogoichi: Gogoichi) {
for _ in 0...2 {
sleep(1)
print("配達中...")
}
print("\(gogoichi.kind)の送信を完了しました.値段は\(gogoichi.price)円です.")
}
sleep(1)は配達には時間がかかることを想定して処理に時間をかけてます.
インスタンス化
let butamanPerson = ButamanPersonInTokyo()
butamanPerson.delegate = Amazon()
butamanPerson.orderButaman()
全体のコード
import UIKit
import Foundation
enum GogoichiKind: String {
case Butaman = "豚まん"
case Dango = "団子"
case Gyoza = "餃子"
}
class Gogoichi {
var price: Int {
switch kind {
case .Butaman:
return 170
case .Dango:
return 30
case .Gyoza:
return 30
}
}
var kind: GogoichiKind = .Butaman
}
protocol SendGogoichi {
func send(gogoichi: Gogoichi) -> Void
}
class Amazon: SendGogoichi {
func send(gogoichi: Gogoichi) {
//
for _ in 0...2 {
sleep(1)
print("配達中...")
}
print("\(gogoichi.kind)の送信を完了しました.値段は\(gogoichi.price)円です.")
}
}
class ButamanPersonInTokyo {
var delegate: SendGogoichi?
func orderButaman() {
let gogoichi: Gogoichi = Gogoichi()
gogoichi.kind = .Butaman
delegate?.send(gogoichi: gogoichi)
}
}
let butamanPerson = ButamanPersonInTokyo()
butamanPerson.delegate = Amazon()
butamanPerson.orderButaman()
コンソール
なぜデリゲートを使うのか
薄々感づいた人もいると思いますが, これdelegateなくても書けます.
delegateを使うメリットの1つとして処理を任せた相手がどんなクラスであるのかを考えなくていいという点があります.
今回はdelegateのメリットはあまりありませんでしたが、もし**Amazonではなく大阪の友達がgogoichiを送ってくれたらどうでしょう**
この実装の変更の流れをみていきます。
もしdelegateを用いていなかったら
class ButamanPersonInTokyo {
func orderButaman() {
let gogoichi: Gogoichi = Gogoichi()
gogoichi.kind = .Butaman
Amazon().send(gogoichi: gogoichi) // ここ
}
}
こうやって直接読んであげることになります.
これを大阪の友達に頼むと
class ButamanPersonInTokyo {
func orderButaman() {
let gogoichi: Gogoichi = Gogoichi()
gogoichi.kind = .Butaman
大阪の友達().send(gogoichi: gogoichi) // ここ
}
}
こうなりますよね
しかし
delegateを用いると
ButamanPersonInTokyoはコードを変更する必要がありません.
class ButamanPersonInTokyo {
var delegate: SendGogoichi?
func orderButaman() {
let gogoichi: Gogoichi = Gogoichi()
gogoichi.kind = .Butaman
delegate?.send(gogoichi: gogoichi)
}
}
このままで大丈夫です.
インスタンス化した時にdelegateに入れる相手を変更すればいいんです
class OsakaFriend: SendGogoichi {
func send(gogoichi: Gogoichi) {
for _ in 0...2 {
sleep(1)
print("配達中...")
}
print("\(gogoichi.kind)の送信を完了しました.値段は友達なので無料だよ.")
}
}
let butamanPerson = ButamanPersonInTokyo()
butamanPerson.delegate = OsakaFriend()
コンソール
まとめ
適当な話の下りでしたがdelegateは理解できたでしょうか/
delegateを使うことで得られる
classのように再利用できる
お願いする先を気にしなくて良い
の2つの利点について考えてみました。
classのように再利用できる点についてはSwiftをオブジェクト指向ならぬプロトコル指向として捉える考えもあるようでプロトコルは重要なのではと感じています.
自分なり砕けて説明してみたのでもしここは違うよというところがあればアドバイスください。
最後まで読んでいただきありがとうございました!


