Mediatorパターンとはどういうものか
Mediatorとは仲介者という意味です。
プログラムを組んでいく中で、様々なクラスがお互いにやり取りする状態が多くなり、複雑な構造となることがしばしばあります。
そこでオブジェクト間のやり取りを仲介するmediatorを配置することで、オブジェクト同士で直接通信を行うことなくお互いの依存関係を削減することができます。
登場するクラス
Mediator:オブジェクト間のやり取りを仲介するクラス
Colleague:Mediatorによってやり取りされる側のクラス
[参考サイト]
https://ja.wikipedia.org/wiki/Mediator_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3
https://bmf-tech.com/posts/PHP%E3%81%A7%E5%AD%A6%E3%81%B6%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%20-%20Mediator%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3
https://qiita.com/i-tanaka730/items/9f96d28d32ab3c9099d9#4-2-loginframe%E3%82%AF%E3%83%A9%E3%82%B9
https://www.ritolab.com/entry/137
Mediatorパターンを用いるメリット
・オブジェクト間のやり取りがmediatorによって行われるため、処理がカプセル化される。
・オブジェクト同士の複雑な依存関係を集約できる。
サンプルコード
仕様
・出社しているアルバイトに仕事を割り振る
・料理ができる人は優先的にキッチンへ
・シフトがいっぱいになったら出社できないように
アルバイトのクラス(Colleague)
クラス内でのちに登場するMediatorクラスを保持しています。
arriveAtWork(出社)メソッドを呼ぶことで、Mediator側にWokerクラスの状態判断を委ねます。
つまり、このクラス内で他のオブジェクトとの関係性を気にする必要がなくなるという事です。
class Worker {
private $name = "";
private $goodCookFlg = 0;
private $workerMediator = null;
public function __construct($name, $goodCookFlg)
{
$this->name = $name;
$this->goodCookFlg = $goodCookFlg;
}
public function setWorkerMediator($workerMediator)
{
$this->workerMediator = $workerMediator;
}
public function getRole()
{
return $this->workerMediator->getRole($this->name);
}
public function getName()
{
return $this->name;
}
public function getGoodCookFlg()
{
return $this->goodCookFlg;
}
}
Mediatorクラス
このMediatorクラスで各Wokerクラスの状態を判断し結果を指定しています。
(コンパクトさに欠ける形となってしまいましたが、、^^;)
先にも記述したように、Mediator側で各クラスの状態に応じた判断を行うため、
Wokerクラス内で他のオブジェクトとの関係性を気にせず、すべての判断をこのMediatorクラス内に集約できます。
class WorkerMediator {
const KITCHEN = 1;
const FLOOR = 2;
const WORKER_CAPACITY = 4;
private $workersInfo = array();
public function arriveAtWork($worker)
{
$worker->setWorkerMediator($this);
$this->divisionOfRoles($worker->getName(), $worker->getGoodCookFlg());
}
// 他オブジェクトの状態に応じて役割分担
private function divisionOfRoles($name, $goodCookFlg)
{
if (count($this->workersInfo) >= self::WORKER_CAPACITY) {
return 0;
}
if (count($this->workersInfo) == 0) {
if ($goodCookFlg) {
$this->workersInfo[$name] = self::KITCHEN;
} else {
$this->workersInfo[$name] = self::FLOOR;
}
} else {
$counts = array_count_values($this->workersInfo);
if ($goodCookFlg) {
if ($counts[self::KITCHEN] >= self::WORKER_CAPACITY - 1) {
$kitchenArray = array_keys($this->workersInfo, self::KITCHEN);
$this->workersInfo[$kitchenArray[0]] = self::FLOOR;
$this->workersInfo[$name] = self::KITCHEN;
} else {
$this->workersInfo[$name] = self::KITCHEN;
}
} else {
if (!in_array(self::KITCHEN, $this->workersInfo)) {
$this->workersInfo[$name] = self::KITCHEN;
} elseif (!in_array(self::FLOOR, $this->workersInfo)) {
$this->workersInfo[$name] = self::FLOOR;
} elseif ($counts[self::KITCHEN] > $counts[self::FLOOR]) {
$this->workersInfo[$name] = self::FLOOR;
} else {
$this->workersInfo[$name] = self::KITCHEN;
}
}
}
}
public function getRole($name)
{
if (!array_key_exists($name, $this->workersInfo)) {
return $name ."さんはシフトがいっぱいなので出社できません。\n";
} elseif ($this->workersInfo[$name] == self::FLOOR) {
return $name . "さんはフロアです。\n";
} else {
return $name . "さんはキッチンです。\n";
}
}
}
使ってみる
5人のアルバイトが順番に出社
料理できる人にはフラグが立っている
$workerMediator = new WorkerMediator();
$tanaka = new Worker('田中', 1);
$sato = new Worker('佐藤', 1);
$kato = new Worker('加藤', 0);
$tsuji = new Worker('辻', 1);
$kusunoki = new Worker('楠木', 1);
// 出社
$workerMediator->arriveAtWork($tanaka);
$workerMediator->arriveAtWork($sato);
$workerMediator->arriveAtWork($kato);
$workerMediator->arriveAtWork($tsuji);
$workerMediator->arriveAtWork($kusunoki);
echo $tanaka->getRole();
echo $sato->getRole();
echo $kato->getRole();
echo $tsuji->getRole();
echo $kusunoki->getRole();
/*結果
田中さんはキッチンです。
佐藤さんはキッチンです。
加藤さんはフロアです。
辻さんはキッチンです。
楠木さんはシフトがいっぱいなので出社できません。
*/
まとめ
・Mediatorパターンは、各Colleagueクラス間での相互関係などによるやり取りをすべて1点に集約することで全体の構造をシンプルに、かつオブジェクト同士の依存関係を削減できる。
・サンプルコードにて、出社したのに入れないのは仕様として問題ありですね。。楠木さんごめんなさい。。