概要
今回は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
をオブジェクト指向
ならぬプロトコル指向
として捉える考えもあるようでプロトコルは重要なのではと感じています.
自分なり砕けて説明してみたのでもしここは違うよというところがあればアドバイスください。
最後まで読んでいただきありがとうございました!