【Java】【DDD】NavigableMapを使った支払い料金に応じた獲得ポイントの計算

やりたいこと

以下のように支払い料金に応じてポイントを獲得するというビジネスルールをJavaで実装する方法を考えます。

支払い料金獲得ポイント
1,000円未満0ポイント
1,000円以上、2,000円未満100ポイント
2,000円以上200ポイント

if文を使った条件分岐による実装

表の条件を素直にif文で実装すると以下のようになります。

public class PointTable1 {

    public int calculatePointsOnPurchase(int paymentAmount) {

        if (paymentAmount < 1000) {
            return 0;
        } else if (paymentAmount < 2000) {
            return 100;
        }

        return 200;

    }
}

NavigableMapを使った実装

NavigableMapに用意されているfloorEntryメソッドを利用することでif文を使うことなく上記のポイント表を実装することができます。支払い料金に対するポイント計算の部分がメインであるため、支払い料金、ポイント数はintで表現しています。

import java.util.Map;
import java.util.NavigableMap;
import java.util.Optional;
import java.util.TreeMap;

public class PointTable2 {

    private NavigableMap<Integer, Integer> table;

    public PointTable2() {
        table = new TreeMap<>();
        table.put(1000, 100);
        table.put(2000, 200);
    }

    public int calculatePointsOnPurchase(int paymentAmount) {
        return Optional.ofNullable(table.floorEntry(paymentAmount))
                .map(Map.Entry::getValue)
                .orElse(0);
    }
}

floorEntryメソッドは指定されたキー以下の最大のキーに紐づくエントリを返します。該当するエントリが存在しない場合はnullを返します。

上記のコードでは最小のキーは1,000であるため、キーが999以下の場合にtable.floorEntry(paymentAmount)はnullを返します。このnullを0に変換するためにOptionalクラスを使用しています。

テスト

単体テストを以下のように作成しました。JUnit5の@ParameterizedTestと@CsvSourceの組み合わせは便利ですね。

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

public class PointTableTest {


    @ParameterizedTest
    @CsvSource({
            "0, 0",
            "999, 0",
            "1000, 100",
            "1999, 100",
            "2000, 200",
            "2001, 200",
    })
    public void pointTable1Test(int paymentAmount, int expectedPoint) {

        var pointTable = new PointTable1();
        assertThat(pointTable.calculatePointsOnPurchase(paymentAmount)).isEqualTo(expectedPoint);
    }

    @ParameterizedTest
    @CsvSource({
            "0, 0",
            "999, 0",
            "1000, 100",
            "1999, 100",
            "2000, 200",
            "2001, 200",
    })
    public void pointTable2Test(int paymentAmount, int expectedPoint) {

        var pointTable = new PointTable2();
        assertThat(pointTable.calculatePointsOnPurchase(paymentAmount)).isEqualTo(expectedPoint);
    }
}

参考情報