PHPで使うGoFパターン ひとり Advent Calendar - メディエーター


## メディエーターってなに?

メディエーターは要約しても翻訳しても仲介者って意味です。つまり、オブジェクト同士で勝手に通信しないで、メディエーターを通して通信するようにするってことですね。疎結合を実現するための仕組です。

例えば、データを具象化したエンティティクラスが有るとして、当然、関連データのエンティティ同士がお互いに通信し合って処理をしたくなりますが、そこをグっと堪えてメディエーターを 特定のサービスに特化 して定義して処理をするようにする というようなパターンです。


## メディエーターの構造

* MediatorBase - Colleague オブジェクト間のコミュニケーションのインタフェースを定義する
* Mediator - Mediatoの実クラス、Colleague オブジェクト間の通信を調整する。全ての Colleague の存在と、通信の目的について知っている。
* Colleague - Mediator を介して他の Colleagues と通信する実クラス。今回は抽象クラスも用意している。

```PHP
<?php
//
// MediatorBase
//
abstract class MediatorBase {
  private   $_Colleagues  = array ();

  public function learn ( $obj ) {
    if ( !( $obj instanceof ColleagueBase ) )
      throw new Exception ( 'そんなオブジェクト無理だもん!' );

    if ( $this->getObject ( $obj->getID() )  )
      throw new Exception ( 'ダブってるもん!' .  $obj->getID() );
    $obj->setMediator ( $this );
    $this->_Colleagues[] = $obj;
  }

  public function getObject ( $id ) {
    $arr = array_filter ( $this->_Colleagues, function ( $obj ) use ( $id ) {
      return $id == $obj->getID();
    });
    $key = array_keys ( $arr );
    return $arr[$key[0]];
  }
}

//
// 操作される側 Colleague の 雛形
//
abstract class ColleagueBase {
  private   $_ID = null;
  protected $_Mediator = null;
  private $_Inbox = array ();
  public function setID ( $id ) {
    if ( !$id ) throw new Exception ( 'IDが無いもん!' );
    $this->_ID = $id;
  }

  public function getID () {
    if ( !$this->_ID )
      throw new Exception ( 'IDが設定されてないもん!' );
    return get_class ( $this ) . "|" . $this->_ID;
  }

  public function setMediator ( $obj ) {
    if ( !( $obj instanceof MediatorBase ) )
      throw new Exception ( 'そんなオブジェクト無理だもん!' );
    $this->_Mediator = $obj;
  }

  public function sendMessage ( $to_id, $msg ) {
    $this->_Mediator->sendMessage ( $this->getID(), $to_id, $msg );
  }

  public function recieveMessage ( $from_id, $msg ) {
    $this->_Inbox[] = array (
      'from'    => $from_id,
      'message' => $msg,
      'date_time' => date ( 'Y/m/d H:i:s' ),
    );
  }

  public function getInbox () {
    return $this->_Inbox;
  }

}

//
// 操作される側 Colleague の 実クラス
//
class User extends ColleagueBase {
  public function __construct ( $id ) {
    $this->setID( $id );
  }
}

//
// 操作される側 Colleague の 実クラス その2
//
class Company extends ColleagueBase {
  public function __construct ( $id ) {
    $this->setID ( $id );
  }
}

//
// 実 Mediator
//
class PostMan extends MediatorBase {
  public function sendMessage ( $from_id, $to_id, $msg ) {
    if ( !( $to = $this->getObject ( $to_id ) ) )
      throw new Exception ( 'そんなID無いもん' . $to_id );
    $to->recieveMessage ( $from_id, $msg );
  }
}

$pm = new PostMan();
$larry =  new User ( 'Larry' );
$gates  = new User ( 'Gates' );
$jobs   = new User ( 'Jobs' );
$ms     = new Company ( 'M$' ) ;
$apple  = new Company ( 'Apple' );

$pm->learn ( $larry );
$pm->learn ( $gates );
$pm->learn ( $jobs );
$pm->learn ( $ms );
$pm->learn ( $apple );

$larry->sendMessage ( 'User|Gates', 'ヽ(゜▽、゜)ノ' );
$larry->sendMessage ( 'User|Jobs', '＼(^o^)／' );
$larry->sendMessage ( 'Company|Apple', '(^^)' );

print "== " . $apple->getID() . " == \n";
var_dump ( $apple->getInbox() );

print "== " . $jobs->getID() . " == \n";
var_dump ( $jobs->getInbox() );

print "== " . $gates->getID() . " == \n";
var_dump ( $gates->getInbox() );
```

## 単体テスト的にどうなの?

親子で持ち持ちの関係になるので単体でのテストが難しいです。
Mediatorを極力他に依存しないように作り、
MediatorとのセットでUnitテストを定義すれば可能ではあります。