章: 第1章: オブジェクト指向の深化
「処理の種類が増えるたびにif文が増える」問題
ソートアルゴリズムを切り替えたい、送信方法を切り替えたい、価格計算ルールを切り替えたい——。こうした「処理を差し替えたい」要求は実務で頻繁に起きます。
多くの場合、最初は if ($type === '...') で解決します。しかし種類が増えるたびに条件分岐が肥大化し、新しい処理の追加が既存コードへの変更を強制します。
Strategyパターンは「処理のアルゴリズム」をクラスとして表現し、呼び出し元を一切変えずに処理を差し替えられる設計です。
Strategyパターンの実装
<?php
// インターフェイス(戦略の契約)
interface SortStrategy {
public function sort(array &$data): void;
}
// 具体的な戦略クラス
class BubbleSort implements SortStrategy {
public function sort(array &$data): void {
// バブルソートの実装
}
}
class QuickSort implements SortStrategy {
public function sort(array &$data): void {
// クイックソートの実装
}
}
// コンテキスト(戦略を使う側)
class Sorter {
public function __construct(private SortStrategy $strategy) {}
public function run(array &$data): void {
$this->strategy->sort($data);
}
}
// 使い方:処理を差し替えるだけ
$sorter = new Sorter(new QuickSort());
$sorter->run($data);
if文 vs Strategyパターン
<?php
// NG: 種類が増えるたびにここを変更しなければならない
function sendNotification(string $type, string $message): void {
if ($type === 'email') {
// メール送信
} elseif ($type === 'slack') {
// Slack送信
} elseif ($type === 'sms') {
// SMS送信(新規追加するたびにこのメソッドを変更)
}
}
// OK: 新しい通知手段はクラスを追加するだけ
interface NotificationStrategy {
public function send(string $message): void;
}
class EmailNotification implements NotificationStrategy { /* ... */ }
class SlackNotification implements NotificationStrategy { /* ... */ }
class SmsNotification implements NotificationStrategy { /* ... */ }
| 観点 | if文のアプローチ | Strategyパターン |
| 新しい処理の追加 | 既存の分岐ブロックを変更 | 新クラスを追加するだけ |
| 単体テスト | 全分岐をまとめてテスト | 各戦略クラスを独立してテスト |
| コードの読みやすさ | 種類が増えるほど複雑化 | クラス名が処理の意図を表す |
チェックポイント:
if ($type === ...)やswitch ($type)が3種類以上になってきたら、Strategyパターンへの切り出しを検討しましょう。
実務での使いどころ
- 支払い方法(クレジット、銀行振込、コンビニ払い)の切り替え
- ファイル出力形式(CSV、JSON、XML)の切り替え
- バリデーションルールの組み合わせ
- キャッシュドライバーの切り替え
まとめ & 次のステップ
- Strategyパターンはアルゴリズムをクラスに閉じ込め、呼び出し元を変えずに差し替えを可能にする
- if文の連鎖が増えてきたら、インターフェイスを定義して戦略クラスに切り出すのがサイン
- DIと組み合わせることで、コンストラクタで戦略を注入する設計が実現できる
次回はObserverパターンを学びます。「何かが起きたら複数の処理を動かす」仕組みを、発行側のコードを変えずに実現する方法です。