第三方庫升級導致介面變更 (方法重命名、參數調整、回傳值改變) 時,適配器模式讓你在不修改核心業務代碼的情況下平滑遷移。
<?php
// ===== 版本 1 (舊版,既有代碼依賴此介面) =====
class HwLibV1 {
public function hello(): string { return 'Hello '; }
public function world(): string { return 'World!'; }
}
// 既有代碼使用方式
$lib = new HwLibV1();
echo $lib->hello() . $lib->world(); // "Hello World!"
// ===== 版本 2 (新版,介面已變更) =====
class HwLibV2 {
public function greet(): string { return 'Greetings and Salutations '; }
public function world(): string { return 'World!'; }
}
// hello() 已移除,改為 greet()
?>
<?php
// 1. 定義統一介面 (既有代碼依賴此介面)
interface HelloWorldInterface {
public function sayHello(): string;
public function sayWorld(): string;
}
// 2. 適配器:包裝 V1
class HwLibV1Adapter implements HelloWorldInterface {
private HwLibV1 $lib;
public function __construct(HwLibV1 $lib) {
$this->lib = $lib;
}
public function sayHello(): string {
return $this->lib->hello();
}
public function sayWorld(): string {
return $this->lib->world();
}
}
// 3. 適配器:包裝 V2 (介面轉換)
class HwLibV2Adapter implements HelloWorldInterface {
private HwLibV2 $lib;
public function __construct(HwLibV2 $lib) {
$this->lib = $lib;
}
public function sayHello(): string {
return $this->lib->greet(); // 關鍵:映射 greet() → sayHello()
}
public function sayWorld(): string {
return $this->lib->world();
}
}
// 4. 工廠:根據版本自動選擇適配器
class HelloWorldFactory {
public static function create(string $version = 'v2'): HelloWorldInterface {
return match($version) {
'v1' => new HwLibV1Adapter(new HwLibV1()),
'v2' => new HwLibV2Adapter(new HwLibV2()),
default => throw new InvalidArgumentException("Unknown version: $version"),
};
}
}
// 5. 客戶端代碼完全不變
$hw = HelloWorldFactory::create('v2'); // 或從配置讀取
echo $hw->sayHello() . $hw->sayWorld(); // "Greetings and Salutations World!"
?>
<?php
// 新系統使用新介面,舊系統使用舊介面,適配器同時實作兩者
class DualAdapter implements HelloWorldInterface, NewSystemInterface {
private HwLibV2 $lib;
public function __construct(HwLibV2 $lib) { $this->lib = $lib; }
// 舊介面
public function sayHello(): string { return $this->lib->greet(); }
public function sayWorld(): string { return $this->lib->world(); }
// 新介面
public function greetFormally(): string { return $this->lib->greet(); }
public function getPlanet(): string { return $this->lib->world(); }
}
?>
| 模式 | 目的 | 介面關係 | 典型用途 |
|---|---|---|---|
| 適配器 | 介面轉換 | 目標介面 ≠ 來源介面 | 第三方庫整合、舊系統遷移、API 版本兼容 |
| 代理 | 控制存取 | 目標介面 = 來源介面 | 延遲載入、權限控制、遠程代理、快取 |
| 裝飾器 | 動態增強 | 目標介面 = 來源介面 (可擴展) | 功能疊加、I/O 流處理、中介軟體 |
<?php
// 統一資料庫介面
interface DatabaseAdapter {
public function connect(array $config): void;
public function query(string $sql): iterable;
public function execute(string $sql): int;
public function lastInsertId(): string;
public function beginTransaction(): void;
public function commit(): void;
public function rollBack(): void;
}
// MySQL 適配器
class MySQLAdapter implements DatabaseAdapter {
private PDO $pdo;
public function connect(array $config): void {
$dsn = "mysql:host={$config['host']};dbname={$config['dbname']};charset=utf8mb4";
$this->pdo = new PDO($dsn, $config['user'], $config['pass'], [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
}
public function query(string $sql): iterable {
return $this->pdo->query($sql);
}
// ... 其他方法委派給 PDO
}
// PostgreSQL 適配器
class PostgreSQLAdapter implements DatabaseAdapter {
private PDO $pdo;
public function connect(array $config): void {
$dsn = "pgsql:host={$config['host']};dbname={$config['dbname']}";
$this->pdo = new PDO($dsn, $config['user'], $config['pass']);
}
// ... 相同介面,不同實作
}
// SQLite 適配器
class SQLiteAdapter implements DatabaseAdapter {
private PDO $pdo;
public function connect(array $config): void {
$this->pdo = new PDO("sqlite:{$config['path']}");
}
// ...
}
// 使用:配置驅動切換
$config = parse_ini_file('database.ini'); // driver=mysql
$adapterClass = match($config['driver']) {
'mysql' => MySQLAdapter::class,
'postgresql' => PostgreSQLAdapter::class,
'sqlite' => SQLiteAdapter::class,
};
$db = new $adapterClass();
$db->connect($config);
foreach ($db->query("SELECT * FROM users") as $user) { /* ... */ }
?>