简短描述
当前有一个功能完善的对象,如果我们想要给这个对象添加一个新的职责,那么我们可以用一个新的类去装饰它来实现对原有对象职责的扩展。新的类称为“装饰者”,原有的对象称为“被装饰者”。这种模式被称为装饰器模式。
现实生活中的装饰器模式
装饰器模式在现实生活中的例子简直太多了。比如我开了一个奶茶店,卖的是普通的奶茶。现在我想引入一个叫珍珠奶茶的商品,我要怎么做呢?
我是不是需要升级一下我的制作奶茶的机器,让它支持珍珠奶茶的做法?但这种成本估计比较高,说不定还没原来做奶茶的机器好用呢!实际上我只需要买一个能做“珍珠”的机器就行!把“珍珠”放进奶茶不就成了珍珠奶茶。原有的做奶茶的机器依然可以稳定地制作做奶茶,也避免花成本去学习操作新的机器。
在这个案例中,珍珠就是装饰者,奶茶就是被装饰者。
很容易就写出一个上面所描述的装饰器模式的代码。 首先定义一个饮料接口
interface Drink { public function getPrice(); }
我们现在已经在卖奶茶了,下面是一个奶茶的类。价格已经订好了,10元!
class MilkTea implements Drink { public function getPrice() { return 10; } }
接着有一个新的品种,叫珍珠奶茶,我只要写一个珍珠的类,然后装饰一下奶茶,这个奶茶就成了珍珠奶茶。
class Boba implements Drink { public function __construct($MilkTea) { $this->MilkTea = $MilkTea; } public function getPrice() { return $this->MilkTea->getPrice() + 5; } }
看看珍珠奶茶是怎么制造出来的
// 先把奶茶做出来 $MilkTea = new MilkTea(); // 用珍珠去装饰这个奶茶,变成了波霸奶茶 $Boba = new Boba($MilkTea); // 看看现在波霸奶茶的价格 echo $Boba->getPrice();
装饰模式使用起来还是非常容易的。在实际应用中,装饰器模式使用场景也十分广泛。
装饰器应用场景一:参数验证
api中参数验证是必不可少的,我们会选择一些已经很成熟的参数验证器,假如现在有一个非常成熟的参数验证器叫validator,我们现在用的也非常好。
突然有一天,一个需求说接口需要增加一个签名验证,然而现有的验证器不支持该功能,怎么解决呢?
第一反应可能是直接修改这个验证器,让其增加签名验证功能,但这样可不是个很好的解决方案。一个是直接修改会引入风险,一个是如果后续有更多需求,这个稳定的验证器可能会被改的一团糟,还有就是增加一个业务的需求可能让这个验证器变得有点“多事”了…
这个时候装饰器就派上用场了,不修改原有的验证器,用一个新的验证器去装饰原有的验证器就好啦!新的验证器就是为当前业务定制的专有验证器,而原来的验证器也保持了它只该有的功能。
装饰器应用场景二:日志记录
现在有一个使用了很久的日志记录类,代码写的还行,使用也很稳定,但是功能比较单一,仅仅是把日志写入一个文件就完事了。
我突发奇想想让这个日志类能够自动实现根据日期归档,这个时候我就可以写一个装饰器,这个装饰器实现了文件归档的功能,去装饰日志记录类,装饰之后的日志记录类就成为了一个带有文件归档的日志记录类。
你可能还喜欢下面这些文章
赞赏微信赞赏
支付宝赞赏
应用场景1:现成的Filter责任链模式。
应用场景2:现成的AOP代理模式。