Strategyパターン
CoddyのPHPジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 74/91。
Strategy Patternは、アルゴリズムのファミリーを定義し、それぞれを独自のクラスにカプセル化して、実行時に交換可能にする振る舞いに関するデザインパターンです。ストラテジーを使用するオブジェクトは、どのアルゴリズムを実行しているのかを知る必要がありません。
さまざまな discount 戦略を適用できるショッピングカートを想像してください。条件分岐で discount ロジックをハードコーディングする代わりに、それぞれの discount を個別の class として定義します。
<?php
interface DiscountStrategy {
public function calculate(float $total): float;
}
class NoDiscount implements DiscountStrategy {
public function calculate(float $total): float {
return $total;
}
}
class PercentageDiscount implements DiscountStrategy {
public function __construct(private int $percent) {}
public function calculate(float $total): float {
return $total - ($total * $this->percent / 100);
}
}
class FixedDiscount implements DiscountStrategy {
public function __construct(private float $amount) {}
public function calculate(float $total): float {
return max(0, $total - $this->amount);
}
}
context class は任意の戦略を受け入れ、計算を delegates します:
<?php
class ShoppingCart {
public function __construct(private DiscountStrategy $discount) {}
public function setDiscount(DiscountStrategy $discount): void {
$this->discount = $discount;
}
public function checkout(float $total): float {
return $this->discount->calculate($total);
}
}
$cart = new ShoppingCart(new NoDiscount());
echo $cart->checkout(100) . "\n";
$cart->setDiscount(new PercentageDiscount(20));
echo $cart->checkout(100);
出力:
100
80Strategy Patternは、操作を実行する複数の方法があり、それらを使用するclassを変更せずに切り替えたい場合に力を発揮します。新しいdiscountの種類を追加するには、新しいclassを作成するだけでよく、既存のコードに手を加える必要はありません。
チャレンジ
簡単Strategy Patternを使用して送料計算機を作成しましょう。配送方法ごとに異なる料金ルールがあります。通常配送は固定料金を請求し、速達配送は割合を加算し、翌日配送はその両方を組み合わせます。Strategy Patternを使うと、それらを使用するコードを変更せずに、これらの計算を入れ替えられます。
コードを4つのファイルに分けて構成します。
ShippingStrategy.php:すべての配送方法の contract をdefineするShippingStrategyinterface をCreateします。荷物の重量を受け取り、送料を返すcalculate(float $weight): floatメソッドを持たせます。ShippingMethods.php:ShippingStrategy ファイルを読み込み、interfaceをimplementsする3つのクラスをCreateします。StandardShipping:重量に関係なく、$5.00のflat rateを請求しますExpressShipping:1 kilogramあたり$2.00を請求します(重量に2を掛けます)OvernightShipping:base feeとして$10.00に加え、1 kilogramあたり$3.00を請求します
ShippingCalculator.php:ShippingStrategy ファイルを読み込み、contextとしてactsするShippingCalculatorクラスをCreateします。calculatorには次の機能を持たせます。- constructorで
ShippingStrategyをAcceptします(constructor promotionを使用します) - 実行時に配送方法をchangeするための
setStrategy(ShippingStrategy $strategy): voidメソッドを持たせます - current strategyにdelegatesする
calculateCost(float $weight): floatメソッドを持たせます
- constructorで
main.php:ShippingMethods ファイルと ShippingCalculator ファイルを読み込みます。2つのinputを受け取ります。配送タイプ("standard"、"express"、または"overnight")と荷物の重量です。StandardShippingで開始するShippingCalculatorをCreateします。配送タイプのinputに基づき、setStrategy()を使用して appropriate な配送方法に切り替えます。その後、given の重量のcostを計算し、小数点以下2桁にformattedして結果を出力します。出力は次のようになります:
Shipping cost: $[cost]
このchallengeでは、Strategy Patternによってshipping systemがどのようにflexibleになるかを示します。SameDayShipping のような新しい配送方法を追加する場合、calculatorや既存の配送方法に触れることなく、interfaceをimplementsする新しいクラスをCreateするだけで済みます。
自分で試してみよう
<?php
require_once 'ShippingMethods.php';
require_once 'ShippingCalculator.php';
// 入力を読み取る
$shippingType = trim(fgets(STDIN));
$weight = floatval(fgets(STDIN));
// TODO: StandardShippingで始まるShippingCalculatorを作成する
// TODO: 配送タイプの入力("standard"、"express"、または"overnight")に基づいて、
// setStrategy()を使用して適切な配送方法に切り替える
// TODO: 指定された重量のコストを計算する
// TODO: 結果を小数点以下2桁にフォーマットして出力する
// Output format: Shipping cost: $[cost]
?>このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: PHPオンラインコンパイラ