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