装饰器模式又称:装饰者模式、Wrappe、Decorator。装饰是一种结构型设计模式,允许你通过将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为。
装饰器模式就如生活中的装饰或者配料一样,一级一级包装。就拿长沙最火的 茶颜悦色 奶茶举例吧,茶颜悦色的奶茶有原味奶茶:幽兰拿铁(特色)、声声乌龙、三季生椰 等,大部分奶茶都可以通过加价进行加料,加料类型有:奶油、碧根果碎、开心果碎 等,现实生活中我们可以买一杯奶茶进行多次加料,也可不加料,甚至可以把同一种料加三次,在软件开发中我们能很简单通过继承实现。
装饰模式是为已有的类动态添加更多功能,而且不改动原来的类基础上,使用 关联替代继承。
上述情况我们需要更改一个对象的行为时,第一个跳入脑海的想法就是扩展它所属的类。
但是,不能忽视继承可能引发的几个严重问题:
前面我们介绍了 组合模式 和 适配器模式 , 都是利用了设计原则中 组合优于继承的意识,在装饰器模式中也不例外。我们可以将包含多个指向其他对象的引用, 并将各种工作 委派给引用对象。
MilkTea:原本的对象和装饰共同的接口 示例中指:奶茶
Oolong、Latte: 原本的对象 示例中指:声声乌龙、幽兰拿铁
Decorator: 实现接口的装饰抽象类
Cream、…:具体的装饰 示例中:奶油、碧根果、开心果
/**
* 奶茶
*/
interface MilkTea
{
/**
* 名称
* @return mixed
*/
public function name();
/**
* 价格
* @return mixed
*/
public function price();
}
PHP
Copy
声声乌龙
/**
* 声声乌龙
*/
class Oolong implements MilkTea
{
public function name()
{
return '声声乌龙';
}
public function price()
{
return 16;
}
}
PHP
Copy
幽兰拿铁
/**
* 幽兰拿铁
*/
class Latte implements MilkTea
{
public function name()
{
return '幽兰拿铁';
}
public function price()
{
return 17;
}
}
PHP
Copy
class Decorator implements MilkTea
{
protected $milkTea;
public function __construct(MilkTea $milkTea)
{
$this->milkTea = $milkTea;
}
public function name()
{
if ($this->milkTea != null) {
return $this->milkTea->name();
}
return '';
}
public function price()
{
if ($this->milkTea != null) {
return $this->milkTea->price();
}
return 0;
}
}
PHP
Copy
奶油
/**
* 奶油
*/
class Cream extends Decorator
{
public function name()
{
return $this->milkTea->name() . '+ 奶油';
}
public function price()
{
return $this->milkTea->price() + 4;
}
}
PHP
Copy
碧根果
/**
* 碧根果
*/
class Pecan extends Decorator
{
public function name()
{
return $this->milkTea->name() . '+ 碧根果';
}
public function price()
{
return $this->milkTea->price() + 2;
}
}
PHP
Copy
开心果
/**
* 开心果
*/
class Pistachio extends Decorator
{
public function name()
{
return $this->milkTea->name() . '+ 开心果';
}
public function price()
{
return $this->milkTea->price() + 4;
}
}
PHP
Copy
/**
* 我点一杯 幽兰拿铁+ 奶油+ 开心果
*/
$latte = new Latte();
$cream = new Cream($latte);
$pistachio = new Pistachio($cream);
echo $pistachio->name();
echo ' ' . $pistachio->price() . '元' . PHP_EOL;
/**
* 点一杯加三个奶油的声声乌龙(因为我比较喜欢喝奶油)
*/
$oolong = new Oolong();
$cream = new Cream($oolong);
$cream = new Cream($cream);
$cream = new Cream($cream);
echo $cream->name();
echo ' ' . $cream->price() . '元' . PHP_EOL;
PHP
Copy
输出
幽兰拿铁+ 奶油+ 开心果 25元
声声乌龙+ 奶油+ 奶油+ 奶油 28元
学过 建造者模式 的朋友可能会有疑问,这个怎么不直接用 建造者模式,其实 建造者模式 强调的是按照顺序稳定执行,而 装饰器模式 则可以自由灵活组合,比如像前面例子,我可以一杯奶茶点三个料。