RitoLabo

Mediatorパターン - PHPデザインパターン

  • 公開:
  • カテゴリ: PHP DesignPatterns
  • タグ: PHP,Behavior,DesignPatterns,Mediator

Mediatorパターン(メディアター・パターン)は振る舞いに関するデザインパターン手法の1つで、オブジェクト同士の無秩序な相互参照を一極集中型にする事で、シンプルかつメンテナビリティ(保守性)の高いインターフェイスを実現する処理モデルです。

Mediatorパターンの基本的なクラス図は以下の通りです。

Mediatorパターンクラス図

実装例

今回は、飛行機の着陸を例にMediatorパターンを実装します。

空港には日々たくさんの旅客機が離発着しますが、飛び立ったり降り立ったりするには滑走路が必要です。いわば空港とは、飛行機たちの滑走路の取り合い、戦場なわけですが、好き勝手に使えば衝突など大惨事になります。かと言って、どこにどれだけいるかわからない他の飛行機に無線で「12番滑走路使って良いですか?」なんて聞いていたらきりがありません。

そこで登場するのが管制塔です。飛行機の機長たちは、管制塔へ問い合わせを行いさえすれば、離発着に関しては全て適切な回答を得る事ができ、何台飛行機がいようと即座に使って良い滑走路を知る事が出来ます。

まずはColleagueクラスであり、空の雄である旅客機クラスを作成します。

App/Airport/Aircraft.php
<?php

namespace App\Airport;

use App\Airport\ControlTower;

class Aircraft
{
private $control_tower;
private $name;

public function __construct($name)
{
$this->name = $name;
}
public function getName()
{
return $this->name;
}
public function setControlTower(ControlTower $controlTower)
{
$this->control_tower = $controlTower;
}
/**
* 着陸申請
*/
public function applyForLanding()
{
$this->control_tower->applyForLanding($this->name);
}
}

ポイントは、自身で管制塔クラスを保持する点です。applyForLanding()メソッドで着陸申請を行いますが、その際に管制塔へ申請を行うようにしている事で、他の旅客機クラスに滑走路を使用して良いかを問い合わせる必要がなくなります。デザインパターン的というか、プログラミング的にに言えば、他のColleagueクラスの状態を気にしなくて良くなります。

次にMediatorクラスであり、今回の主役である管制塔クラスを作成します。

App/Airport/ControlTower.php
<?php

namespace App\Airport;

use App\Airport\Aircraft;

class ControlTower
{
private $aircrafts = []; // 管制塔が把握している旅客機
private $landing = [ // 着陸滑走路の使用状況
'01' => null,
'02' => null,
'03' => null,
];

public function discovered(Aircraft $aircraft)
{
$aircraft->setControlTower($this);
if (!array_key_exists($aircraft->getName(), $this->aircrafts)) {
$this->aircrafts[$aircraft->getName()] = $aircraft;
echo sprintf('%s をレーダーで捕捉しました<br>', $aircraft->getName());
}
}

public function applyForLanding($aircraft_name)
{
$runway_number = array_search($aircraft_name, $this->landing);
if ($runway_number) {
echo sprintf('もう着陸許可は下りているわ、%s番滑走路を使って。<br>', $runway_number);
} else {
$allow = false;
foreach ($this->landing as $key => $runway) {
if (!$runway) {
$this->landing[$key] = $aircraft_name;
$allow = true;
echo sprintf('%s こちら管制塔。着陸を許可する。%s番滑走路を使ってください。<br>', $aircraft_name, $key);
return false;
}
}
if (!$allow) {
echo sprintf('%sへ、こちら管制塔。現在滑走路は埋まっているので少し待ってください。<br>', $aircraft_name);
}
}
}
}

管制塔クラスでは、2つの機能を実装しました。

  • レーダーによって旅客機を捕捉する discovered() メソッド
  • 着陸申請を処理する applyForLanding() メソッド

ポイントは、discovered() メソッドで旅客機を捕捉した、つまり引数として旅客機クラスが渡ってきます。そして旅客機クラス自身にこの管制塔を認識させる、つまりsetControlTower()メソッドで管制塔自身を旅客機クラスへセットしている部分です。

これで実装が完了したので、クライアントから利用してみます。

index.php
<?php
require_once 'autoload.php';

use App\Airport\ControlTower;
use App\Airport\Aircraft;

// 管制塔インスタンス
$control_tower = new ControlTower();

// 旅客機インスタンス
$aircraft_1 = new Aircraft('A001便');
$aircraft_2 = new Aircraft('B002便');
$aircraft_3 = new Aircraft('C003便');
$aircraft_4 = new Aircraft('D004便');
$aircraft_5 = new Aircraft('E005便');

// 管制塔がレーダーで旅客機を捕捉しました
$control_tower->discovered($aircraft_1);
$control_tower->discovered($aircraft_2);
$control_tower->discovered($aircraft_3);
$control_tower->discovered($aircraft_4);
$control_tower->discovered($aircraft_5);

echo '<hr>';

// 着陸許可を求める機長たち
$aircraft_1->applyForLanding();
$aircraft_2->applyForLanding();
$aircraft_3->applyForLanding();
$aircraft_4->applyForLanding();
$aircraft_5->applyForLanding();

echo '<hr>';

// 「無線の調子がおかしい、返答が良く聞こえなかったな。」
// 「もう一度管制塔へ連絡してみよう。」
$aircraft_1->applyForLanding();

結果は以下になります。

PHPでMediatorパターン実行結果

PHPからMediatorパターンで、参照を一極集中型にした処理を実装できました。

まとめ

mediator = 仲介者」を意味する通り、処理の中で関係するオブジェクトたちの相互参照を止めmediatorクラス1つに集約する事によって、互いの結合度が弱まる、つまりは分離され独立します。こうする事で拡張性の高く明快な構造を持つ1つの機能として成立します。

1つの機能にいくつものオブジェクトが関わる事は少なくなく、規模が大きくなればなるほどその関係性は複雑化していきます。開発の際は実装に入る前に一度立ち止まり、1つの機能の先を見据え、適切にクラス設計を行ってからPHPを書き始めましょう。