本文目录导读:

装饰器模式(Decorator Pattern)允许你动态地给一个对象添加新的功能,同时不改变其原有的结构,它通过创建一个包装对象(装饰器)来包裹原始对象。
下面我通过一个咖啡点单系统的例子来展示装饰器模式的核心用法。
核心组件(接口和基础类)
定义一个所有咖啡都必须实现的接口,以及一个基础的咖啡类。
<?php
// 1. 组件接口:定义了所有咖啡的基本操作
interface Coffee
{
public function getDescription(): string;
public function getCost(): float;
}
// 2. 具体组件:基础的浓缩咖啡(被装饰的对象)
class Espresso implements Coffee
{
public function getDescription(): string
{
return '浓缩咖啡';
}
public function getCost(): float
{
return 20.00;
}
}
// 另一个基础组件:美式咖啡
class Americano implements Coffee
{
public function getDescription(): string
{
return '美式咖啡';
}
public function getCost(): float
{
return 25.00;
}
}
抽象装饰器(基类)
装饰器基类实现 Coffee 接口,并持有一个 Coffee 对象的引用,它的作用是将请求转发给被包裹的对象。
<?php
// 3. 抽象装饰器:所有调料(装饰器)的基类
abstract class CoffeeDecorator implements Coffee
{
protected Coffee $coffee;
public function __construct(Coffee $coffee)
{
$this->coffee = $coffee;
}
// 默认实现:转发给被包裹的咖啡
public function getDescription(): string
{
return $this->coffee->getDescription();
}
public function getCost(): float
{
return $this->coffee->getCost();
}
}
具体装饰器(各种调料)
每个具体装饰器都继承自 CoffeeDecorator,并重写父类的方法,在原有功能上增加新的描述和价格。
<?php
// 4. 具体装饰器A:牛奶
class Milk extends CoffeeDecorator
{
public function getDescription(): string
{
return $this->coffee->getDescription() . ',加牛奶';
}
public function getCost(): float
{
return $this->coffee->getCost() + 5.00;
}
}
// 具体装饰器B:糖
class Sugar extends CoffeeDecorator
{
public function getDescription(): string
{
return $this->coffee->getDescription() . ',加糖';
}
public function getCost(): float
{
return $this->coffee->getCost() + 2.00;
}
}
// 具体装饰器C:奶泡(拿铁风格)
class Whip extends CoffeeDecorator
{
public function getDescription(): string
{
return $this->coffee->getDescription() . ',加奶泡';
}
public function getCost(): float
{
return $this->coffee->getCost() + 3.00;
}
}
客户端使用(动态组合)
这是装饰器模式最核心的用法:运行时自由组合装饰器,而无需修改现有类。
<?php // 引入所有文件(此处假设已自动加载) // 1. 点一杯基础的浓缩咖啡 $coffee = new Espresso(); echo $coffee->getDescription() . ':' . $coffee->getCost() . '元<br>'; // 输出:浓缩咖啡:20元 // 2. 动态地加牛奶和糖 $coffeeWithMilk = new Milk($coffee); $coffeeWithMilkAndSugar = new Sugar($coffeeWithMilk); echo $coffeeWithMilkAndSugar->getDescription() . ':' . $coffeeWithMilkAndSugar->getCost() . '元<br>'; // 输出:浓缩咖啡,加牛奶,加糖:27元 // 3. 换一种点法:直接嵌套(先加糖,再加奶泡) $coffee2 = new Espresso(); $decorated = new Whip(new Sugar($coffee2)); echo $decorated->getDescription() . ':' . $decorated->getCost() . '元<br>'; // 输出:浓缩咖啡,加糖,加奶泡:25元 // 4. 装饰器也可以用于其他基础咖啡(美式) $americanoWithEverything = new Whip(new Sugar(new Milk(new Americano()))); echo $americanoWithEverything->getDescription() . ':' . $americanoWithEverything->getCost() . '元<br>'; // 输出:美式咖啡,加牛奶,加糖,加奶泡:35元
模式总结
装饰器模式的关键点:
- 动态性:装饰器可以在运行时添加,而不像继承那样在编译时固定。
- 组合优于继承:你可以通过任意顺序组合装饰器,产生多样化的行为(奶+糖、糖+奶泡、奶+糖+奶泡)。
- 透明性:装饰器实现了与被装饰对象相同的接口,客户端不需要知道它正在使用装饰器。
在PHP中的实际应用场景:
- 中间件(如 Laravel 中间件):一层层包裹请求处理逻辑。
- 日志系统:为文件日志添加时间戳、加密、压缩等功能。
- 前端控制器:为请求添加缓存、权限验证等。
对比继承:如果使用继承,你需要为“浓缩+牛奶”、“浓缩+糖”、“浓缩+牛奶+糖”、“美式+牛奶+糖”等分别创建子类,这会导致类爆炸,装饰器模式通过组合解决了这个问题。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播