PHP项目装饰器与适配器

wen PHP项目 168

本文目录导读:

PHP项目装饰器与适配器

  1. 核心定义
  2. PHP 项目中的典型场景
  3. 关键区别(面试常问)
  4. 如何在 PHP 项目中做选择?
  5. 常见陷阱

在 PHP 项目中,装饰器模式 和 适配器模式 都是结构型设计模式,但它们的目的和使用场景有本质区别。

下面从核心定义、PHP 示例、典型场景以及关键区别来详细说明。


核心定义

  • 适配器模式(Adapter):

    • 目标: 让原本不兼容的接口能够协同工作,它把一个类的接口转换成客户端期望的另一个接口。
    • 本质: “接口转换器” 或 “封装器”。
    • 关注点: 解决接口不匹配问题,重写、适配已有接口。
  • 装饰器模式(Decorator):

    • 目标: 动态地给一个对象添加额外的职责或行为,而无需修改其原始类。
    • 本质: “包装器” + “增强器”。
    • 关注点: 解决功能扩展问题,不改变接口,只增加行为。

PHP 项目中的典型场景

适配器 —— 集成第三方支付

假设你的系统设计了一个统一的支付接口 PaymentInterface,但你要集成的支付宝 SDK 使用的是完全不同的一套方法。

<?php
// --- 你的系统标准接口 ---
interface PaymentInterface {
    public function pay(float $amount): void;
}
// --- 系统内部类(使用标准接口) ---
class OrderProcessor {
    public function processOrder(PaymentInterface $payment, float $amount) {
        // 这里只依赖 PaymentInterface
        $payment->pay($amount);
    }
}
// --- 第三方支付宝类(不兼容的接口) ---
class AlipaySDK {
    public function sendPayment(string $orderId, float $totalAmount): string {
        // 实际调用支付宝 API
        echo "支付宝支付:订单 {$orderId},金额 {$totalAmount}\n";
        return "success";
    }
}
// --- 适配器:将 AlipaySDK 转换成 PaymentInterface ---
class AlipayAdapter implements PaymentInterface {
    private AlipaySDK $alipay;
    public function __construct(AlipaySDK $alipay) {
        $this->alipay = $alipay;
    }
    public function pay(float $amount): void {
        // 适配:转换方法名和参数
        $orderId = 'ORD' . uniqid();
        $this->alipay->sendPayment($orderId, $amount);
    }
}
// --- 使用 ---
$alipay = new AlipaySDK();
$adapter = new AlipayAdapter($alipay);
$processor = new OrderProcessor();
$processor->processOrder($adapter, 99.99); // 输出:支付宝支付:订单...,金额 99.99

装饰器 —— 消息通知增强

假设你有一个基础的消息发送器,现在希望在发送消息前/后进行一些增强(如记录日志、添加签名、或者推送渠道切换)。

<?php
// --- 基础接口 ---
interface NotifierInterface {
    public function send(string $message): void;
}
// --- 核心组件 ---
class BasicNotifier implements NotifierInterface {
    public function send(string $message): void {
        echo "发送消息: {$message}\n";
    }
}
// --- 基础装饰器(抽象类,也可以直接类实现接口) ---
abstract class NotifierDecorator implements NotifierInterface {
    protected NotifierInterface $wrapped;
    public function __construct(NotifierInterface $notifier) {
        $this->wrapped = $notifier;
    }
    // 抽象类中通常不重写 send,交由子类实现
    abstract public function send(string $message): void;
}
// --- 具体装饰器 1:添加日志 ---
class LoggingDecorator extends NotifierDecorator {
    public function send(string $message): void {
        echo "[日志] 记录发送操作: " . date('Y-m-d H:i:s') . "\n";
        $this->wrapped->send($message);
    }
}
// --- 具体装饰器 2:添加签名 ---
class SignatureDecorator extends NotifierDecorator {
    public function send(string $message): void {
        $signedMessage = $message . "\n--- 由系统自动生成 ---";
        $this->wrapped->send($signedMessage);
    }
}
// --- 使用 ---
$notifier = new BasicNotifier();
// 先加日志,再加签名
$notifier = new LoggingDecorator($notifier);
$notifier = new SignatureDecorator($notifier);
$notifier->send("Hello World");
// 输出:
// [日志] 记录发送操作: 2025-03-25 10:00:00
// 发送消息: Hello World
// --- 由系统自动生成 ---

关键区别(面试常问)

特征 适配器模式 装饰器模式
目的 接口转换,让不兼容的类协作 功能增强,动态添加新职责
输入/输出 输入是旧接口,输出是新接口 输入和输出接口相同(不改变签名)
对象数量 通常一对一(一个适配器适配一个被适配者) 可以层层嵌套(多个装饰器叠加)
包装关系 包装后的对象通常不再具备原始类型(只暴露目标接口) 包装后的对象仍然是原始类型(符合 Liskov 替换原则)
典型应用 集成第三方库、老系统改造 日志、缓存、权限、AOP 横向切面

如何在 PHP 项目中做选择?

情况 推荐模式
有一个旧类(如 OldLogger),你需要让它符合系统新的 LoggerInterface ✅ 适配器
有一个 HttpClient,你想在每次请求前自动添加认证 Token(不影响原接口) ✅ 装饰器
集成微信支付,其回调格式与系统要求不一样 ✅ 适配器
对数据库查询结果做缓存、或对控制器响应做格式化 ✅ 装饰器

常见陷阱

  • 装饰器可能改变原始类型(错误做法): 如果装饰器返回的不是同一个接口类型,那就变成了适配器或代理,失去了装饰器的可叠加特性。
  • 适配器不应增加新行为: 适配器主要做翻译/转换,不应添加功能(如缓存、日志),如果既转换接口又增强功能,请分层:内部用适配器,外部用装饰器。
  • 过度使用装饰器: 太多层装饰会导致调试困难,可以考虑用 Pipeline 模式(管道模式)替代多层装饰器。

如果你需要完整的代码示例文件或 Laravel/Symfony 框架下的具体实现模板,我可以帮你进一步展开。

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

友情链接攻略窝 · 礼包窝 · 配置窝 · 换算窝 · 算法窝 · 查文窝 · 手册窝 · 问答窝 · 全文搜 · 手册库
相关站点礼包窝 · 查文窝