PHP装饰器模式举例

wen PHP项目 2

本文目录导读:

PHP装饰器模式举例

  1. 核心组件(接口和基础类)
  2. 抽象装饰器(基类)
  3. 具体装饰器(各种调料)
  4. 客户端使用(动态组合)
  5. 模式总结

装饰器模式(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元

模式总结

装饰器模式的关键点:

  1. 动态性:装饰器可以在运行时添加,而不像继承那样在编译时固定。
  2. 组合优于继承:你可以通过任意顺序组合装饰器,产生多样化的行为(奶+糖、糖+奶泡、奶+糖+奶泡)。
  3. 透明性:装饰器实现了与被装饰对象相同的接口,客户端不需要知道它正在使用装饰器。

在PHP中的实际应用场景:

  • 中间件(如 Laravel 中间件):一层层包裹请求处理逻辑。
  • 日志系统:为文件日志添加时间戳、加密、压缩等功能。
  • 前端控制器:为请求添加缓存、权限验证等。

对比继承:如果使用继承,你需要为“浓缩+牛奶”、“浓缩+糖”、“浓缩+牛奶+糖”、“美式+牛奶+糖”等分别创建子类,这会导致类爆炸,装饰器模式通过组合解决了这个问题。

抱歉,评论功能暂时关闭!