範例:打招呼(中介者模式)

Pattern: 中介者模式

Class Diagram: 打招呼


情境:以下是人腦的運作程式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<?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)

  • 使用中樞神經系統作為中介者介面 (Mediator)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

namespace App\MediatorPattern\SayHello\Contracts;

interface CentralNervousSystem
{
/**
* @param string $organName
* @param string $message
* @return string
*/
public function sendMessage($organName, $message);
}

  • 使用器官作為合作者介面 (Colleague)
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php

namespace App\MediatorPattern\SayHello\Contracts;

interface Executable
{
/**
* @param string $message
* @return string
*/
public function execute($message);
}


需求二:定義實體中介者,來改變合作者間的依賴關係

  • 實作大腦(中介者)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?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;
}
}

  • 抽象器官(合作者)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?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;
}
}
  • 實作眼睛 (合作者)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?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;
}
}
}

  • 實作耳朵 (合作者)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?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;
}
}
}

  • 實作手 (合作者)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?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]的動作";
}
}

  • 實作嘴巴 (合作者)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?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]的聲音";
}
}

  • 實作臉 (合作者)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?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器官是誰(鬆耦合)。


需求三:改寫既有程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
<?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);
}
}

[單一職責原則]
我們將器官的功能器官間的關係視作兩種不同的職責。

藉由大腦(中介者)負責聯絡各個器官(合作者)執行對應的行為。

[開放封閉原則]
無論是新增/修改器官,或者新增/修改器官間的關係,
我們都不會改動到所有程式碼。

[介面隔離原則]
中介者介面:負責傳送器官間的訊息。
合作者介面:負責執行該器官的功能。

[依賴反轉原則]
大腦依賴於合作者介面。
器官負責實作合作者介面。

ʕ •ᴥ•ʔ:早上剛睡醒時,想到的範例。