アダプターパターン
CoddyのPHPジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 76/91。
アダプターパターン(Adapter Pattern)は、互換性のないインターフェースを持つオブジェクト同士を連携させるための構造に関するデザインパターンです。これは2つのインターフェース間の架け橋として機能し、ある形式から別の形式へと呼び出しを変換します。
決済を処理するアプリケーションを構築しており、サードパーティの決済ライブラリを統合する必要があると想像してください。そのライブラリは完璧に動作しますが、そのインターフェースはコードが期待するものと一致しません。アプリケーション全体を書き直す代わりに、両者の間を変換するアダプターを作成します。
<?php
// アプリケーションが期待するインターフェース
interface PaymentProcessor {
public function pay(float $amount): string;
}
// 異なるインターフェースを持つサードパーティライブラリ
class StripeAPI {
public function createCharge(int $cents, string $currency): string {
return "Charged {$cents} {$currency} via Stripe";
}
}
このアダプターは、内部でサードパーティのクラスを使用しながら、期待されるインターフェースを実装します:
<?php
class StripeAdapter implements PaymentProcessor {
public function __construct(private StripeAPI $stripe) {}
public function pay(float $amount): string {
$cents = (int)($amount * 100);
return $this->stripe->createCharge($cents, 'USD');
}
}
$stripe = new StripeAPI();
$processor = new StripeAdapter($stripe);
echo $processor->pay(29.99);
出力:
Charged 2999 USD via Stripeアダプターはドルをセントに変換し、適切なメソッドを呼び出します。アプリケーションコードは PaymentProcessor のことだけを認識しており、Stripe固有の要件については関知しません。後で別の決済プロバイダーに切り替える場合でも、既存のコードを変更することなく、新しいアダプターを作成するだけで済みます。
チャレンジ
簡単アダプターパターン(Adapter Pattern)を使用して、温度変換システムを構築しましょう。華氏(Fahrenheit)で値を読み取るレガシーな温度センサーがありますが、最新の気象アプリケーションはすべての温度を摂氏(Celsius)で受け取ることを想定しています。どちらのシステムも変更することなく、そのギャップを埋めるアダプターを作成します。
コードは以下の4つのファイルに分けて構成します:
TemperatureSensor.php— 気象アプリケーションが期待するTemperatureSensorインターフェースを定義します。これには、摂氏の温度を返すgetTemperature(): floatメソッドが必要です。LegacySensor.php— 古いハードウェアをシミュレートするLegacySensorクラスを作成します。このクラスは、アプリケーションが必要とするものとは異なるインターフェースを持っています:- コンストラクタで華氏の読み取り値を受け取ります(コンストラクタのプロパティ昇格を使用してください)
- 保存された華氏の値を返す
readFahrenheit(): floatメソッドを持ちます
LegacySensorAdapter.php— TemperatureSensor インターフェースと LegacySensor クラスの両方をインクルードします。TemperatureSensorを実装し、LegacySensorをラップするLegacySensorAdapterクラスを作成します。アダプターは以下のことを行う必要があります:- コンストラクタで
LegacySensorを受け取る - レガシーセンサーから華氏の値を読み取り、公式
(fahrenheit - 32) * 5 / 9を使用して摂氏に変換することでgetTemperature()を実装する
- コンストラクタで
main.php— LegacySensorAdapter ファイルをインクルードします。入力として華氏の温度を1つ受け取ります。入力された温度で
LegacySensorを作成し、それをLegacySensorAdapterでラップします。アダプターのgetTemperature()を呼び出し、結果を小数点以下2桁で四捨五入して出力します。出力形式は
"Temperature: " . round($celsius, 2) . "C"となるようにしてください。
このチャレンジは、アダプターパターンによって、既存のコードを修正することなく互換性のないインターフェースを統合する方法を示しています。気象アプリケーションは TemperatureSensor インターフェースのみを介して動作し、実際の読み取り値がレガシーな華氏センサーからのものであることを全く意識しません。
チートシート
アダプターパターン(Adapter Pattern)は、互換性のないインターフェースを持つオブジェクト同士を、それらの間の架け橋として機能させることで連携できるようにする構造的なデザインパターンです。
基本構造
アプリケーションが期待するインターフェースを定義します:
<?php
interface PaymentProcessor {
public function pay(float $amount): string;
}異なるインターフェースを持つサードパーティのクラス:
<?php
class StripeAPI {
public function createCharge(int $cents, string $currency): string {
return "Charged {$cents} {$currency} via Stripe";
}
}アダプターの作成
アダプターは、内部でサードパーティのクラスを使用しながら、期待されるインターフェースを実装します:
<?php
class StripeAdapter implements PaymentProcessor {
public function __construct(private StripeAPI $stripe) {}
public function pay(float $amount): string {
$cents = (int)($amount * 100);
return $this->stripe->createCharge($cents, 'USD');
}
}使用例
$stripe = new StripeAPI();
$processor = new StripeAdapter($stripe);
echo $processor->pay(29.99);
// 出力: Charged 2999 USD via Stripeアダプターはインターフェース間の変換を行うため、サードパーティのライブラリを統合したり実装を切り替えたりする際に、アプリケーションのコードを変更せずに済みます。
自分で試してみよう
<?php
require_once 'LegacySensorAdapter.php';
// 入力を読み込む
$fahrenheit = floatval(trim(fgets(STDIN)));
// TODO: 入力された温度でLegacySensorを作成する
// TODO: それをLegacySensorAdapterでラップする
// TODO: アダプターのgetTemperature()を呼び出す
// TODO: 小数点以下2桁に丸めて結果を出力する
// 出力形式: Temperature: [celsius]C
?>このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。