パターンメディエーター

メディエーター パターンは、動作デザイン パターンを指します。

ある日、ジョーク アプリケーションの開発を依頼されました。ユーザーが画面中央のボタンを押すと、アヒルが鳴く面白い音が聞こえます。
アプリストアにアップロードすると、アプリケーションはヒットします。誰もがあなたのアプリケーションを大騒ぎし、イーロン・マスクは火星の超高速トンネルの次回の打ち上げで自身のインスタグラムで大騒ぎし、ヒラリー・クリントンは討論会でドナルド・トランプを圧倒します。そしてウクライナの選挙に勝利し、成功しましょう!
アプリケーションの単純な実装は次のようになります。

class DuckButton {
    func didPress() {
        print("quack!")
    }
}

let duckButton = DuckButton()
duckButton.didPress()

次に、犬の鳴き声を追加することにします。そのためには、音を選択するための 2 つのボタンを表示する必要があります –アヒルと犬と一緒に。 DuckButton と DogButton という 2 つのボタン クラスを作成しましょう。
コードを変更します:

class DuckButton {
    func didPress() {
        print("quack!")
    }
}

class DogButton {
    func didPress() {
        print("bark!")
    }
}

let duckButton = DuckButton()
duckButton.didPress()

let dogButton = DogButton()
dogButton.didPress()

さらに成功したら、豚の鳴き声を追加します。ボタンには 3 つのクラスがあります。

class DuckButton {
    func didPress() {
        print("quack!")
    }
}

class DogButton {
    func didPress() {
        print("bark!")
    }
}

class PigButton {
    func didPress() {
        print("oink!")
    }
}

let duckButton = DuckButton()
duckButton.didPress()

let dogButton = DogButton()
dogButton.didPress()

let pigButton = PigButton()
pigButton.didPress()

ユーザーからは音が重なっているとの苦情が寄せられています。
これが起こらないようにチェックを追加し、同時にクラスを相互に導入します。

class DuckButton {
    var isMakingSound = false
    var dogButton: DogButton?
    var pigButton: PigButton?
    func didPress() {
        guard dogButton?.isMakingSound ?? false == false &&
                pigButton?.isMakingSound ?? false == false else { return }
        isMakingSound = true
        print("quack!")
        isMakingSound = false
    }
}

class DogButton {
    var isMakingSound = false
    var duckButton: DuckButton?
    var pigButton: PigButton?
    func didPress() {
        guard duckButton?.isMakingSound ?? false == false &&
                pigButton?.isMakingSound ?? false == false else { return }
        isMakingSound = true
        print("bark!")
        isMakingSound = false
    }
}

class PigButton {
    var isMakingSound = false
    var duckButton: DuckButton?
    var dogButton: DogButton?
    func didPress() {
        guard duckButton?.isMakingSound ?? false == false && 
                dogButton?.isMakingSound ?? false == false else { return }
        isMakingSound = true
        print("oink!")
        isMakingSound = false
    }
}

let duckButton = DuckButton()
duckButton.didPress()

let dogButton = DogButton()
dogButton.didPress()

let pigButton = PigButton()
pigButton.didPress()

あなたの申請が成功したことを受けて、政府はモバイル デバイスでの鳴き声、吠え声、うなり声を平日の午前 9 時から 15 時までのみ許可するという法律を制定することを決定しました。その場合、あなたのアプリケーションのユーザーは、個人用電子機器を使用したわいせつな音の生成により、5 年間の懲役刑に処される危険があります。
コードを変更します:

import Foundation

extension Date {
    func mobileDeviceAllowedSoundTime() -> Bool {
        let hour = Calendar.current.component(.hour, from: self)
        let weekend = Calendar.current.isDateInWeekend(self)
        
        let result = hour >= 9 && hour <= 14 && weekend == false
        
        return result
    }
}

class DuckButton {
    var isMakingSound = false
    var dogButton: DogButton?
    var pigButton: PigButton?
    func didPress() {
        guard dogButton?.isMakingSound ?? false == false &&
                pigButton?.isMakingSound ?? false == false &&
                 Date().mobileDeviceAllowedSoundTime() == true else { return }
        isMakingSound = true
        print("quack!")
        isMakingSound = false
    }
}

class DogButton {
    var isMakingSound = false
    var duckButton: DuckButton?
    var pigButton: PigButton?
    func didPress() {
        guard duckButton?.isMakingSound ?? false == false &&
                pigButton?.isMakingSound ?? false == false &&
                 Date().mobileDeviceAllowedSoundTime() == true else { return }
        isMakingSound = true
        print("bark!")
        isMakingSound = false
    }
}

class PigButton {
    var isMakingSound = false
    var duckButton: DuckButton?
    var dogButton: DogButton?
    func didPress() {
        guard duckButton?.isMakingSound ?? false == false && 
                dogButton?.isMakingSound ?? false == false &&
                 Date().mobileDeviceAllowedSoundTime() == true else { return }
        isMakingSound = true
        print("oink!")
        isMakingSound = false
    }
}

let duckButton = DuckButton()
let dogButton = DogButton()
let pigButton = PigButton()

duckButton.dogButton = dogButton
duckButton.pigButton = pigButton

dogButton.duckButton = duckButton
dogButton.pigButton = pigButton

