はじめに
本記事は、GoFデザインパターン解説シリーズの1つです。
今回は、振る舞いに関するパターンの中のMediator(メディエーター)パターンを解説します。
オブジェクト同士が「お互いを直接知りすぎている」状態を解消するパターンです。
解決したい課題
複数のオブジェクトがお互いを直接参照し合って連携する設計は、オブジェクトの数が増えるにつれて、その依存関係が網の目状(N対N)に複雑化していきます。
例えば、チャットアプリのUIコンポーネント群(入力欄・送信ボタン・文字数カウンター・送信履歴一覧)が、お互いに直接メソッドを呼び合っているとします。
「送信ボタンが押されたら、入力欄の内容を取得し、履歴一覧に追加し、入力欄をクリアし、文字数カウンターをリセットする」といった連携を、各コンポーネントが直接他のコンポーネントを参照して実装すると、コンポーネントを1つ追加・削除するたびに、関連する他の全コンポーネントの実装を見直す必要が出てきます。
Mediatorパターンは、オブジェクト同士を直接やり取りさせず、仲介役(Mediator)を1つ挟んで、すべてのやり取りをそこに集約することで、この問題を解決します。
各オブジェクトは、Mediatorとだけやり取りすればよくなります。
クラス構成
- Mediator(メディエーター): オブジェクト間のやり取りを仲介する、共通インターフェース
- ConcreteMediator(具体的なメディエーター): 実際の仲介ロジックを持つクラス。各Colleagueへの参照を保持する
- Colleague(コリーグ/同僚): Mediatorを介してやり取りする、個々のオブジェクト。他のColleagueを直接は知らない
Javaでの実装例
先ほどのチャットUIの例で実装してみます。
Mediator:共通インターフェース
public interface ChatMediator {
void notify(Component sender, String event);
}
Colleague:UIコンポーネント共通の抽象クラス
public abstract class Component {
protected final ChatMediator mediator; // Mediatorへの参照だけを持つ(他のColleagueは知らない)
protected Component(ChatMediator mediator) {
this.mediator = mediator;
}
}
ConcreteColleague:具体的なUIコンポーネント群
public class MessageInput extends Component {
private String text = "";
public MessageInput(ChatMediator mediator) {
super(mediator);
}
public void type(String text) {
this.text = text;
System.out.println("[入力欄] \"" + text + "\" と入力されました");
mediator.notify(this, "TEXT_CHANGED"); // 自分の状態変化をMediatorに通知するだけ
}
public String getText() {
return text;
}
public void clear() {
this.text = "";
}
}
public class CharCounter extends Component {
public CharCounter(ChatMediator mediator) {
super(mediator);
}
public void updateCount(int count) {
System.out.println("[文字数カウンター] " + count + "文字");
}
}
public class SendButton extends Component {
public SendButton(ChatMediator mediator) {
super(mediator);
}
public void click() {
System.out.println("[送信ボタン] クリックされました");
mediator.notify(this, "SEND_CLICKED");
}
}
public class MessageHistory extends Component {
public MessageHistory(ChatMediator mediator) {
super(mediator);
}
public void addMessage(String message) {
System.out.println("[送信履歴] 追加: " + message);
}
}
ConcreteMediator:実際の連携ロジックを持つクラス
public class ChatRoomMediator implements ChatMediator {
private MessageInput input;
private CharCounter counter;
private SendButton sendButton;
private MessageHistory history;
public void setInput(MessageInput input) {
this.input = input;
}
public void setCounter(CharCounter counter) {
this.counter = counter;
}
public void setSendButton(SendButton sendButton) {
this.sendButton = sendButton;
}
public void setHistory(MessageHistory history) {
this.history = history;
}
@Override
public void notify(Component sender, String event) {
// どのコンポーネントから、どんなイベントが来たかに応じて、連携ロジックをここに集約する
if (sender == input && event.equals("TEXT_CHANGED")) {
counter.updateCount(input.getText().length());
} else if (sender == sendButton && event.equals("SEND_CLICKED")) {
history.addMessage(input.getText());
input.clear();
counter.updateCount(0);
}
}
}
利用側のコード
public class Main {
public static void main(String[] args) {
ChatRoomMediator mediator = new ChatRoomMediator();
MessageInput input = new MessageInput(mediator);
CharCounter counter = new CharCounter(mediator);
SendButton sendButton = new SendButton(mediator);
MessageHistory history = new MessageHistory(mediator);
mediator.setInput(input);
mediator.setCounter(counter);
mediator.setSendButton(sendButton);
mediator.setHistory(history);
input.type("こんにちは");
sendButton.click();
}
}
実行結果:
[入力欄] "こんにちは" と入力されました
[文字数カウンター] 6文字
[送信ボタン] クリックされました
[送信履歴] 追加: こんにちは
[文字数カウンター] 0文字
MessageInput・CharCounter・SendButton・MessageHistoryは、お互いを一切直接知りません。
「入力欄が変化したらカウンターを更新する」「送信ボタンが押されたら履歴に追加し、入力欄をクリアする」といった連携ロジックはすべてChatRoomMediatorに集約されています。
新しいコンポーネント(例えば「文字数の上限警告表示」)を追加したい場合も、既存のコンポーネント同士のコードには手を入れず、Mediator側にロジックを追加するだけで済みます。
Observerパターンとの関係
Mediatorパターンは、後述するObserverパターンと組み合わせて実装されることがよくあります(上記の例では簡略化のため直接notify()を呼んでいますが、実際にはMediatorが各Colleagueのイベントを購読するObserverとして振る舞う実装も一般的です)。
両者は「複数オブジェクト間の連携を疎結合にする」という目的は共通していますが、Mediatorは「中央に連携ロジックを集約する」ことに主眼があるのに対し、Observerは「状態変化の通知の仕組み」自体に主眼がある、という違いがあります。
使用場面
- GUIの複数コンポーネントが、複雑に連携し合うダイアログ・フォームの実装
- 複数のオブジェクトが直接参照し合うことで、依存関係が複雑化(スパゲッティ化)している場合の整理
- チャットルームのように、複数の参加者(オブジェクト)間のメッセージのやり取りを一元管理したい場合
- 航空管制システム(複数の飛行機が、管制塔という仲介役を通じてのみやり取りする)のような、実世界の「仲介役」をモデル化する場合
まとめ
| 項目 | 内容 |
|---|---|
| 解決する課題 | 複数オブジェクト間の直接的な依存関係が、網の目状に複雑化してしまう |
| 実現方法 | オブジェクト同士のやり取りを、仲介役(Mediator)に一元集約する |
| メリット | 各オブジェクトはMediatorとだけやり取りすればよく、連携ロジックの変更・追加が容易になる |
| 関連パターン | Observerパターンと組み合わせて実装されることも多い |
おすすめ書籍
GoFの23パターンをより深く学びたい方には、以下の書籍がおすすめです。
Java言語のサンプルコードとUMLを使い、初心者にもわかりやすく解説されています。
Java言語で学ぶデザインパターン入門 第3版 [ 結城 浩 ]
※本リンクはアフィリエイトリンクを含みます。


