【Gang of Four】デザインパターン学習 - Mediator


Mediator - 仲介者

目次
その名の通り、オブジェクト間の関連を仲介者クラスを介することによってなくしてしまおうというのが本パターンです。

テキストボックスに値が入っていないとボタンが有効にならない、というような仕様をよく見かけます。
これはテキストボックスオブジェクトがボタンオブジェクトを知っていなければ(インスタンス変数として保持していなければ)実現できません。
このようなシンプルな仕様では直接関連していても問題はないかもしれませんが、もっと複雑な機能になるといくつものオブジェクトをあるクラスが知っていなければならなくなります。
それを仲介者クラスを使ってなくします。

やはり、どのようなパターンも直交性を高めることに帰結しますね。

目的

オブジェクト群の相互作用をカプセル化するオブジェクトを定義する。Mediator パターンは、オブジェクト同士がお互いを明示的に参照し合うことがないようにして、結合度を低めることを促進する。それにより、オブジェクトの相互作用を独立に変えることができるようになる。

構成要素

・Mediator 仲介者抽象クラス
・ConcreteMediator 仲介者具象クラス
・Colleague 色々なオブジェクトと関連したいオブジェクト

実装

チェックボックス、テキストボックス、ボタンオブジェクトを実装します。
テキストボックスはチェックボックスのどれかにチェックが入っていないと入力ができません。
ボタンはチェックボックスにチェックが入っているかつテキストボックスに入力がないと押下できません。

Mediator 仲介者抽象クラス

Mediator.kt
package mediator

interface Mediator {

    /**
     * ボタン押下可否
     */
    fun buttonPress(): Boolean

    /**
     * テキストボックス入力可否
     */
    fun inputTextBox(): Boolean
}

ConcreteMediator 仲介者具象クラス

ConcreteMediator.kt
package mediator

class ConcreteMediator: Mediator {

    var checkBox: CheckBox? = null
    var textBox: TextBox? = null
    var button: Button? = null

    override fun buttonPress(): Boolean {
        return checkBox?.sex != null && textBox?.value?.isNotEmpty() ?: false
    }

    override fun inputTextBox(): Boolean {
        return checkBox?.sex != null
    }

}

Colleague 色々なオブジェクトと関連したいオブジェクト

抽象クラス

Colleague.kt
package mediator

interface Colleague {
    enum class Type {
        CheckBox,
        TextBox,
        Button
    }

    val type: Type
    val mediator: Mediator
}

具象クラス
・チェックボックス

CheckBox.kt
package mediator

    class CheckBox(override val mediator: Mediator): Colleague {
    override val type = Colleague.Type.CheckBox
    enum class Sex {
        Male,
        Female
    }

    var sex: Sex? = null

    fun select(sex: Sex) {
        this.sex = sex
    }
}

・テキストボックス

TextBox.kt
package mediator

class TextBox(override val mediator: Mediator): Colleague {
    override val type = Colleague.Type.TextBox
    var value = ""

    fun input(value: String) {
        if (mediator.inputTextBox()) {
            this.value = value
            println("テキストボックス入力成功")
        } else {
            println("テキストボックス入力失敗")
        }
    }
}

・ボタン

Button.kt
package mediator

class Button(override val mediator: Mediator): Colleague {
    override val type = Colleague.Type.Button

    fun press() {
        if (mediator.buttonPress()) {
            println("ボタン押下成功")
        } else {
            println("ボタン押下失敗")
        }
    }
}

使う人

Client.kt
package mediator

class Client {
    init {
        val mediator = ConcreteMediator()
        val checkBox = CheckBox(mediator)
        val textBox = TextBox(mediator)
        val button = Button(mediator)

        mediator.checkBox = checkBox
        mediator.textBox = textBox
        mediator.button = button

        // テキストボックス入力 チェックボックスが選択されていないため入力失敗
        textBox.input("山田太郎")

        // チェックボックス選択
        checkBox.select(CheckBox.Sex.Female)

        // ボタン押下 テキストボックスが入力されていないため失敗
        button.press()

        // テキストボックス入力 成功!!!
        textBox.input("山田花子")

        // ボタン押下 成功!!!
        button.press()
    }
}

出力結果

[out-put]
テキストボックス入力失敗
ボタン押下失敗
テキストボックス入力成功
ボタン押下成功

以上