iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 19
1

本文同步發佈於blog

情境:以下是某才藝表演

<?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;
    }
}

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


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

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

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

  • 首先定義表演的介面
<?php

namespace App\AdapterPattern\TalentShow\Contracts;

interface ShowInterface
{
    public function show();
}
  • 接著實作鋼琴表演者的轉接頭
<?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();
    }
}

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


  • 再來是笑話表演者的轉接頭
<?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();
    }
}

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


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

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


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

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

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

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

最後附上類別圖:
https://ithelp.ithome.com.tw/upload/images/20201003/20111630eewkvivYC7.png
(註:若不熟悉 UML 類別圖,可參考UML類別圖說明。)

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


上一篇
Day18. 轉接頭模式
下一篇
Day20. 工廠方法模式
系列文
你終究都要學設計模式的,那為什麼不一開始就學呢?57
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言