pigButton.duckButton = duckButton
pigButton.dogButton = dogButton

duckButton.didPress()
dogButton.didPress()
pigButton.didPress()

突然、懐中電灯アプリケーションが私たちのアプリケーションを市場から追い出し始めます。それに負けないように、「oink-oink」ボタンと残りのボタンを押して懐中電灯を追加しましょう。

import Foundation

extension Date {
    func mobileDeviceAllowedSoundTime() -> Bool {
        let hour = Calendar.current.component(.hour, from: self)
        let weekend = Calendar.current.isDateInWeekend(self)
        
        let result = hour >= 9 && hour <= 14 && weekend == false
        
        return result
    }
}

class Flashlight {

    var isOn = false

    func turn(on: Bool) {
        isOn = on
    }
}

class DuckButton {
    var isMakingSound = false
    var dogButton: DogButton?
    var pigButton: PigButton?
    var flashlight: Flashlight?
    func didPress() {
        flashlight?.turn(on: true)
        guard dogButton?.isMakingSound ?? false == false &&
                pigButton?.isMakingSound ?? false == false &&
                 Date().mobileDeviceAllowedSoundTime() == true else { return }
        isMakingSound = true
        print("quack!")
        isMakingSound = false
    }
}

class DogButton {
    var isMakingSound = false
    var duckButton: DuckButton?
    var pigButton: PigButton?
    var flashlight: Flashlight?
    func didPress() {
        flashlight?.turn(on: true)
        guard duckButton?.isMakingSound ?? false == false &&
                pigButton?.isMakingSound ?? false == false &&
                 Date().mobileDeviceAllowedSoundTime() == true else { return }
        isMakingSound = true
        print("bark!")
        isMakingSound = false
    }
}

class PigButton {
    var isMakingSound = false
    var duckButton: DuckButton?
    var dogButton: DogButton?
    var flashlight: Flashlight?
    func didPress() {
        flashlight?.turn(on: true)
        guard duckButton?.isMakingSound ?? false == false && 
                dogButton?.isMakingSound ?? false == false &&
                 Date().mobileDeviceAllowedSoundTime() == true else { return }
        isMakingSound = true
        print("oink!")
        isMakingSound = false
    }
}

let flashlight = Flashlight()
let duckButton = DuckButton()
let dogButton = DogButton()
let pigButton = PigButton()

duckButton.dogButton = dogButton
duckButton.pigButton = pigButton
duckButton.flashlight = flashlight

dogButton.duckButton = duckButton
dogButton.pigButton = pigButton
dogButton.flashlight = flashlight

pigButton.duckButton = duckButton
pigButton.dogButton = dogButton
pigButton.flashlight = flashlight

duckButton.didPress()
dogButton.didPress()
pigButton.didPress()

その結果、大量のコピー&ペーストコードを含む巨大なアプリケーションができました。内部のクラスはデッドリンクによって相互に接続されています。弱い結合は存在せず、このような奇跡を維持するのは非常に困難です。エラーが発生する可能性が高いため、将来変更される可能性があります。

メディエーターを使用する

中間メディエーター クラス ApplicationController を追加しましょう。このクラスはオブジェクトの疎結合を提供し、クラス間の責任を確実に分離し、重複したコードを排除します。
書き直してみましょう:

import Foundation

class ApplicationController {

    private var isMakingSound = false
    private let flashlight = Flashlight()
    private var soundButtons: [SoundButton] = []

    func add(soundButton: SoundButton) {
        soundButtons.append(soundButton)
    }
    
    func didPress(soundButton: SoundButton) {
        flashlight.turn(on: true)
        guard Date().mobileDeviceAllowedSoundTime() && 
                isMakingSound == false else { return }
        isMakingSound = true
        soundButton.didPress()
        isMakingSound = false
    }
}

class SoundButton {
    let soundText: String
    
    init(soundText: String) {
        self.soundText = soundText
    }
    
    func didPress() {
        print(soundText)
    }
}

class Flashlight {
    var isOn = false

    func turn(on: Bool) {
        isOn = on
    }
}

extension Date {
    func mobileDeviceAllowedSoundTime() -> Bool {
        let hour = Calendar.current.component(.hour, from: self)
        let weekend = Calendar.current.isDateInWeekend(self)
        
        let result = hour >= 9 && hour <= 14 && weekend == false
        
        return result
    }
}

let applicationController = ApplicationController()
let pigButton = SoundButton(soundText: "oink!")
let dogButton = SoundButton(soundText: "bark!")
let duckButton = SoundButton(soundText: "quack!")

applicationController.add(soundButton: pigButton)
applicationController.add(soundButton: dogButton)
applicationController.add(soundButton: duckButton)

pigButton.didPress()
dogButton.didPress()
duckButton.didPress()

ユーザー インターフェイス アプリケーション アーキテクチャに関する多くの記事で、MVC パターンとその派生について説明しています。モデルはビジネス ロジック データを操作するために使用され、ビューまたは表現はインターフェイスでユーザーに情報を表示し、ユーザーとの対話を提供します。コントローラーはシステム コンポーネントの対話を保証するメディエーターです。

ソース

https://refactoring.guru/ru/design-patterns/仲介者

ソースコード

https://gitlab.com/demensdeum/patterns/< /p>

Leave a Comment

Your email address will not be published. Required fields are marked *