デリゲートパターン

デリゲート パターンは、主要なデザイン パターンの 1 つです。
理髪店アプリケーションを開発しているとします。アプリケーションには記録する日を選択するためのカレンダーがあり、日付をタップすると床屋のリストが開き、選択肢が表示されます。
システム コンポーネントの単純なリンクを実装し、相互へのポインタを使用してカレンダーと画面を結合し、リスト表示を実装しましょう。


// псевдокод

class BarbershopScreen {
   let calendar: Calendar

   func showBarbersList(date: Date) {
      showSelectionSheet(barbers(forDate: date))
   }
}

class Calendar {
    let screen: BarbershopScreen

    func handleTap(on date: Date) {
        screen.showBarbersList(date: date)
    }
}

数日後、要件が変更されます。リストを表示する前に、サービス (ひげのトリミングなど) を選択したオファーを表示する必要があります。ただし、土曜日を除くすべての日に表示される必要はありません。
カレンダーに土曜日かどうかのチェックを追加し、それに応じて理容師のリストまたはサービスのリストのメソッドを呼び出します。わかりやすくするために、次に示します。


// псевдокод

class BarbershopScreen {
   let calendar: Calendar

   func showBarbersList(date: Date) {
      showSelectionSheet(barbers(forDate: date))
   }

   func showOffersList() {
      showSelectionSheet(offers)
   }
}

class Calendar {
    let screen: BarbershopScreen

    func handleTap(on date: Date)  {
        if date.day != .saturday {
             screen.showOffersList()
        }
        else {
             screen.showBarbersList(date: date)
        }
    }
}

1 週間後、フィードバック画面にカレンダーを追加するように求められました。その瞬間、最初のアーキテクチャ上の問題が発生しました。
何をするか?カレンダーはカットの予約画面としっかりと連動
しています。おお!うーん!ああああ
このクレイジーなアプリケーション アーキテクチャで作業を続ける場合は、カレンダー クラス全体のコピーを作成し、このコピーをフィードバック画面に関連付ける必要があります。
よし、良さそうだ。さらに画面をいくつか追加し、カレンダーのコピーをいくつか追加したところ、X の瞬間が来た。カレンダーのデザインを変更するように求められました。つまり、カレンダーのすべてのコピーを見つけて、すべてに同じ変更を追加する必要があります。この「アプローチ」は開発のスピードに大きく影響し、間違いを犯す可能性も高まります。その結果、そのようなプロジェクトは最終的に壊れた状態になり、元のアーキテクチャの作成者ですらクラスのコピーがどのように機能するかを理解できなくなり、途中で追加された他のハックがその場で崩壊してしまいます。
何をしなければならなかったのか、あるいはもっと良く言えば、何を始めるのに遅すぎなかったのか?委任パターンを使用してください。
委任は、共通のインターフェイスを通じてクラス イベントを渡す方法です。以下は、カレンダーのデリゲートの例です。

protocol CalendarDelegate {
   func calendar(_ calendar: Calendar, didSelect date: Date)
}

次に、デリゲートを操作するためのコードをサンプル コードに追加しましょう。


// псевдокод

class BarbershopScreen: CalendarDelegate {
   let calendar: Calendar

   init() {
       calendar.delegate = self
   }

   func calendar(_ calendar: Calendar, didSelect date: Date) {
        if date.day != .saturday {
            showOffersList()
        }
        else {
             showBarbersList(date: date)
        }
   }

   func showBarbersList(date: Date) {
      showSelectionSheet(barbers(forDate: date))
   }

   func showOffersList() {
      showSelectionSheet(offers)
   }
}

class Calendar {
    weak var delegate: CalendarDelegate

    func handleTap(on date: Date)  {
        delegate?.calendar(self, didSelect: date)
    }
}

その結果、カレンダーから日付を選択するときにカレンダーを画面から完全に解放し、日付選択イベント – を送信します。イベント処理をサブスクライバに *委任*します。加入者は画面
です。このアプローチからどのようなメリットが得られるのでしょうか?クラスを複製することなく、カレンダーと画面のロジックを互いに独立して変更できるようになり、さらなるサポートが簡素化されます。このようにして、システムコンポーネントの実装に対する「単独責任の原則」が実装され、DRY 原則が遵守されます。
委任を使用すると、ウィンドウを表示するためのロジックや画面上の順序を追加したり変更したりできます。これは、客観的には直接関係のないプロセスに参加すべきではないカレンダーや他のクラスにはまったく影響しません。< br />あるいは、あまり気にしないプログラマは、別個のプロトコル/デリゲート インターフェイスを作成せずに、共通バス経由でメッセージを送信することを使用します。この場合、デリゲーションを使用する方が良いでしょう。このアプローチの欠点については、以前の投稿で書きました。 「オブザーバーパターン」

ソース

https://refactoring.guru/ru/replace-inheritance -委任付き