導入
「通常価格」「10%オフ」「会員はさらに固定額オフ」……割引の計算方法が増えるたびに if/else if を継ぎ足していくと、どんどん巨大な分岐の塊になります。Strategy(ストラテジー) は、アルゴリズム(処理の手順)そのものをインタフェースとして切り出し、丸ごと差し替えられるようにする パターンです。第1章のSOLIDで見た「依存性逆転」の考え方の具体例でもあります。
説明
「割引の計算方法」を DiscountStrategy というインタフェースに切り出し、Cart はその具体的な中身を知らずに呼び出すだけにします。
public class Main {
public static void main(String[] args) {
Cart cart = new Cart(new PercentDiscount(10));
System.out.println(cart.checkout(1000));
cart.setStrategy(new NoDiscount());
System.out.println(cart.checkout(1000));
}
}
interface DiscountStrategy {
int apply(int price);
}
class NoDiscount implements DiscountStrategy {
public int apply(int price) {
return price;
}
}
class PercentDiscount implements DiscountStrategy {
private int percent;
PercentDiscount(int percent) {
this.percent = percent;
}
public int apply(int price) {
return price - price * percent / 100;
}
}
class Cart {
private DiscountStrategy strategy;
Cart(DiscountStrategy strategy) {
this.strategy = strategy;
}
void setStrategy(DiscountStrategy strategy) {
this.strategy = strategy;
}
int checkout(int price) {
return strategy.apply(price);
}
}
classDiagram
DiscountStrategy <|.. NoDiscount
DiscountStrategy <|.. PercentDiscount
Cart --> DiscountStrategy : uses
class DiscountStrategy {
<<interface>>
+apply(price) int
}
CartはDiscountStrategy strategy;というフィールドを持ちますが、それがNoDiscountなのかPercentDiscountなのかを一切気にしません。cart.checkout(1000)は、そのとき差し込まれているstrategyのapplyを呼ぶだけ。割引の計算ロジックはCartの外側に出ています。cart.setStrategy(new NoDiscount())… 実行の途中でアルゴリズムを丸ごと差し替えられます。Cart自体のコードは一切変更していません。- 新しい割引方法(例えば「会員は固定200円引き」)を追加したいときも、
DiscountStrategyを実装した新しいクラスを1つ足すだけで済みます。
やってみよう
下のエディタを実行し、PercentDiscount(10) で1000円が900円になること、setStrategy(new NoDiscount()) に切り替えた後は1000円のままになることを確認しましょう。
演習
FixedDiscount という DiscountStrategy を作ってください。コンストラクタで割引額(int amount)を受け取り、apply(price) で price - amount を返します。main で Cart cart = new Cart(new FixedDiscount(200)); を作り、cart.checkout(1000) を表示してください。
ヒント1を見る
class FixedDiscount implements DiscountStrategy { private int amount; FixedDiscount(int amount) { this.amount = amount; } public int apply(int price) { return price - amount; } }。
ヒント2を見る
Cart はコンストラクタで最初の strategy を受け取れるので、new Cart(new FixedDiscount(200)) とそのまま渡せます。