if文による条件分岐の複雑さ
ある日が過去・現在・未来なのか判定し、それに応じて処理を分けるとします。これをif文を使って実装すると以下のようなコードになります。実際、この程度ならば見通しがそこまで悪いわけではありませんが、更に細かく条件を分けようとすると、複雑な分岐を読み解く必要があります。
public static void main(String[] args) {
//変数:pastの時制を判定したい
LocalDate past = LocalDate.of(2019, 10, 11);
LocalDate today = LocalDate.now(); //現在は(2019-10-12)
if(past.isBefore(today)) {
System.out.println("Past");
}
if(past.isEqual(today)) {
System.out.println("Today");
}
if(past.isAfter(today)) {
System.out.println("After");
}
//出力: Past
}
「if文による条件分岐」よりも「条件-処理をまとめた設定」を
####Strategy Enumを使うと、条件とそれに応じた処理を1対1で記述することができます。
まず、Enum(DatePattern)のフィールドに次の関数型インターフェイスを2つ定義します
1. 条件: Predicate
2. 処理: Runnnable
次に、ここではEnumのクラスメソッド of()として、Factory Methodを記述します。Factory Methodは引数で受け取った日付から時制を判定し、それに応じた処理を引き当て、返り値として返却します。
このようにすると、条件と処理を設定 として記述できます。
public enum DatePattern {
//(条件, 処理)の設定
Past(date -> isBeforeToday(date), () -> System.out.println("Past")),
Today(date -> isSameToday(date), () -> System.out.println("Today")),
Future(date -> isAfterToday(date), () -> System.out.println("Future"));
private final Predicate<LocalDate> dateValidator; //条件
private final Runnable function; //処理
//Enumのコンストラクタ
DatePattern(Predicate<LocalDate> dateValidator, Runnable function) {
this.dateValidator = dateValidator;
this.function = function;
}
/* Factory Method
* 引数のLocal Dateの時制をフィールドで定義した条件(Predicate)で判定して、
* 処理(Runnnable)を返却する
*/
public static Runnable of(LocalDate date) {
Optional<Runnable> pattern = Stream.of(DatePattern.values())
.filter(e -> e.dateValidator.test(date))
.map(e -> e.function)
.findFirst();
return Optional.ofNullable(pattern.get()).orElseThrow(IllegalArgumentException::new);
}
// 過去かどうかの判定
private static boolean isBeforeToday(LocalDate date) {
LocalDate today = LocalDate.now();
return date.isAfter(today);
}
// 未来かどうかの判定
private static boolean isAfterToday(LocalDate date) {
LocalDate today = LocalDate.now();
return date.isBefore(today);
}
// 現在かどうかの判定
private static boolean isSameToday(LocalDate date) {
LocalDate today = LocalDate.now();
return date.equals(today);
}
}
Enumを使った結果、以下のようにif文を消すことができます。条件分岐が無いため、見通しを良くすることができました。また、更に細かい判定条件を追加しても、このDatePatternの呼び出し元に影響はありません。
if文による条件分岐を次々に繰り返すのではなく、 Enumに条件と処理をワンセットにして設定 する方が個人的に見通しが高く、保守性が高まると考えています。
public static void main(String[] args) {
LocalDate past = LocalDate.of(2019, 10, 11); //過去
LocalDate today = LocalDate.of(2019, 10, 12); //現在(2019-10-12)
LocalDate future = LocalDate.of(2019, 10, 13); //未来
Runnable past_function = DatePattern.of(past);
Runnable today_function = DatePattern.of(today);
Runnable futute_function = DatePattern.of(future);
past_function.run();
// 出力: Past
today_function.run();
// 出力: Today
futute_function.run();
// 出力: Future
}