この記事は何?
iOSアプリ開発を学び始めると、大抵の初学者はデリゲートがなかなか理解できずに苦しみます。私もその1人でした。しかし、それも今となっては昔の話。いつの間にか「そんなものなんだな」と理解したつもりになって、適当に使っていました。
ここらで一旦、「誰にでもわかるように説明できるのか」をテーマに解説してみたいと思いました。
Swiftを基礎から学ぶには
自著、工学社より発売中の「まるごと分かるSwiftプログラミング」をお勧めします。変数、関数、フロー制御構文、データ構造はもちろん、構造体からクロージャ、エクステンション、プロトコル、クロージャまでを基礎からわかりやすく解説しています。
また、Swiftプログラミングを基礎から動画で学びたい方には、Udemyコース「今日からはじめるプログラミング」をお勧めします。
実行環境
Swift 5.3
Xcode 12.4
macOS 11.2
はじめに
デリゲートを理解するためには、プロトコルを知っておく必要があります。
必要最低限のプロトコルについて、こちらで解説しています。
#ハンズオン
実際にコードを書きながら、説明していきます。
まずは、次のようなプロトコルを定義しておきます。
protocol LanguageDelegate {
func hello()
}
このLanguageDelegate
プロトコルは、hello()
メソッドの実装を要件としています。
つまり、このプロトコルへの適合は、「その言語を知っているなら、挨拶ができる」と保証することを意味します。
実際に、そのようなクラスを定義してみましょう。
class Japanese: LanguageDelegate {
func hello() {
print("やあ")
}
}
let japanese = Japanese()
これは、日本語をモデル化するクラスです。
hello()
メソッドを実装しているので、LanguageDelegate
プロトコルに適合しています。
インスタンスも作成しておきます。
英語やイタリア語のクラスも、同じように定義してみましょう。
class English: LanguageDelegate {
func hello() {
print("Hello")
}
}
let english = English()
class Italiano: LanguageDelegate {
func hello() {
print("Ciao")
}
}
let italiano = Italiano()
ここまではプロトコルのお話なので、難しいことはないですね。
ここからがデリゲートの出番です。
それでは、「挨拶できる人間」をクラスとして定義してみましょう。
class Person {
func greeting() {
// say something...
}
}
let someOne = Person()
このクラスをインスタンス化した「誰かさんsomeOne
」は、何語でどんな挨拶ができるでしょうか?
その方法をこの「誰かさん」が決めるのではなく、他のクラスに「おまかせ」するのがデリゲートです。
本来、デリゲート(Delegate)とは「移譲する」という意味です。
Person
クラスの定義に、以下の2行を追記しましょう。
class Person {
var delegate: LanguageDelegate? // おまかせ先
func greeting() {
delegate?.hello() // おまかせ先の言語で挨拶
}
}
let someOne = Person()
ここでは、変数delegate
の型が: LanguageDelegate
と明示されていることに注目します。最初に定義したプロトコルを型として利用していますね。つまり、この変数プロパティには「LanguageDelegate
に適合した型」ならなんでも割り当てることができます。
日本語を示すインスタンスを設定してみましょう。
someOne.delegate = japanese
someOne.greeting() // prints "やぁ"
「誰かさん」は日本語が喋れるようになったので、挨拶も「やぁ」と出力されます。
英語やイタリア語を喋れるようにするには、インスタンスのdelegate
プロパティにそれぞれの言語インスタンスを設定します。
someOne.delegate = english
someOne.greeting() // prints "Hello"
someOne.delegate = italiano
someOne.greeting() // prints "Ciao"
これがいわゆる「デリゲート・パターン」です。
someOne.greeting()
というコードが3回、実行されていますが出力結果はどれも異なる挨拶です。
デリゲート先に実装を移譲しているからですね。
##何が嬉しいのか
デリゲート・パターンの利点は、ここからです。
あなたが王様になって、新しい国を作ったとしましょう。もちろん、使用言語も独自のものです。
以下のように、クラスとして定義しましょう。
class MyKingdom: LanguageDelegate {
func hello() {
print("🤟")
}
}
let myKingdom = MyKingdom()
とてもファンキーな挨拶ですね。飛沫も飛ばないので安全です。
「誰かさん」も、この国の言語を喋れるようにデリゲート先を設定しましょう。
someOne.delegate = myKingdom
someOne.greeting() // prints "🤟"
Person
クラスの実装に触れることなく、「誰かさん」の挙動を変更できました。
デリゲート・パターンのメリットは、「実装方法を定義するときではなく、後で決定できる」ことです。
さらに、「別の誰か」を作成しましょう。
この「別の誰か」は、イタリア語を喋ります。
let anoterOne = Person()
anotherOne.delegate = italiano
anotherOne.greeting() // prints "Ciao"
someOne
とanotherOne
は、同じPerson
クラスのインスタンスなのに、greeting()
メソッドの挙動が異なります。
それぞれのインスタンスは、デリゲート先が別の言語クラスになっているからですね。
これも、デリゲート・パターンの利点です。
説明は以上です
いかがだったでしょうか?
長くなってしまいましたが、iOSやビューコントローラの概念と切り離して、できるだけシンプルにデリゲート・パターンを説明してきました。
このように説明することで、自分自身の理解も整理することができました。
感想なども、ぜひコメントしていただけると嬉しいです。