LoginSignup
9
14

【Swift】この世で一番わかりやすいデリゲートのお話

Last updated at Posted at 2021-04-26

#この記事は何?
iOSアプリ開発を学び始めると、大抵の初学者はデリゲートがなかなか理解できずに苦しみます。私もその1人でした。しかし、それも今となっては昔の話。いつの間にか「そんなものなんだな」と理解したつもりになって、適当に使っていました。
ここらで一旦、「誰にでもわかるように説明できるのか」をテーマに解説してみたいと思いました。

Swiftを基礎から学ぶには
自著、工学社より発売中の「まるごと分かるSwiftプログラミング」をお勧めします。変数、関数、フロー制御構文、データ構造はもちろん、構造体からクロージャ、エクステンション、プロトコル、クロージャまでを基礎からわかりやすく解説しています。

##実行環境
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"

someOneanotherOneは、同じPersonクラスのインスタンスなのに、greeting()メソッドの挙動が異なります。
それぞれのインスタンスは、デリゲート先が別の言語クラスになっているからですね。
これも、デリゲート・パターンの利点です。

##説明は以上です

いかがだったでしょうか?
長くなってしまいましたが、iOSやビューコントローラの概念と切り離して、できるだけシンプルにデリゲート・パターンを説明してきました。
このように説明することで、自分自身の理解も整理することができました。
感想なども、ぜひコメントしていただけると嬉しいです。

9
14
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
9
14