[Ava]戦略モデル


いくつかの機能では、特定の基準に基づいてプロセスを変更する必要がある場合があります.例えば、果物屋で果物を売っているとします.イベントの最初のお客様は10%割引で、新鮮でない果物は20%割引です.計算が必要な金額を決定する際には、対첫 손님인지과일이 신선한지 아닌지の条件によって結果が異なる.購入する果物が3000ウォンなら、最初の顧客は2700ウォン、果物が新鮮でなければ2400ウォンを受け取る.次のように、この状況をコードに記述します.
public class Calculator {

    public int calculate(final boolean firstGuest, final List<Item> items) {
        int sum = 0;
        for (Item item : items) {
            if (firstGuest) {
                sum += (int) (item.getPrice() * 0.9);
            } else if (!item.isFresh()) {
                sum += (int) (item.getPrice() * 0.8);
            } else {
                sum += item.getPrice();
            }
        }
        return sum;
    }
}
if文を用いて条件を満たす処理を行う.値は正確に計算できますが、メンテナンスが難しいという欠点があります.条件を追加するために他のアクティビティを追加する必要がある場合は、既存の条件の相関を考慮するのは難しいです.エラーを修正すると、他の論理にも影響を与える可能性があります.コードだけを見て、どういう意味で書かれたコードなのかを知るのは難しい.
同じ機能に対して異なる内部論理を行う必要がある場合は、戦略モデルを使用します.

戦略モデルとは?


戦略図案は동일한 기능에서 다른 알고리즘을 구현해야할 때 사용の設計図案である.上記の例のポリシー・モードを使用してクラス図を構築します.

既存の計算機オブジェクトが実行する計算()機能を他のオブジェクトに内部で実行させます.各条件に基づいてオブジェクトを生成し、条件を満たす処理を行う.どのポリシーを使用するかに応じて、同じメソッドを呼び出して異なる値を取得し、DiscountStrategyをインタフェースとして生成した後、各詳細ポリシーはインタフェースを実現する.
既存のif文を削除し、ポリシー・モードを使用するコードは次のとおりです.計算機オブジェクトを生成するときに使用するポリシーを入力し、入力ポリシーに一致する結果を返します.
public class Calculator {

    private final DiscountStrategy discountStrategy;

    Calculator(final DiscountStrategy discountStrategy) {
        this.discountStrategy = discountStrategy;
    }

    public int calculate(final List<Item> items) {
        int sum = 0;
        for (Item item : items) {
            sum += discountStrategy.getDiscountPrice(item);
        }
        return sum;
    }
}
DiscountStrategyインタフェースのコードは、次のように内部論理の共通点をメソッドで囲みます.様々な戦略で割引価格が必要なため、方法名はgetDiscountPriceで、入力値は購入する果物を受け入れる.
public interface DiscountStrategy {

    int getDiscountPrice(final Item item);
}
これはifクエリの最初の条件첫 손님일 경우 10프로 할인に対する戦略である.条件自体はオブジェクトに置き換えられ,条件の実現は方法に置き換えられる.
public class FirstGuestDiscountStrategy implements DiscountStrategy {

    @Override
    public int getDiscountPrice(final Item item) {
        return (int) (item.getPrice() * 0.9);
    }
}
これは2番目の条件신선하지 않은 과일일 경우 20프로 할인に対する戦略です.
public class NonFreshItemDiscountStrategy implements DiscountStrategy {

    @Override
    public int getDiscountPrice(final Item item) {
        return (int) (item.getPrice() * 0.8);
    }
}
参考書のサンプルコードには含まれていませんが、新鮮な果物を購入するときに計算できるように、関連オブジェクトも追加されています.if文でのelseの処理に相当します.
public class OriginalDiscountStrategy implements DiscountStrategy {

    public int getDiscountPrice(final Item item) {
        return item.getPrice();
    }
}
上記の戦略モデルを適用したコードが正常に動作することを確保するために,テストコードを作成した.3つのテストコードが合格したことを確認できます.
public class CalculatorTest {

    @Test
    @DisplayName("사과 금액 계산")
    void calculateApple() {
        Calculator calculator = new Calculator(new OriginalDiscountStrategy());
        List<Item> items = new ArrayList<>(List.of(new Apple()));

        assertThat(calculator.calculate(items)).isEqualTo(3000); // 3000
    }

    @Test
    @DisplayName("첫번째로 온 손님이 산 사과 금액 계산")
    void calculateAppleToFirstGuest() {
        Calculator calculator = new Calculator(new FirstGuestDiscountStrategy());
        List<Item> items = new ArrayList<>(List.of(new Apple()));

        assertThat(calculator.calculate(items)).isEqualTo(2700); // 2700
    }

    @Test
    @DisplayName("썩은 사과 금액 계산")
    void calculateRottenApple() {
        Calculator calculator = new Calculator(new NonFreshItemDiscountStrategy());
        List<Item> items = new ArrayList<>(List.of(new Apple()));

        assertThat(calculator.calculate(items)).isEqualTo(2400); // 2400
    }
}

戦略モデルを用いて,上述したif文の欠点を解決した.実装されたクラスの名前で、他のポリシーを追加してもDiscountStrategyインタフェースを実装するオブジェクトを追加できる機能を明確に示します.メンテナンスが容易です.実際、마지막 손님일 경우 대폭 할인戦略を追加するのも便利かどうか.任意の10%オフの金額で計算してみます.
public class LastGuestDiscountStrategy implements DiscountStrategy {

    @Override
    public int getDiscountPrice(final Item item) {
        return (int) (item.getPrice() * 0.1);
    }
}
ポリシーが追加されたテストケースを含め、すべてのテストが合格したことを確認できます.
    @Test
    @DisplayName("마지막으로 온 손님이 산 사과 금액 계산")
    void calculateAppleToLastGuest() {
        Calculator calculator = new Calculator(new LastGuestDiscountStrategy());
        List<Item> items = new ArrayList<>(List.of(new Apple()));

        assertThat(calculator.calculate(items)).isEqualTo(300);
    }

リファレンス

  • オブジェクト向けと設計モデルは開発者が征服しなければならない-Chapter 07の主な設計モデル