Menu
Coddy logo textTech

Adapterパターン

CoddyのPHPジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 76/91。

アダプターパターンは、互換性のないinterfaceを持つオブジェクト同士が連携できるようにする構造設計パターンです。2つのinterfaceの間の橋渡しとして機能し、一方の形式の呼び出しを別の形式に変換します。

支払いを処理するapplicationを構築していて、サードパーティの支払いlibraryを統合する必要があると想像してください。そのlibraryは完全に動作しますが、そのinterfaceはコードがexpectsものと一致しません。application全体を書き直す代わりに、2つの間を翻訳するadapterを作成します。

<?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";
    }
}

adapter は、内部ではサードパーティのクラスを使用しながら、期待されるインターフェースを実装します。

<?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);

Output:

Charged 2999 USD via Stripe

adapter はドルを cents に converts し、適切な method を呼び出します。あなたの application code は PaymentProcessor についてしか知らず、Stripe 固有の要件にはまったく関与しません。後で different な決済プロバイダーに切り替える場合も、既存のコードを変更せずに新しい adapter を作成するだけです。

challenge icon

チャレンジ

簡単

Adapter Pattern を使用して温度変換システムを構築しましょう。Fahrenheit で測定値を出力する古い温度センサーがありますが、最新の気象アプリケーションではすべての温度を Celsius で受け取ることを expects しています。どちらのシステムも変更する代わりに、両者の隔たりを埋める adapter を作成します。

コードを4つのファイルに分けて整理します。

  • TemperatureSensor.php: 気象アプリケーションが expects する TemperatureSensor interface を Define します。温度を Celsius で返す getTemperature(): float method を have する必要があります。
  • LegacySensor.php: 古い hardware をシミュレートする LegacySensor class を Create します。この class は、application が必要とするものとは different な interface を have します。
    • constructor で Fahrenheit の測定値を受け取ります(constructor promotion を使用します)
    • 保存された Fahrenheit の値を返す readFahrenheit(): float method を have します
  • LegacySensorAdapter.php: TemperatureSensor interface と LegacySensor class の両方を Include します。TemperatureSensor を implements し、LegacySensor をラップする LegacySensorAdapter class を Create します。adapter は次のようにします。
    • constructor で LegacySensor を Accept します
    • legacy sensor から Fahrenheit の値を読み取り、formula (fahrenheit - 32) * 5 / 9 を使用して Celsius に変換することで、getTemperature() を Implement します
  • main.php: LegacySensorAdapter file を Include します。入力として Fahrenheit の温度を1つ受け取ります。

    入力温度を使って LegacySensor を Create し、それを LegacySensorAdapter でラップします。adapter で getTemperature() を Call し、結果を小数点以下2桁に丸めて Output します。

    Output format は次のとおりです: Temperature: [celsius]C

この challenge は、Adapter Pattern によって既存の code を変更せずに互換性のない interface を統合できることを示します。気象アプリケーションは TemperatureSensor interface のみを使用し、実際の測定値が legacy の Fahrenheit sensor から取得されていることを完全に意識しません。

自分で試してみよう

<?php
require_once 'LegacySensorAdapter.php';

// 入力を読み取る
$fahrenheit = floatval(trim(fgets(STDIN)));

// TODO: 入力温度でLegacySensorを作成する
// TODO: それをLegacySensorAdapterでラップする
// TODO: アダプターでgetTemperature()を呼び出す
// TODO: 結果を小数点以下2桁に丸めて出力する
// 出力形式: Temperature: [celsius]C

?>
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: PHPオンラインコンパイラ