0Pricing
PHP Academy · レッスン

構造パターン:Adapter、Decorator、Facade

構造パターンでオブジェクトを組み合わせ、インターフェースを簡潔にします。

「構造パターン:Adapter、Decorator、Facade」はCoddyKit上の無料PHP Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはPHP Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 PHP Academyコースには全4レッスンが含まれています。

オブジェクトを組み合わせる

構造パターンは、柔軟性を保ちながらクラスとオブジェクトを組み合わせて、より大きな構造を作る方法を表します。ここでは、特に頻繁に使われる次の3つを扱います。

  • Adapter — 互換性のないインターフェースを利用可能にします。
  • Decorator — サブクラス化せずに振る舞いを追加します。
  • Facade — 複雑なサブシステムに対して、シンプルな窓口を提供します。

Adapter:課題

PaymentGatewayインターフェースに依存している一方で、サードパーティーSDKが異なる形式のchargeCard($amountInCents)を公開しているとします。SDKを直接呼び出すと、そのAPIがあらゆる場所に漏れ出します。以下では、2つのインターフェースに明確な互換性がありません。この不一致こそが、Adapterで解決する対象です。

<?php
// What our app wants to depend on
interface PaymentGateway { public function pay(float $dollars): string; }

// What the vendor actually gives us (cents, array result)
final class StripeSdk {
    public function chargeCard(int $cents): array {
        return ['id' => 'ch_1', 'amount' => $cents];
    }
}
// dollars vs cents, string vs array -> shapes don't line up
var_dump((new StripeSdk())->chargeCard(2000));

コードで見るAdapter

Adapterは適応対象を保持し、対象インターフェースを実装して、引数と戻り値を変換します。アプリケーションコードはすっきり保たれ、ベンダーに依存しない形になります。

<?php
interface PaymentGateway { public function pay(float $dollars): string; }

// Third-party SDK with an incompatible API
final class StripeSdk {
    public function chargeCard(int $cents): array {
        return ['id' => 'ch_1', 'amount' => $cents];
    }
}

final class StripeAdapter implements PaymentGateway {
    public function __construct(private StripeSdk $sdk) {}
    public function pay(float $dollars): string {
        $res = $this->sdk->chargeCard((int) round($dollars * 100));
        return "charged {$res['amount']}c -> {$res['id']}";
    }
}
echo (new StripeAdapter(new StripeSdk()))->pay(20.0), PHP_EOL;

Decorator:考え方

Decoratorは、自身と同じインターフェースを持つオブジェクトをラップし、委譲の前後に振る舞いを追加します。継承とは異なり、Decoratorは実行時に積み重ねることができるため、クラスが爆発的に増えることなく、任意の順序で機能を組み合わせられます。以下は、同じインターフェースを実装して内部のインスタンスを保持する、最小限の骨格です。

<?php
interface Coffee { public function cost(): float; }
final class Espresso implements Coffee {
    public function cost(): float { return 2.0; }
}
final class WithMilk implements Coffee {
    public function __construct(private Coffee $inner) {}
    public function cost(): float { return $this->inner->cost() + 0.5; }
}
echo (new WithMilk(new Espresso()))->cost(), PHP_EOL; // 2.5

コードで見るDecorator

各Decoratorは同じインターフェースを実装し、内部のインスタンスを保持します。ここでは、データソースをキャッシュとロギングでラップし、それらを自由に組み合わせています。

<?php
interface DataSource { public function read(): string; }

final class FileSource implements DataSource {
    public function read(): string { return 'raw-data'; }
}

final class UpperCaseDecorator implements DataSource {
    public function __construct(private DataSource $inner) {}
    public function read(): string { return strtoupper($this->inner->read()); }
}
final class ExclaimDecorator implements DataSource {
    public function __construct(private DataSource $inner) {}
    public function read(): string { return $this->inner->read() . '!!!'; }
}

$src = new ExclaimDecorator(new UpperCaseDecorator(new FileSource()));
echo $src->read(), PHP_EOL; // RAW-DATA!!!

実際のDecorator

PSR-7/PSR-15のミドルウェアは、HTTP処理にDecoratorパターンを適用したものです。各ミドルウェアが次のミドルウェアをラップし、リクエスト/レスポンスに対して認証、ロギング、キャッシュなどを追加します。ストリームラッパーやPSR-6/16のキャッシュレイヤーでも、装飾がよく使われます。重要な特徴は、ラッパーがラップ対象の代わりに利用できることです。

