範例:才藝表演(轉接頭模式)

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
<?php

namespace App\AdapterPattern\TalentShow;

class Program
{
/**
* @return array
*/
public function run()
{
$result = [];

$pianoPlayer = new PianoPlayer();
$result[] = $pianoPlayer->play();

$jokeTeller = new JokeTeller();
$result[] = $jokeTeller->tell();

return $result;
}
}

主持人覺得需要知道,每位表演者的表演方式,有些麻煩。
希望我們定義一個統一的介面,讓他的主持工作能更順利。


需求一:定義表演介面,使得表演項目能夠被管理

隨著項目變多,每個人的表演都不一樣。
可能會有彈鋼琴、拉扯鈴、講笑話、演話劇等。

但是這些項目,未必都是為了才藝表演而存在
讓我們以此為基礎,切分兩者的職責。

  • 首先定義表演的介面
1
2
3
4
5
6
7
8
<?php

namespace App\AdapterPattern\TalentShow\Contracts;

interface ShowInterface
{
public function show();
}
  • 接著實作鋼琴表演者的轉接頭
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php

namespace App\AdapterPattern\TalentShow\Adapter;

use App\AdapterPattern\TalentShow\PianoPlayer;
use App\AdapterPattern\TalentShow\Contracts\ShowInterface;

class PianoPlayerAdapter implements ShowInterface
{
/**
* @var PianoPlayer
*/
protected $pianoPlayer;

public function __construct(PianoPlayer $pianoPlayer)
{
$this->pianoPlayer = $pianoPlayer;
}

public function show()
{
return $this->pianoPlayer->play();
}
}

透過這個轉接頭,
當我們請鋼琴表演者表演時,
他就會開始鋼琴。


  • 再來是笑話表演者的轉接頭
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php

namespace App\AdapterPattern\TalentShow\Adapter;

use App\AdapterPattern\TalentShow\Contracts\ShowInterface;
use App\AdapterPattern\TalentShow\JokeTeller;

class JokeTellerAdapter implements ShowInterface
{
/**
* @var JokeTeller
*/
protected $jokeTeller;

public function __construct(JokeTeller $jokeTeller)
{
$this->jokeTeller = $jokeTeller;
}

public function show()
{
return $this->jokeTeller->tell();
}
}

透過這個轉接頭,
當我們請笑話表演者表演時,
他就會開始笑話。


  • 最後是修改原本的程式
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
<?php

namespace App\AdapterPattern\TalentShow;

use App\AdapterPattern\TalentShow\Adapter\PianoPlayerAdapter;
use App\AdapterPattern\TalentShow\Adapter\JokeTellerAdapter;
use App\AdapterPattern\TalentShow\Contracts\ShowInterface;

class Program
{
/**
* @var ShowInterface[]
*/
protected $performers = [];

/**
* @return array
*/
public function run()
{
$this->preparePerformers();

$result = [];
foreach ($this->performers as $performer) {
$result[] = $performer->show();
}

return $result;
}

private function preparePerformers()
{
$pianoPlayer = new PianoPlayer();
$this->performers[] = new PianoPlayerAdapter($pianoPlayer);

$jokeTeller = new JokeTeller();
$this->performers[] = new JokeTellerAdapter($jokeTeller);
}
}

透過各種實作表演介面的轉接頭,我們便能更好地管理表演項目了。
(比如:進退場的流程、項目的介紹等)


[單一職責原則]
我們將才藝類別表演項目類別視作兩種不同的職責。

[開放封閉原則]
若我們需要在表演時,增加新的表演者,可以不修改原本才藝類別的程式碼。
只需新增一個對應的轉接頭

[介面隔離原則]
才藝類別表演項目類別實作的是兩個不同的接口。
透過轉接頭,讓原本接口不同的類別能夠介接。

[依賴反轉原則]
客戶端的程式碼依賴抽象的表演介面
轉接頭實作抽象的表演介面

ʕ •ᴥ•ʔ:除了上述包裹類別的方式外,轉接頭還能透過多重繼承的方式實現。
將來撰寫其他語言範例時,再來補充。