1. 概要

この記事では、GoFの動作パターンの1つであるメディエーターパターンについて説明します。 その目的を説明し、いつ使用すべきかを説明します。

いつものように、簡単なコード例も提供します。

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

オブジェクト指向プログラミングでは、コンポーネントが緩く結合されて再利用できるようにシステムを設計するように常に努める必要があります。 このアプローチにより、コードの保守とテストが容易になります。

ただし、実際には、依存オブジェクトの複雑なセットを処理する必要があることがよくあります。 これは、メディエーターパターンが役立つ場合があります。

メディエーターパターンの目的は、互いに直接通信する密結合オブジェクト間の複雑さと依存関係を軽減することです。 これは、依存オブジェクト間の相互作用を処理するメディエーターオブジェクトを作成することによって実現されます。 その結果、すべての通信はメディエーターを経由します。

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

3. メディエーターパターンのUML図

パターンを視覚的に見てみましょう。

上記のUMLダイアグラムでは、次の参加者を識別できます。

  • Mediator は、Collegeオブジェクトが通信に使用するインターフェースを定義します
  • 同僚は、Mediatorへの単一の参照を保持する抽象クラスを定義します
  • ConcreteMediator は、同僚オブジェクト間の相互作用ロジックをカプセル化します
  • ConcreteColleague1ConcreteColleague2は、Mediatorを介してのみ通信します。

ご覧のとおり、 同僚のオブジェクトは、相互に直接参照しません。 代わりに、すべての通信はによって実行されますメディエーター。

その結果、ConcreteColleague1およびConcreteColleague2をより簡単に再利用できます。

また、同僚オブジェクトの連携方法を変更する必要がある場合は、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クラスが緊密に結合されていることに注意してくださいボタンファン上で直接動作し、ファンボタン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());
}

当社の冷却システムは期待どおりに機能します。

メディエーターパターンを実装したので、Button、Fan、またはPowerSupplierクラスのいずれも直接通信しませんMediatorへの参照は1つだけです。

将来、2つ目の電源を追加する必要がある場合は、Mediatorのロジックを更新するだけです。 ButtonおよびFanクラスは変更されません。

この例は、依存オブジェクトを簡単に分離し、システムの保守を容易にする方法を示しています。

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

メディエーターパターンは、緊密に結合されて保守が難しいオブジェクトのセットを処理する必要がある場合に適しています。 このようにして、オブジェクト間の依存関係を減らし、全体的な複雑さを減らすことができます。

さらに、メディエーターオブジェクトを使用して、通信ロジックを単一コンポーネントに抽出するため、単一責任原則に従います。 さらに、システムの残りの部分を変更することなく、新しいメディエーターを導入できます。 したがって、私たちは開放閉鎖原則に従います。

ただし、システムの設計に問題があるために、密に結合されたオブジェクトが多すぎる場合があります。 この場合、メディエーターパターンを適用しないでください 。 代わりに、一歩下がって、クラスのモデル化方法を再考する必要があります。

他のすべてのパターンと同様に、 Mediator Pattern を盲目的に実装する前に、特定のユースケースを検討する必要があります。

6. 結論

この記事では、メディエーターパターンについて学びました。 このパターンがどのような問題を解決し、いつ実際に使用を検討すべきかを説明しました。 また、デザインパターンの簡単な例を実装しました。

いつものように、完全なコードサンプルはGitHubから入手できます。