原文地址(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();