JavaのMediatorパターン

1. 概要

この記事では、https://en.wikipedia.org/wiki/Design_Patterns [GoF行動パターン] *の1つである* Mediatorパターンを見ていきます。 その目的を説明し、いつ使用すべきかを説明します。
いつものように、簡単なコード例も提供します。

2. メディエーターパターン

オブジェクト指向プログラミングでは、*コンポーネントが疎結合で再利用可能になるように*システムを設計するように常に試みる必要があります*。 このアプローチにより、コードの保守とテストが容易になります。
ただし、実際には、多くの場合、依存オブジェクトの複雑なセットを処理する必要があります。 これは、Mediatorパターンが役立つ場合です。
  • Mediatorパターンの目的は、互いに直接通信する密結合オブジェクト間の複雑さと依存関係を減らすことです*。 これは、依存オブジェクト間の相互作用を処理するメディエーターオブジェクトを作成することで実現されます。 したがって、すべての通信はメディエーターを経由します。

    これにより、連携するコンポーネントのセットが直接相互作用する必要がなくなるため、疎結合が促進されます。 代わりに、単一のメディエーターオブジェクトのみを参照します。 この方法では、システムの他の部分でこれらのオブジェクトを再利用することも簡単です。

3. Mediator PatternのUML図

パターンを視覚的に見てみましょう。
link:/uploads/mediator-100x52.png%20100w []
上記のUML図では、次の参加者を識別できます。
  • _Mediator_は、_Colleague_オブジェクトが使用するインターフェースを定義します
    通信する

  • Colleague_は、単一の参照を保持する抽象クラスを定義します
    _Mediator

  • _ConcreteMediator_は、相互作用ロジックをカプセル化します
    _同僚_オブジェクト

  • ConcreteColleague1_および_ConcreteColleague2_は、
    _Mediator

    ご覧のとおり、* _ Colleague_オブジェクトは互いに直接参照しません。 代わりに、すべての通信は* _ * Mediator * ._によって実行されます
    その結果、_ConcreteColleague1_および_ConcreteColleague2_はより簡単に再利用できます。
    また、_Colleague_オブジェクトの連携方法を変更する必要がある場合は、_ConcreteMediator_ロジックを修正するだけです。 または、_Mediator._の新しい実装を作成できます

4. Java実装

理論の明確なアイデアが得られたので、実際に概念をよりよく理解するための例を見てみましょう。

* 4.1。 シナリオ例*

ファン、電源、ボタンで構成されるシンプルな冷却システムを構築していると想像してください。 ボタンを押すと、ファンがオンまたはオフになります。 ファンをオンにする前に、電源をオンにする必要があります。 同様に、ファンをオフにした直後に電源をオフにする必要があります。
ここで、実装例を見てみましょう。
public class Button {
    private Fan fan;

    // constructor, getters and setters

    public void press(){
        if(fan.isOn()){
            fan.turnOff();
        } else {
            fan.turnOn();
        }
    }
}
public class Fan {
    private Button button;
    private PowerSupplier powerSupplier;
    private boolean isOn = false;

    // constructor, getters and setters

    public void turnOn() {
        powerSupplier.turnOn();
        isOn = true;
    }

    public void turnOff() {
        isOn = false;
        powerSupplier.turnOff();
    }
}
public class PowerSupplier {
    public void turnOn() {
        // implementation
    }

    public void turnOff() {
        // implementation
    }
}
次に、機能をテストしましょう。
@Test
public void givenTurnedOffFan_whenPressingButtonTwice_fanShouldTurnOnAndOff() {
    assertFalse(fan.isOn());

    button.press();
    assertTrue(fan.isOn());

    button.press();
    assertFalse(fan.isOn());
}
すべてうまくいっているようです。 ただし、* _ Button、Fan、_、および_PowerSupplier_クラスがどのように密結合されているかに注意してください。 _Button_は_Fan_で直接動作し、_Fan_は_Button_と_PowerSupplier._の両方と対話します。
_Button_クラスを他のモジュールで再利用するのは難しいでしょう。 また、システムに2番目の電源を追加する必要がある場合は、_Fan_クラスのロジックを変更する必要があります。

4.2. メディエーターパターンの追加

次に、メディエーターパターンを実装して、クラス間の依存関係を減らし、コードを再利用しやすくします。
まず、_Mediator_クラスを紹介しましょう:
public class Mediator {
    private Button button;
    private Fan fan;
    private PowerSupplier powerSupplier;

    // constructor, getters and setters

    public void press() {
        if (fan.isOn()) {
            fan.turnOff();
        } else {
            fan.turnOn();
        }
    }

    public void start() {
        powerSupplier.turnOn();
    }

    public void stop() {
        powerSupplier.turnOff();
    }
}
次に、残りのクラスを変更しましょう。
public class Button {
    private Mediator mediator;

    // constructor, getters and setters

    public void press() {
        mediator.press();
    }
}
public class Fan {
    private Mediator mediator;
    private boolean isOn = false;

    // constructor, getters and setters

    public void turnOn() {
        mediator.start();
        isOn = true;
    }

    public void turnOff() {
        isOn = false;
        mediator.stop();
    }
}
繰り返しになりますが、機能をテストしましょう。
@Test
public void givenTurnedOffFan_whenPressingButtonTwice_fanShouldTurnOnAndOff() {
    assertFalse(fan.isOn());

    button.press();
    assertTrue(fan.isOn());

    button.press();
    assertFalse(fan.isOn());
}
当社の冷却システムは期待どおりに機能します。
  • Mediatorパターンを実装したので、Button _、 Fan_、または_PowerSupplier_クラスのいずれも直接通信しません*。 _Mediator._への参照は1つのみです。

    将来2番目の電源を追加する必要がある場合は、_Mediator's_ロジックを更新するだけです。 _Button_および_Fan_クラスは変更されません。
    この例は、依存オブジェクトを簡単に分離し、システムの保守を容易にする方法を示しています。

5. メディエーターパターンを使用する場合

*緊密に結合され、保守が困難なオブジェクトのセットを処理する必要がある場合、メディエーターパターンは良い選択です。*この方法により、オブジェクト間の依存関係を減らし、全体的な複雑さを減らすことができます。
さらに、メディエーターオブジェクトを使用して、通信ロジックを単一のコンポーネントに抽出します。したがって、https://www.baeldung.com/solid-principles#s [単一の責任原則]に従います。 さらに、システムの残りの部分を変更する必要なく、新しいメディエーターを導入できます。 したがって、オープンクローズド原則に従います。
*ただし、システムの設計の誤りにより、密結合オブジェクトが多すぎる場合があります。 この場合、メディエーターパターン*を適用しないでください。 代わりに、一歩後退して、クラスをモデル化した方法を再考する必要があります。
他のすべてのパターンと同様に、* Mediatorパターンを盲目的に実装する前に、特定のユースケースを考慮する必要があります*。

6. 結論

この記事では、メディエーターパターンについて学びました。 このパターンが解決する問題と、実際にそれを使用することをいつ検討すべきかを説明しました。 また、設計パターンの簡単な例を実装しました。
いつものように、完全なコードサンプルはhttps://github.com/eugenp/tutorials/tree/master/patterns/design-patterns-behavioral[GitHubで]から入手できます。