本文同步更新於blog
情境:以下是人腦的運作程式
<?php
namespace App\MediatorPattern\SayHello;
class Program
{
/**
* @param string $item
* @return string
*/
public function see($item)
{
switch ($item) {
case '認識的人':
return $this->sayHello();
break;
case '熟識的人':
return $this->waveHand();
break;
}
}
/**
* @param string $item
* @return string
*/
public function hear($item)
{
switch ($item) {
case '喜歡的人':
return $this->blush();
break;
case '討厭的人':
return $this->pretendToLookBusy();
break;
}
}
private function sayHello()
{
return '[嘴巴]發出[你好]的聲音';
}
private function waveHand()
{
return '[手]做出[揮手]的動作';
}
private function blush()
{
return '[臉]開始[發紅]';
}
private function pretendToLookBusy()
{
return '[手]做出[裝忙]的動作';
}
}
隨著行為日趨複雜,我們可能會有更多的動作。
這些動作會聯繫著不同的器官。
因為強耦合,無論是器官的增加或是行為改變,
都會大大地影響既有程式碼。
讓我們用中介者模式改造它!
需求一:定義中介者介面 (Mediator)與合作者介面 (Colleague)
<?php
namespace App\MediatorPattern\SayHello\Contracts;
interface CentralNervousSystem
{
/**
* @param string $organName
* @param string $message
* @return string
*/
public function sendMessage($organName, $message);
}
<?php
namespace App\MediatorPattern\SayHello\Contracts;
interface Executable
{
/**
* @param string $message
* @return string
*/
public function execute($message);
}
需求二:定義實體中介者,來改變合作者間的依賴關係
<?php
namespace App\MediatorPattern\SayHello;
use App\MediatorPattern\SayHello\Contracts\CentralNervousSystem;
use App\MediatorPattern\SayHello\Abstracts\Organ;
class Brain implements CentralNervousSystem
{
/**
* @var Organ[]
*/
protected $organs = [];
/**
* @param string $organName
* @param string $message
* @return string
*/
public function sendMessage($organName, $message)
{
$organ = $this->organs[$organName];
return $organ->execute($message);
}
public function setOrgan(Organ $organ)
{
$organName = $organ->getName();
$this->organs[$organName] = $organ;
}
}
<?php
namespace App\MediatorPattern\SayHello\Abstracts;
use App\MediatorPattern\SayHello\Contracts\Executable;
use App\MediatorPattern\SayHello\Brain;
abstract class Organ implements Executable
{
/**
* @var string
*/
protected $name = 'Unknown';
/**
* @var Brain
*/
protected $brain;
public function __construct(Brain $brain)
{
$this->brain = $brain;
}
public function getName()
{
return $this->name;
}
}
<?php
namespace App\MediatorPattern\SayHello;
use App\MediatorPattern\SayHello\Abstracts\Organ;
class Eye extends Organ
{
/**
* @var string
*/
protected $name = '眼睛';
/**
* @param string $message
* @return string
*/
public function execute($message)
{
switch ($message) {
case '認識的人':
return $this->brain->sendMessage('嘴巴', '你好');
break;
case '熟識的人':
return $this->brain->sendMessage('手', '揮手');
break;
}
}
}
<?php
namespace App\MediatorPattern\SayHello;
use App\MediatorPattern\SayHello\Abstracts\Organ;
class Ear extends Organ
{
/**
* @var string
*/
protected $name = '耳朵';
/**
* @param string $message
* @return string
*/
public function execute($message)
{
switch ($message) {
case '喜歡的人':
return $this->brain->sendMessage('臉', '發紅');
break;
case '討厭的人':
return $this->brain->sendMessage('手', '裝忙');
break;
}
}
}
<?php
namespace App\MediatorPattern\SayHello;
use App\MediatorPattern\SayHello\Abstracts\Organ;
class Hand extends Organ
{
protected $name = '手';
/**
* @param string $message
* @return string
*/
public function execute($message)
{
return "[手]做出[$message]的動作";
}
}
<?php
namespace App\MediatorPattern\SayHello;
use App\MediatorPattern\SayHello\Abstracts\Organ;
class Mouth extends Organ
{
/**
* @var string
*/
protected $name = '嘴巴';
/**
* @param string $message
* @return string
*/
public function execute($message)
{
return "[嘴巴]發出[$message]的聲音";
}
}
<?php
namespace App\MediatorPattern\SayHello;
use App\MediatorPattern\SayHello\Abstracts\Organ;
class Face extends Organ
{
/**
* @var string
*/
protected $name = '臉';
/**
* @param string $message
* @return string
*/
public function execute($message)
{
return "[臉]開始[$message]";
}
}
以這個範例來說,中介者是大腦,合作者則是各個器官。
當A器官要呼叫B器官,執行某些動作時,
會透過大腦,使得A器官不必知道真正的B器官是誰(鬆耦合)。
需求三:改寫既有程式碼
<?php
namespace App\MediatorPattern\SayHello;
use App\MediatorPattern\SayHello\Brain;
use App\MediatorPattern\SayHello\Eye;
use App\MediatorPattern\SayHello\Mouth;
use App\MediatorPattern\SayHello\Hand;
use App\MediatorPattern\SayHello\Ear;
use App\MediatorPattern\SayHello\Face;
class Program
{
/**
* @var Brain
*/
protected $brain;
/**
* @var Eye
*/
protected $eye;
/**
* @var Mouth
*/
protected $mouth;
/**
* @var Hand
*/
protected $hand;
/**
* @var Ear
*/
protected $ear;
/**
* @var Face
*/
protected $face;
public function __construct()
{
$this->brain = $this->resolveBrainAndOrgans();
}
/**
* @param string $item
* @return string
*/
public function see($item)
{
return $this->eye->execute($item);
}
/**
* @param string $item
* @return string
*/
public function hear($item)
{
return $this->ear->execute($item);
}
private function resolveBrainAndOrgans()
{
$this->brain = new Brain();
$this->resolveOrgans();
$this->brain->setOrgan($this->eye);
$this->brain->setOrgan($this->mouth);
$this->brain->setOrgan($this->hand);
$this->brain->setOrgan($this->ear);
$this->brain->setOrgan($this->face);
}
private function resolveOrgans()
{
$this->eye = new Eye($this->brain);
$this->mouth = new Mouth($this->brain);
$this->hand = new Hand($this->brain);
$this->ear = new Ear($this->brain);
$this->face = new Face($this->brain);
}
}
[單一職責原則]
我們將器官的功能與器官間的關係視作兩種不同的職責。
藉由大腦(中介者)負責聯絡各個器官(合作者)執行對應的行為。
[開放封閉原則]
無論是新增/修改器官,或者新增/修改器官間的關係,
我們都不會改動到所有程式碼。
[介面隔離原則]
中介者介面:負責傳送器官間的訊息。
合作者介面:負責執行該器官的功能。
[依賴反轉原則]
大腦依賴於合作者介面。
器官負責實作合作者介面。
最後附上類別圖:
(註:若不熟悉 UML 類別圖,可參考UML類別圖說明。)
ʕ •ᴥ•ʔ:早上剛睡醒時,想到的範例。