本文目录导读:

在PHP项目中,适配器模式(Adapter Pattern)用于解决接口不兼容的问题,让原本因接口不同而无法协作的类能够协同工作,针对“兼容多接口”的需求,最核心的思路是将多个不同的接口统一为一个目标接口,客户端只依赖这个目标接口,而适配器负责将多个不同的源接口转换为目标接口。
下面给出几种常见的实现方案,以及一个完整的实战示例。
核心思想
假设你的系统需要与多个第三方支付网关(支付宝、微信、PayPal)交互,每个网关都有自己的接口签名和调用方式,适配器模式允许你定义一个统一的 PaymentInterface,然后为每个网关创建一个适配器类,这些适配器将网关特有的 API 调用封装成统一的接口方法。
实现方式
方案A:类适配器(使用继承)
- 适用场景:适配器需要继承被适配的类,并实现目标接口。
- 优点:可以直接访问被适配类的 protected 方法。
- 缺点:PHP 单继承限制,一个适配器只能适配一个类。
// 目标接口
interface PaymentInterface {
public function pay(float $amount): string;
}
// 被适配的类(第三方支付SDK)
class AlipaySDK {
public function alipayRequest(float $amount): string {
return "Alipay paid: {$amount}";
}
}
// 类适配器
class AlipayAdapter extends AlipaySDK implements PaymentInterface {
public function pay(float $amount): string {
return $this->alipayRequest($amount);
}
}
方案B:对象适配器(使用组合)—— 推荐
- 适用场景:需要同时适配多个接口、或者不想破坏被适配类的封装。
- 优点:灵活,可适配任意数量的接口,符合组合优于继承原则。
// 目标接口
interface PaymentInterface {
public function pay(float $amount): string;
}
// 多个被适配的类
class WechatPay {
public function wechatRequest(float $amount): string {
return "Wechat paid: {$amount}";
}
}
class PayPal {
public function sendPayment(float $amount): string {
return "PayPal paid: {$amount}";
}
}
// 对象适配器(组合方式)
class WechatAdapter implements PaymentInterface {
private WechatPay $wechatPay;
public function __construct(WechatPay $wechatPay) {
$this->wechatPay = $wechatPay;
}
public function pay(float $amount): string {
return $this->wechatPay->wechatRequest($amount);
}
}
class PayPalAdapter implements PaymentInterface {
private PayPal $payPal;
public function __construct(PayPal $payPal) {
$this->payPal = $payPal;
}
public function pay(float $amount): string {
// 在适配器内部做参数转换、签名、异常处理等
return $this->payPal->sendPayment($amount);
}
}
方案C:适配多个接口的聚合适配器
当需要同时适配多个不同来源的接口(例如既要调用支付接口,又要调用日志接口)时,可以设计一个聚合适配器。
// 目标接口1
interface PaymentInterface {
public function pay(float $amount): string;
}
// 目标接口2
interface LoggerInterface {
public function log(string $message): void;
}
// 被适配的第三方类
class ThirdPartySDK {
public function doPayment(float $amount): string {
return "ThirdParty paid: {$amount}";
}
public function writeLog(string $msg): void {
echo "[Log]: {$msg}" . PHP_EOL;
}
}
// 聚合适配器,同时实现两个目标接口
class ThirdPartyAdapter implements PaymentInterface, LoggerInterface {
private ThirdPartySDK $sdk;
public function __construct(ThirdPartySDK $sdk) {
$this->sdk = $sdk;
}
public function pay(float $amount): string {
// 可以做一些预处理、参数映射
return $this->sdk->doPayment($amount);
}
public function log(string $message): void {
$this->sdk->writeLog($message);
}
}
实战示例:多接口支付系统
假设你要对接支付宝、微信、银联三个支付渠道,每个渠道的接口签名、参数、返回格式都不同。
定义统一接口
interface UnifiedPayment {
public function charge(float $amount, array $orderInfo): array;
public function refund(string $transactionId, float $amount): bool;
}
创建多个适配器
class AlipayAdapter implements UnifiedPayment {
private AlipayService $alipay;
public function __construct(AlipayService $alipay) {
$this->alipay = $alipay;
}
public function charge(float $amount, array $orderInfo): array {
// 将统一参数转换为支付宝SDK需要的参数
$alipayParams = [
'total_amount' => $amount,
'subject' => $orderInfo['subject'],
'out_trade_no' => $orderInfo['orderNo'],
];
$result = $this->alipay->tradePagePay($alipayParams);
return ['success' => true, 'redirect_url' => $result];
}
public function refund(string $transactionId, float $amount): bool {
return $this->alipay->refund($transactionId, $amount);
}
}
class WechatAdapter implements UnifiedPayment {
// 类似上述逻辑,调用微信SDK
// ...
}
客户端使用
function processPayment(UnifiedPayment $payment, float $amount, array $order) {
$result = $payment->charge($amount, $order);
// 统一处理结果
return $result;
}
// 根据配置选择适配器
$config = ['channel' => 'alipay'];
$payment = match ($config['channel']) {
'alipay' => new AlipayAdapter(new AlipayService()),
'wechat' => new WechatAdapter(new WechatPayService()),
'unionpay' => new UnionPayAdapter(new UnionPayService()),
};
processPayment($payment, 100.00, ['subject' => 'Test', 'orderNo' => '20241001']);
如何兼容多个不同风格的接口?
当被适配的类有不同的方法签名、参数顺序、返回值类型时,适配器内部可以做以下几件事:
| 被适配接口风格 | 适配器内部处理 |
|---|---|
| 参数结构不同 | 做参数映射、重组、类型转换 |
| 方法命名不同 | 调用对应的方法并传递参数 |
| 返回值格式不同 | 解析返回值,统一为标准格式 |
| 异常处理不同 | 捕获第三方异常,抛出自定义业务异常 |
| 需要额外认证/签名 | 在适配器内部完成认证、签名逻辑 |
优缺点总结
优点
- 解耦:客户端代码与具体实现分离。
- 复用:复用已有的第三方库,无需修改其代码。
- 可扩展:新增一个渠道只需增加一个适配器类,符合开闭原则。
- 统一接口:多接口被统一,业务逻辑无需关心底层差异。
缺点
- 适配器数量随接入系统增多而增加。
- 过度使用可能导致系统结构复杂(可结合工厂模式创建适配器)。
最佳实践建议
- 优先使用对象适配器(组合):避免单继承限制,且不破坏被适配类的封装。
- 适配器不应包含业务逻辑:它只做接口转换和数据映射,业务判断放在调用方或服务层。
- 结合工厂模式创建适配器:根据配置动态生成合适的适配器实例。
- 使用接口定义契约:所有适配器实现同一个接口,客户端只依赖该接口。
- 做好单元测试:每个适配器应独立测试,验证参数映射和异常处理是否正确。
通过适配器模式,你的 PHP 项目可以优雅地对接任意数量、任意风格的第三方接口,同时保持核心业务代码的整洁和可维护性。