構造パターン: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フィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- SOLID原則の実践
- 生成パターン:Factory、Builder、Singleton
- 構造パターン:Adapter、Decorator、Facade
- 振る舞いパターン:Strategy、Observer、Command