首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 適配器模式實戰:統一異構接口、第三方庫整合與遺留系統改造

     編輯:PHP 相關知識

PHP 適配器模式實戰:統一異構接口、第三方庫整合與遺留系統改造

適配器模式將一個類的接口轉換為客戶端期望的另一個接口,使原本不兼容的類可以協同工作。它是整合第三方支付、物流、存儲、郵件等異構服務的標準模式。本文展示類適配器(繼承)與對象適配器(組合)兩種形式,並給出多適配器統一管理的工廠模式結合方案。

一、核心結構:目標介面 + 被適配者 + 適配器

<?php
// 1. 目標介面:客戶端期望的統一協議
interface PaymentGateway {
    public function charge(float $amount, string $currency, array $options): PaymentResult;
    public function refund(string $transactionId, float $amount): RefundResult;
    public function getTransaction(string $id): Transaction;
}

// 2. 被適配者:第三方 SDK 真實接口(不可修改)
class StripeSdk {
    public function createCharge(array $params): \Stripe\Charge { /* ... */ }
    public function createRefund(array $params): \Stripe\Refund { /* ... */ }
    public function retrieveCharge(string $id): \Stripe\Charge { /* ... */ }
}

class PayPalSdk {
    public function executePayment(array $data): \PayPal\Payment { /* ... */ }
    public function refundSale(string $saleId, array $amount): \PayPal\Refund { /* ... */ }
    public function getSale(string $id): \PayPal\Sale { /* ... */ }
}

// 3. 統一數據傳輸對象(DTO)
class PaymentResult { public bool $success; public string $transactionId; public string $rawResponse; }
class RefundResult { public bool $success; public string $refundId; }
class Transaction { public string $id; public float $amount; public string $status; public string $currency; }

二、對象適配器(組合模式,推薦)

<?php
class StripeAdapter implements PaymentGateway {
    private StripeSdk $sdk;

    public function __construct(StripeSdk $sdk) {
        $this->sdk = $sdk;
    }

    public function charge(float $amount, string $currency, array $options): PaymentResult {
        try {
            $charge = $this->sdk->createCharge([
                'amount' => (int)($amount * 100), // Stripe 單位:分
                'currency' => strtolower($currency),
                'source' => $options['token'] ?? '',
                'description' => $options['description'] ?? '',
                'metadata' => $options['metadata'] ?? [],
            ]);
            return new PaymentResult([
                'success' => true,
                'transactionId' => $charge->id,
                'rawResponse' => json_encode($charge->jsonSerialize()),
            ]);
        } catch (\Stripe\Exception\CardException $e) {
            return new PaymentResult(['success' => false, 'transactionId' => '', 'rawResponse' => $e->getMessage()]);
        }
    }

    public function refund(string $transactionId, float $amount): RefundResult {
        $refund = $this->sdk->createRefund([
            'charge' => $transactionId,
            'amount' => (int)($amount * 100),
        ]);
        return new RefundResult(['success' => true, 'refundId' => $refund->id]);
    }

    public function getTransaction(string $id): Transaction {
        $charge = $this->sdk->retrieveCharge($id);
        return new Transaction([
            'id' => $charge->id,
            'amount' => $charge->amount / 100,
            'status' => $charge->status,
            'currency' => strtoupper($charge->currency),
        ]);
    }
}

class PayPalAdapter implements PaymentGateway {
    private PayPalSdk $sdk;

    public function __construct(PayPalSdk $sdk) {
        $this->sdk = $sdk;
    }

    public function charge(float $amount, string $currency, array $options): PaymentResult {
        $payment = $this->sdk->executePayment([
            'amount' => ['total' => number_format($amount, 2), 'currency' => $currency],
            'payer' => ['payment_method' => 'paypal'],
            'redirect_urls' => $options['redirect_urls'] ?? [],
        ]);
        return new PaymentResult([
            'success' => $payment->getState() === 'approved',
            'transactionId' => $payment->getId(),
            'rawResponse' => json_encode($payment->toArray()),
        ]);
    }

    public function refund(string $transactionId, float $amount): RefundResult {
        $refund = $this->sdk->refundSale($transactionId, [
            'amount' => ['total' => number_format($amount, 2), 'currency' => 'USD'],
        ]);
        return new RefundResult(['success' => true, 'refundId' => $refund->getId()]);
    }

    public function getTransaction(string $id): Transaction {
        $sale = $this->sdk->getSale($id);
        return new Transaction([
            'id' => $sale->getId(),
            'amount' => (float)$sale->getAmount()->getTotal(),
            'status' => $sale->getState(),
            'currency' => $sale->getAmount()->getCurrency(),
        ]);
    }
}

