로또 게임(2등)
기능요구사항
•
2등을 위해 추가 번호를 하나 더 추첨한다.
•
당첨 통계에 2등도 추가해야 한다.
[... 생략 ...]
지난 주 당첨 번호를 입력해 주세요.
1, 2, 3, 4, 5, 6
보너스 볼을 입력해 주세요.
7
당첨 통계
---------
3개 일치 (5000원)- 1개
4개 일치 (50000원)- 0개
5개 일치 (1500000원)- 0개
5개 일치, 보너스 볼 일치(30000000원) - 0개
6개 일치 (2000000000원)- 0개
총 수익률은 0.35입니다.(기준이 1이기 때문에 결과적으로 손해라는 의미임)
Java
복사
프로그래밍 요구사항
•
모든 기능을 TDD로 구현해 단위 테스트가 존재해야 한다. 단, UI(System.out, System.in) 로직은 제외
•
java enum을 적용해 프로그래밍을 구현한다.
•
규칙 8: 일급 콜렉션을 쓴다.
•
indent(인덴트, 들여쓰기) depth를 2를 넘지 않도록 구현한다. 1까지만 허용한다.
•
함수(또는 메소드)의 길이가 15라인을 넘어가지 않도록 구현한다.
•
자바 코드 컨벤션을 지키면서 프로그래밍한다.
•
else 예약어를 쓰지 않는다.
힌트
•
일급 콜렉션을 쓴다.
◦
6개의 숫자 값을 가지는 java collection을 감싸는 객체를 추가해 구현해 본다.
•
하드 코딩을 하지 않기 위해 상수 값을 사용하면 많은 상수 값이 발생한다. 자바의 enum을 활용해 상수 값을 제거한다. 즉, enum을 활용해 일치하는 수를 로또 등수로 변경해 본다.
기능 목록 및 commit 로그 요구사항
•
기능을 구현하기 전에 README.md 파일에 구현할 기능 목록을 정리해 추가한다.
•
git의 commit 단위는 앞 단계에서 README.md 파일에 정리한 기능 목록 단위로 추가한다.
피드백
리뷰어 : 이 enum에서만 관리하면 되므로, enum에서 해당 필드를 public하게 노출시키고 여기 이 추출들은 제거하는 것이 조금 더 자연스러운 구현인 듯 합니다.
생각정리 : 이넘에서 표현해 주었는데, 내부에서 굳이 필드를 WinningAmountByRank 으로는 줄필요가 없을 것같다.
public enum WinningAmountByRank {
FIRST(WinningAmountByRank.FIRST_PLACE, 2000000000, "FIRST_PLACE"),
SECOND(WinningAmountByRank.SECOND_PLACE, 1500000, "SECOND_PLACE"),
THIRD(WinningAmountByRank.THIRD_PLACE, 50000, "THIRD_PLACE"),
FOURTH(WinningAmountByRank.FOURTH_PLACE, 5000, "FOURTH_PLACE"),
EMPTY(WinningAmountByRank.EMPTY_PLACE, 0, "EMPTY_PLACE");
public static final int FIRST_PLACE = 6;
public static final int SECOND_PLACE = 5;
public static final int THIRD_PLACE = 4;
public static final int FOURTH_PLACE = 3;
public static final int EMPTY_PLACE = 0;
Java
복사
•
다음과 같이 변경
public enum WinningAmountByRank {
FIRST(6, 2000000000, "FIRST_PLACE"),
BONUS(37, 30000000, "BONUS_PLACE"),
SECOND(5, 1500000, "SECOND_PLACE"),
THIRD(4, 50000, "THIRD_PLACE"),
FOURTH(3, 5000, "FOURTH_PLACE"),
EMPTY(0, 0, "EMPTY_PLACE");
private final int rank;
private final int amount;
private final String key;
Java
복사
리뷰어 : LottoService 에서 또 의견을 드리겠지만, 서비스 객체가 파라메터에 따라 생성되는 것이 조금 신기하고 어색하게 느껴집니다. 생성자로 lottoCount 가 넘어가야 할 이유가 있으려나요?
생각정리 : 서비스객체를 생성자를 써서 필드를 남길필요가 없었는데, 싱글톤 패턴이라고 생각했을 때 Thread safe에 맞지 않을 것 같았다.
public class LottoService {
private Lottos lottos;
public LottoService(int lottoCount) {
this.lottos = new Lottos(lottoCount);
}
Java
복사
•
다음과 같이 변경
◦
singleton 적용
public class LottoService {
// singleton 적용
private static LottoService lottoService = null;
public static LottoService createLottoService() {
if (Objects.isNull(lottoService)) {
return new LottoService();
}
return lottoService;
}
Java
복사
리뷰어 : 스트림 기반으로 개선해 보시면 좋겠네요!
생각정리 : 스트림 안에서 너무 많은 기능을 첨부하는 소스코드를 보았다. 복잡해서 거부감이 있었는데, 하나의 기능에 집중하는 스트림을 사용한다면, 충분히 가독성 면에서도 좋다고 생각된다.
public void calculatorProfit() {
double winningAmount = 0;
for (Lotto lotto : lottos.getLottos()) {
int winningCount = getWinningResult(lotto);
WinningAmountByRank from = WinningAmountByRank.from(winningCount);
winningResult.put(from.getKey(), winningResult.getOrDefault(from.getKey(), 0) + 1);
winningAmount += from.getAmount();
}
profit = winningAmount / purchaseAmount;
}
Java
복사
•
다음과 같이 변경
public double calculatorProfit(Lottos lottos, int purchaseAmount) {
double sum = lottos.getLottos()
.stream()
.mapToInt(lotto -> WinningAmountByRank.from(getWinningResult(lotto)).getAmount()).sum();
return sum / purchaseAmount;
}
Java
복사
후기
오늘은 로또 3단계 로또 게임 2등을 진행하였다. 서비스 객체로 생각했을 때, 필드값을 지정해 준다면 과연 thread safe에 안전한가에 대해서 다시 한번 생각할 수 있는 기회였다. 테스트 코드도 객체를 완성할 때마다 작성할 수 있도록 구현해 보자!
깃허브링크 :
출처