第16章では Mediator パターンを学びます。
Mediator とは、英語で「仲裁人、調停者」を意味する単語です。
Mediator パターンとは、多数のオブジェクトの間の調整を行いながら処理をすすめる必要が
ある場合に利用すると威力を発揮するパターンです。
例えば、車どおりの多い交差点で信号が壊れた時に、警官が交通整備をしないとどうなるでしょう?
それぞれの車が、めいめい判断し、行けると思ったら進む。こんな状態で、
信号の故障をうまく乗り切ることができるでしょうか?おそらく無理でしょう。
いくら皆が一生懸命周囲の状況を確認しても、全員が同じ認識にいたるはずがありません。
そこで、仲裁人である「交通巡査」の登場です。彼は、周囲の状況を見渡し、判断した上で、
「進め」や「止まれ」の指示を出します。全ての車は、交通巡査の指示通りに進んだり止まったりするわけです。
Mediator パターンは、複数のオブジェクト間の調整をするために、
各オブジェクトからの問い合わせを受け、
適宜判断を行い指示を出す「仲裁人」の役割を果たすクラスを利用するパターンです。
サンプルケースでは、あなたのクラスの恋愛相談を一手に引き受けている、斉藤君に注目してみましょう。
斉藤君は顔が広く、みんなから恋愛相談を持ち込まれることが多いようです。
さまざまな恋愛相談を受け、周囲の状況をよく把握していることもあり、いつも的確にアドバイスをくれます。
| 水島君 |
: |
おれ、玲奈ちゃんに惚れちゃったんだよね。 |
| 斉藤君 |
: |
まじっ!? (でも、玲奈ちゃんは、いまラブラブらしいな)
うーん、残念ながら、今はたぶん無理だね。 |
| 水島君 |
: |
そっか〜。残念だけど、今はあきらめるよ。
ところで、和香ちゃんと由実ちゃんって今好きな人いるのかなぁ? |
| 斉藤君 |
: |
ちょっと知らないなぁ。 |
| 水島君 |
: |
わかった、また何かあったらよろしく。 |
新田君も相談があるようです。
| 新田君 |
: |
おれ、中井ちゃんに惚れたかもしれん。 |
| 斉藤君 |
: |
まじっすか!? (中井ちゃんかぁ・・・新田君ってけっこう薄い顔が好きなんだな。)
中井ちゃんなら、うまくいけばうまくいくかも。中井ちゃんにそれとなく聞いてみるわ。
|
| 新田君 |
: |
まじで?ありがとう。 がんばってみよっかな。 |
って、「水島、惚れたならまっすぐ進むのが男の道だ!」なんて思わずに続けましょう。
このように、斉藤君は、いろいろな人から「相談役」として認められており、
斉藤君自身も、みんなの恋愛状況をよく把握しています。斉藤君の友達は、恋愛に行き詰ると斉藤君に相談に来ます。
斉藤君は相談を受けると、適切なアドバイスを与えたり、時によってはいろいろな調整をしてくれるかもしれません。
上の会話の中で、水島君が「玲奈ちゃん」「和香ちゃん」「由実ちゃん」に興味を持っているという情報を得ています。
もし、いつか「和香ちゃん」から、「水島君に興味があるの」なんていう相談を斉藤君が受けたら、
太郎君にとっても、和香ちゃんにとっても幸せな結末が待っていることは間違いないですよね。
クラス図にすると以下のようになるでしょうか。
このように、全体の状態を把握し、各自がどのように動けばうまくいくかを管理する役を斉藤君が担っており、
斉藤君に相談する友達は、自分の行動を斉藤君のアドバイスによって決定したり、
斉藤君からの調整を受けて何かの行動を起こしたりするわけです。Mediator パターンでは、
このような調整役の役割を果たす Mediator インタフェース、相談をする側を表す Collegue インタフェースを用意し、
それぞれを実装する形で、実際の調整役や相談者を表すクラスを作成します。
サンプルケースをもう少し整理すると、以下の図のようになります。
Saito クラスと、Nitta クラスを例にサンプルコードを書いてみましょう。
public class Saito implements LoveMediator{
private Map colleagueList = new HashMap();
public void addColleague(Colleague colleague){
colleagueList.set(colleague.getName(),colleague);
}
public int consultation(Colleague colleagueInLove,Colleague loveTarget) {
int possibility;
/* さまざまな状況を考慮して、possibility を導出 */
・・・・
return possibility;
}
}
public class Nitta implements Colleague{
private int tension;
private Colleague loveTarget = null;
private LoveMediator mediator = new Saito();
private void setLoveTarget(Colleague colleague){
this.loveTarget = colleague;
}
public void needsAdvice(){
this.tension = mediator.consultation(this,loveTarget);
}
}
もし、斉藤君がいなかったら、水島君は、たくさんの女の子に声をかける必要がありますし、たくさんの人の情報を把握しておく必要もあります。
もし、水島君の気が変わって別の女の子を好きになったときには、声をかけたたくさんの女の子に「いやぁ、実はね別の女の子のことを好きになっちゃって・・・。」
なんて話をしなくてはなりません。逆に、以前は好意をもたれていたんだけど、今はなんとも思われていない女の子に、「俺も君の事が好きに・・・。」
なんて不幸な告白をすることもありえます。
このように、斉藤君が仲介役として間に入って調整することで、各 Colleague インスタンスが互いに調整する必要がなくなり、
全体の状況を管理することが非常に楽になります。
Mediator パターンは、入力インタフェースなどに利用することができるでしょう。
例えば、『複雑に絡み合ったある条件を満たさなければ「有効」にならないボタン』なんてものを作成したいときは
Mediator パターンを思い出すべきですね。
「ラジオボタン Aが選択されている状態で、テキストボックスに入力がある場合」もしくは、
「ラジオボタンB が選択されている状態で、チェックボックス C もしくは D にチェックが入っている場合」
に「有効」になるボタンは、「ラジオボタン A」がチェックされたときに、
「テキストボックスに入力があるか」を検証したり、ラジオボタン B が選択されたときに
「チェックボックス C もしくは D がチェックされているか」を検証するようなプログラムにしていると、チェックボックス C
にチェックが入れられたときや、チェックボックス D のチェックがはずされたときなども検証する必要があるプログラムになります。
条件が複雑になるにつれ、どこで何をチェックしているのか管理できなくなってくるでしょう。
Mediator パターンでは、各オブジェクトは、自分の状態が変わったときに、Mediator に連絡し、
Mediator は、状態が変わったオブジェクトだけでなく、各オブジェクトの状態を総合的に判断し、
「ボタン」を示すオブジェクトに、「有効」「無効」を伝えるような設計になり、管理が比較的楽になります。