I’m practicing refactoring and Java 8 with very simple examples of Powerball.
I used LottoRank enum to write a function that returns the matched LottoRank based on the number of matches and bonus balls.
-
The 1st place is when you hit all 6 numbers.
-
The 2nd place is when you hit 5 with bonus ball.
-
The 3rd place is when you hit 5 without bonus ball.
-
The 4th place is when you hit 4 numbers.
-
The 5th place is when you hit 3 numbers.
The logic for obtaining this value is as follows.
NONE(Integer.MIN_VALUE, 0),
FIFTH(3, 5000),
FOURTH(4, 50000),
THIRD(5, 1500000),
SECOND(5, 30000000),
FIRST(6, 2000000000);
private final int matchCount;
private final Money winnerMoney;
LottoRank(int matchCount, int winnerMoney) {
this.matchCount = matchCount;
this.winnerMoney = new Money(winnerMoney);
}
public boolean isCorrectMatchCount(int matchCount) {
return this.matchCount == matchCount;
}
public int calculateWinningMoney(Integer value) {
return winnerMoney.multiple(value);
}
public static LottoRank findRank(String winnerRank) {
return LottoRankPredicates.filterLottoRankWithString(winnerRank);
}
public static LottoRank valueOf(int matchCount, boolean bonusBall) {
return Arrays.stream(LottoRank.values())
.filter(rank -> isThirdOrSecond(matchCount) ?
filterIsSecond(bonusBall, rank.winnerMoney) : rank.matchCount == matchCount)
.findAny()
.orElse(LottoRank.NONE);
}
private static boolean isThirdOrSecond(int matchCount) {
return matchCount == LottoRank.THIRD.matchCount;
}
private static boolean filterIsSecond(boolean bonusBall, Money winnerMoney) {
return bonusBall ? winnerMoney.equals(LottoRank.SECOND.winnerMoney) : winnerMoney.equals(LottoRank.THIRD.winnerMoney);
}
public static LottoRank matches(List<Number> winningNumbers, List<Number> holdingLottoNumbers, Number bonusBall) {
int matchCount = Math.toIntExact(winningNumbers.stream()
.filter(holdingLottoNumbers::contains)
.count());
boolean isSecond = holdingLottoNumbers.contains(bonusBall);
return LottoRank.valueOf(matchCount, isSecond);
}
If you look at the filter part of valueOf()
, you will determine whether it is 2nd or 3rd (i.e., you got 5 correct). Based on whether the bonus ball has been hit or not, the logic is updated to 2nd place and if not, the ranking is processed according to the correct number.
I want to erase the trinomial operator in this part.
However, else statements and switch-case statements are not allowed.
I thought of a prefix or multiple filters, but eventually the first condition must be correct to run on the next condition, and this code will be generated.
It’s a code that didn’t work properly, but it’s like this.
public static LottoRank valueOf(int matchCount, boolean bonusBall) {
return Arrays.stream(LottoRank.values())
.filter(rank -> matchCount == rank.matchCount)
.filter(rank -> isThirdOrSecond(matchCount))
.filter(rank -> filterIsSecond(bonusBall, rank.winnerMoney))
.findAny()
.orElse(LottoRank.NONE);
}
Is there a way to refactory this more?
Edited
I was thinking about the above and i processed it with the LottoRankPredicates
class as below.
I wonder what you think about refactoring like this.
public class LottoRankPredicates {
private static Predicate<LottoRank> isSecondOrThird(boolean bonusBall) {
return rank -> getSecondOrThird(bonusBall, rank);
}
private static Predicate<LottoRank> defaultCase(int matchCount) {
return rank -> rank.isCorrectMatchCount(matchCount);
}
private static Predicate<LottoRank> findRank(String winnerRank) {
return rank -> rank.name().equals(winnerRank);
}
private static boolean getSecondOrThird(boolean bonusBall, LottoRank rank) {
if(bonusBall) {
return rank == LottoRank.SECOND;
}
return rank == LottoRank.THIRD;
}
public static LottoRank filterLottoRankWithString(String winnerRank) {
return Arrays.stream(LottoRank.values())
.filter(findRank(winnerRank))
.findAny()
.orElseThrow(IllegalArgumentException::new);
}
public static LottoRank filterLottoRankIsSecondOrThird(boolean bonusBall) {
return Arrays.stream(LottoRank.values())
.filter(isSecondOrThird(bonusBall))
.findAny()
.orElse(LottoRank.NONE);
}
public static LottoRank filterLottRankIsDefault(int matchCount) {
return Arrays.stream(LottoRank.values())
.filter(defaultCase(matchCount))
.findAny()
.orElse(LottoRank.NONE);
}
}
public static LottoRank findRank(String winnerRank) {
return LottoRankPredicates.filterLottoRankWithString(winnerRank);
}
public static LottoRank valueOf(int matchCount, boolean bonusBall) {
if(isThirdOrSecond(matchCount)) {
return LottoRankPredicates.filterLottoRankIsSecondOrThird(bonusBall);
}
return LottoRankPredicates.filterLottRankIsDefault(matchCount);
}
private static boolean isThirdOrSecond(int matchCount) {
return matchCount == LottoRank.THIRD.matchCount;
}