裝飾器模式

原文地址(https://www.awaimai.com/patterns/decorator

優點:解決子類爲類擴展的一個功能。

例子:

假設我們有一個郵件內容模板類,如下,一般情況下我們都用這個模板發送郵件:

class emailBody
{
    public function body()
    {
        echo "公司準備爲您加薪50%。\n";
    }
}

現在元旦準備來了,我們想在郵件模板上加一些元旦快樂的祝福語。

這時,我們可以直接修改emailBody類,但是這樣實在不好。

所以,選擇用子類繼承方式來實現:

class newYearEmail extends emailBody
{
    public function body()
    {
        echo '元旦快樂!!!';
        parent::body();
    }
}

$email = new newYearEmail();
$email->body();

再過一個多月,春節來了,我們又可以按照元旦的做法,添加一個springFestivalEmail類。

可是,如果想在不修改原來類的基礎上,同時祝福元旦快樂和春節快樂呢?

這時我們不禁會想,這種情況繼承方式是否好用。

解決方案:

創建一個接口,用以規範郵件類:

/**
 * 郵件內容接口,規範實現類
 */
interface EmailBody
{
    public function body();
}

然後是正常的郵件內容類,我們用裝飾器的目的就是,在某些情況下不改變其代碼,也能得到不同的結果。

/**
 * 正常郵件內容類
 */
class MainEmail implements EmailBody
{
    public function body()
    {
        echo "公司準備爲您加薪50%。\n";
    }
}

然後是主裝飾器類,這個類用屬性保存MainEmail類的對象,然後根據需要改變它的行爲。

/**
 * 郵件內容裝飾器類
 *
 */
abstract class EmailBodyDecorator implements EmailBody
{
    // 保存MainEmail類對象
    protected $emailBody;

    // 實例化這個類或者子類時,必須傳入一個被修飾的對象
    public function __construct(EmailBody $emailBody)
    {
        $this->emailBody = $emailBody;
    }

    // 用抽象方法聲明EmailBody規定的方法,
    // 在子類中用來改變MainEmail對象的行爲
    abstract public function body();
}

然後我們定義兩個裝飾器的子類,在這兩個子類裏面我們改變原MainEmail的行爲:

class NewYearEmail extends EmailBodyDecorator
{
    public function body()
    {
        echo '元旦快樂!';
        $this->emailBody->body();
    }
}

class SpringFestivalEmail extends EmailBodyDecorator
{
    public function body()
    {
        echo '春節快樂!';
        $this->emailBody->body();
    }
}

客戶端使用:

/**
 * 【正常】發送郵件
 * 輸出: 公司準備爲您加薪50%。
 */
$email = new MainEmail();
$email->body();

/**
 * 發送有【元旦】祝福的郵件
 * 輸出: 元旦快樂!公司準備爲您加薪50%。
 */
$emailNewYear = new NewYearEmail($email);
$emailNewYear->body();

/**
 * 發送有【春節】祝福的郵件
 * 輸出: 春節快樂!公司準備爲您加薪50%。
 */
$emailSpring = new SpringFestivalEmail($email);
$emailSpring->body();

/**
 * 發送同時有【元旦】和【春節】祝福的郵件
 * 輸出: 春節快樂!元旦快樂!公司準備爲您加薪50%。
 */
$emailTwo = new SpringFestivalEmail($emailNewYear);
$emailTwo->body();

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章