三、類適配器(繼承模式,適用於輕量封裝)

<?php
// 假設舊系統有一個類,需適配為新介面
class LegacyLogger {
    public function writeLog(string $level, string $message): void { /* ... */ }
}

// 新介面
interface Logger {
    public function info(string $message): void;
    public function error(string $message): void;
    public function debug(string $message): void;
}

// 類適配器:繼承舊類 + 實現新介面
class LegacyLoggerAdapter extends LegacyLogger implements Logger {
    public function info(string $message): void { $this->writeLog('INFO', $message); }
    public function error(string $message): void { $this->writeLog('ERROR', $message); }
    public function debug(string $message): void { $this->writeLog('DEBUG', $message); }
}

四、適配器工廠:統一獲取入口,支援運行時切換

<?php
class PaymentGatewayFactory {
    private array $adapters = [];

    public function __construct() {
        // 註冊可用適配器(懶加載)
        $this->adapters = [
            'stripe' => fn() => new StripeAdapter(new StripeSdk()),
            'paypal' => fn() => new PayPalAdapter(new PayPalSdk()),
            'mock'   => fn() => new MockAdapter(), // 測試用
        ];
    }

    public function create(string $gateway): PaymentGateway {
        if (!isset($this->adapters[$gateway])) {
            throw new InvalidArgumentException("Unsupported gateway: $gateway");
        }
        return ($this->adapters[$gateway])();
    }

    public function getAvailable(): array {
        return array_keys($this->adapters);
    }
}

// 使用
$factory = new PaymentGatewayFactory();
$gateway = $factory->create(config('payment.default')); // 從配置讀取
$result = $gateway->charge(99.99, 'USD', ['token' => 'tok_visa']);

// 多網關降級重試
$gateways = ['stripe', 'paypal'];
foreach ($gateways as $gw) {
    try {
        $gateway = $factory->create($gw);
        $result = $gateway->charge($amount, $currency, $options);
        if ($result->success) break;
    } catch (Throwable $e) {
        Log::warning("Gateway $gw failed: " . $e->getMessage());
    }
}

五、適配器 vs 外觀 vs 裝飾器

模式意圖接口關係典型場景
適配器接口轉換(不兼容→兼容)Target ≠ Adaptee第三方 SDK 整合、遺留系統改造
外觀簡化複雜子系統接口Facade 封裝多個類提供簡單入口隱藏內部複雜度
裝飾器動態添加職責Decorator = Component日誌、緩存、授權、壓縮等橫切關注點

六、實戰場景:雲存儲適配器(本地/七牛/阿里云/AWS S3)

<?php
interface StorageAdapter {
    public function put(string $path, string $content, array $options = []): string; // 返回訪問 URL
    public function get(string $path): string;
    public function delete(string $path): bool;
    public function exists(string $path): bool;
    public function url(string $path): string;
}

class LocalStorageAdapter implements StorageAdapter { /* ... */ }
class QiniuStorageAdapter implements StorageAdapter { /* ... */ }
class S3StorageAdapter implements StorageAdapter { /* ... */ }

// Laravel 文件系統即採用此模式
// config/filesystems.php
// 'disks' => [
//     'local' => ['driver' => 'local', 'root' => storage_path('app')],
//     's3' => ['driver' => 's3', 'key' => env('AWS_KEY'), ...],
//     'qiniu' => ['driver' => 'qiniu', 'access_key' => ..., 'bucket' => ...],
// ]
// 使用:Storage::disk('s3')->put('file.txt', $content);

七、最佳實踐原則

  1. 優先對象適配器(組合):靈活、可測試、支援多重適配、符合合成復用原則
  2. 適配器只做「轉換」,不做「業務邏輯」:參數映射、異常轉換、格式統一——業務規則留在服務層
  3. DTO 邊界清晰:適配器輸出統一 DTO,屏蔽第三方 SDK 具體類,便於 Mock 測試
  4. 工廠/容器管理實例化:配置驅動適配器選擇,支援運行時熱切換、降級重試
  5. 文檔化接口契約:用 PHP 介面 + PHPDoc 明確定義 Target 接口,作為適配器實現的驗收標準

總結:適配器模式是系統集成的通用適配層。通過「目標介面 + 適配器實現 + 工廠註冊」三層結構,將第三方 SDK 的差異性封裝在適配器內部,業務代碼面向統一介面編程,實現依賴倒置、開閉原則、單一職責。無論是支付、存儲、郵件、短信、地圖、推送,統統適用此模式。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved