ストラテジーパターン

概念

  • 方法だけを渡せるので便利

使わない実装

  • 分岐が増えて困る
  • 修正漏れの可能性
  • ただの文字列なので静的解析も効かないので大変そう
<?php

// 決済処理を行うクラス
class OrderProcessor 
{
    public function __construct(
        private float $amount
    ) {}

    // 決済方法が増えるたびに、このメソッドの中に if/case が増えていく
    public function processPayment(string $method): void 
    {
        if ($method === 'credit_card') {
            echo "クレジットカード決済で ¥" . $this->amount . " を支払いました。\n";
        } elseif ($method === 'paypay') {
            echo "PayPay決済で ¥" . $this->amount . " を支払いました。\n";
        } else {
            echo "未対応の決済方法です。\n";
        }
    }
}

// --- 実行コード ---
$order = new OrderProcessor(1500);

// 決済方法の文字列を渡して実行
$order->processPayment('credit_card');
$order->processPayment('paypay');

使う実装

  • 決済を実行するだけのクラスと、決済方法のクラスを分けて、もらったものを実行するだけ
  • 方法をいくらでも増やせそう
<?php
// 1. 共通のインターフェース(作戦のルール)を決める
interface PaymentStrategy 
{
    public function pay(float $amount): void;
}

// 2. 具体的な決済方法(作戦A)をクラス化
class CreditCardPayment implements PaymentStrategy 
{
    public function pay(float $amount): void 
    {
        echo "クレジットカード決済で ¥" . $amount . " を支払いました。\n";
    }
}

// 3. 具体的な決済方法(作戦B)をクラス化
class PayPayPayment implements PaymentStrategy 
{
    public function pay(float $amount): void 
    {
        echo "PayPay決済で ¥" . $amount . " を支払いました。\n";
    }
}

// 4. 決済を実行する本体(コンテキスト)
class OrderProcessor 
{
    public function __construct(
        private float $amount
    ) {}

    // 決済を実行するメソッド(引数で「どの作戦(オブジェクト)を使うか」を受け取る)
    public function processPayment(PaymentStrategy $paymentMethod): void 
    {
        // 使う側は if文を持たず、渡されたオブジェクトの pay() を呼ぶだけ
        $paymentMethod->pay($this->amount);
    }
}

// --- 実行コード ---
$order = new OrderProcessor(1500);

// クレジットカードで支払う場合(オブジェクトを直で渡す)
$order->processPayment(new CreditCardPayment());

// PayPayで支払う場合
$order->processPayment(new PayPayPayment());