Decoratorと継承

継承はコンパイル時に振る舞いを固定し、基底クラスも1つしか指定できません。装飾は動的で、組み合わせも可能です。次のような場合はDecoratorを選びます。

  • オプション機能の組み合わせが増大する場合
  • 実行時に振る舞いを追加または削除したい場合
  • finalクラスや、サブクラス化できないサードパーティークラスを拡張する必要がある場合

Facade:考え方

Facadeは、多数のクラスからなる複雑なサブシステムに対して、単一の簡略化された入り口を提供します。サブシステムを隠すわけではないため、必要であれば内部にアクセスできますが、一般的な用途向けに便利な高レベルAPIを提供します。次の画面では、以下に示す冗長なサブシステム操作と、Facadeを1行呼び出す方法を比較します。

<?php
// Without a facade, the client wires every step itself
final class Cpu { public function boot(): string { return 'cpu '; } }
final class Disk { public function load(): string { return 'disk '; } }
final class Ram { public function check(): string { return 'ram'; } }

$out = (new Cpu())->boot() . (new Disk())->load() . (new Ram())->check();
echo $out, PHP_EOL; // cpu disk ram

コードで見るFacade

Facadeは、複数の協調オブジェクトを1つのメソッドの背後で調整します。クライアントは在庫、決済、配送を自分で組み立てる代わりに、place()を呼び出します。

<?php
final class Inventory { public function reserve(string $sku): bool { return true; } }
final class Payments { public function charge(float $amt): bool { return true; } }
final class Shipping { public function dispatch(string $sku): string { return 'tracking-99'; } }

final class OrderFacade {
    public function __construct(
        private Inventory $inv,
        private Payments $pay,
        private Shipping $ship
    ) {}
    public function place(string $sku, float $amount): string {
        if (!$this->inv->reserve($sku)) { return 'out of stock'; }
        if (!$this->pay->charge($amount)) { return 'payment failed'; }
        return 'shipped: ' . $this->ship->dispatch($sku);
    }
}
echo (new OrderFacade(new Inventory(), new Payments(), new Shipping()))
    ->place('ABC', 49.99), PHP_EOL;

3つの違い

  • Adapterはインターフェースを変更して、既存の2つの部品が通信できるようにします。振る舞いは同じで、形式だけが異なります。
  • Decoratorはインターフェースを維持したまま振る舞いを追加し、実行時に積み重ねられます。
  • Facadeは、多数のクラスの前に新しく、よりシンプルなインターフェースを導入します。

覚え方として、Adapterは互換性、Decoratorは拡張、Facadeは簡略化に関するものです。

LaravelのFacadeに関する注意

Laravelの「facades」は、サービスコンテナを基盤とする静的プロキシ機構であり、GoFのFacadeパターンではありません。便利な窓口を提供するという目的は共通していますが、実装は異なります。設計について考える際は、フレームワークの用語と構造パターンを混同しないでください。

理解度チェック

意図からパターンを特定してください。

まとめ

3つの構造パターンを使ってオブジェクトを組み合わせました。Adapterで互換性のないインターフェース(例:ベンダーSDK)を橋渡しし、Decoratorで実行時に振る舞いを積み重ね(PSR-15ミドルウェアの基盤)、Facadeで複雑なサブシステムにシンプルな窓口を提供しました。どのパターンも結合を下げるため、コードがベンダーロックインや機能の肥大化に強くなります。

よくある質問

「構造パターン:Adapter、Decorator、Facade」レッスンは無料ですか?

はい。「構造パターン:Adapter、Decorator、Facade」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、PHP Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 PHP Academyコースには全4レッスンが含まれています。

「構造パターン:Adapter、Decorator、Facade」で何を学びますか?

構造パターンでオブジェクトを組み合わせ、インターフェースを簡潔にします。 ブラウザで直接実行するハンズオンコードでPHP Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

PHP Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのPHP Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「構造パターン:Adapter、Decorator、Facade」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このPHP Academyレッスンでコードを書いて実行できますか?

はい。すべてのPHP Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. SOLID原則の実践
  2. 生成パターン:Factory、Builder、Singleton
  3. 構造パターン:Adapter、Decorator、Facade
  4. 振る舞いパターン:Strategy、Observer、Command
← PHP Academyに戻る