어제 설계가 잘 안되어 많이 힘들었는데요. 다행히 오늘은 어제보다 진전이 좀 있었네요. 정리해볼게요.
어제 막혔던 부분
- 보너스로 뽑는 2등을 어떻게 담아야 할까
- LottoChecker의 역할
- enum의 활용
여기에서 LottoChecker
라는 클래스의 역할은 2등을 담는 방법에 대한 고민과 무관하지 않은 것 같아요. 왜냐하면 LottoChecker
에서 로또 체크에서 나오는 값들을 토대로 등수를 정해서 2등을 담아야할 부분이 나오게 될테니까요.
enum의 활용
먼저 'enum의 활용'과 관련된 부분부터 시작하겠습니다. 어제 같은 과정 멤버들이 가르쳐준 자료에서 큰 힌트를 얻을 수 있었는데요. 자료 내용은 다음과 같습니다.
public enum Rank {
FIRST(6, 2000000000),
SECOND(5, 30000000),
THIRD(5, 1500000),
FOURTH(4, 50000),
FIFTH(3, 5000);
[...]
public static Rank valueOf(int countOfMatch, boolean matchBonus) {
Rank[] ranks = values();
for (Rank rank : ranks) {
if (countOfMatch == SECOND.countOfMatch) {
return matchBonus ? SECOND : THIRD;
}
if (rank.countOfMatch == countOfMatch) {
return rank;
}
}
return null;
}
}
enum활용과 관련하여 values()
메서드를 이용하여(지금보니 Rank.values()
로 클래스명 안 붙여도 되군요..) enum의 상수값들을 배열로 받은 다음 조건에 맞는 상수값을 찾는 과정입니다. 아쉽네요.. 왜냐하면 저도 이런 식으로 접근했었거든요.
public static Rank getLottoPrize(int strikeNum) {
for (Rank value : Rank.values()) {
if (value.isEqual(strikeNum)) {
return value;
}
}
return null;
}
하지만 중요한 다른 점이 있었다면 matchBonus
라는 boolean
값을 두어 2등과 3등을 구분한 부분입니다. 전에 pair프로그래밍 할 때 같이 하시던 분들이 이런 방법을 썼었는데 그 방법이 잘 안 떠올랐네요. 체크할 수 있는, 일종 flag 같은 값을 같이 넘겨주는 것이죠. 위의 코드를 참고하여 기존 if문 위에 2등과 3등을 구별해주기 위한 if문을 추가하고, 구별을 도와주는 strikeBonus
라는 boolean
값을 넘겨주도록 개선하였습니다. 이 과정을 통해서 '일치하는 숫자 개수', '보너스 값 일치 유무'로 로또 등수를 알 수 있게되었습니다!
LottoChecker의 역할
결국 등수를 구별하기 위해 넘겨줘야 할 정보가 '일치하는 숫자 개수', '보너스 값 일치 유무'로 분명해졌는데요. 이 2개의 값을 각 로또 별로 하나씩 만들어줘야 하는 것입니다. Collection
을 이용해 구성할 수도 있겠지만 하나의 클래스로 만들어 그것을 List
담아 보관하는게 깔끔하겠다는 판단을 하였습니다. 따라서 StrikeDto
라는 클래스를 만들고 그 안의 인스턴스 변수로 strikeNum
, strikeBonus
로 구성하였고 getter메서드를 만들어주었습니다.
결국 이렇게 로또 체크하고 담는 틀을 고려하니 LottoChecker가 해야할 역할이 좀 더 분명해졌습니다.
여기서 추가적인 고민이 있었는데요.
보너스 체크를 Lotto에 물어볼까? WinnerLotto에게 물어볼까?
결론적으로 WinnerLotto에 메시지를 보내 결과값을 받도록 하였습니다. 그 이유는 다음과 같습니다.
strikeCheck()
는Lotto
에 있던WinnerLotto
에 있던 상관없지만 상위 클래스인Lotto
에 있어서 하위 클래스에서도 쓸 수 있게 할 것- 하지만 bonus는
Lotto
에서 체크하려면 getter 메서드가 필요하고 또 getter를 안 쓰려면 1~45까지 물어봐야 하므로 보너스값에 바로 접근 가능한WinnerLotto
에서 처리할 것
2등을 어떻게 담아야할지
2등을 어떻게 담아야할지 결정하는 부분은 ResultDto
와 연관되어 있습니다. 먼저 든 생각은 ResultDto
는 값을 보내주는 매개체니까 그냥 순서대로 넣자는 것이었습니다. 즉 1등은 인덱스 0, 2등은 인덱스 1 ... 이런 식으로 말이죠. 그러다가 다음과 같은 생각이 들었습니다.
ResultDto의 key값을 Rank로 넣을 수는 없을까?
테스트 코드에서 여러 경우를 테스트해보니 이렇게 넣는 것이 가능했고 미션에서 요구하는 출력부도 이 Rank의 상수값의 정보들을 불러와 일부분 해결할 수 있었습니다.
이제는 '수익률'과 '일치 개수에 따른 로또 수'만 구하면 됩니다.
key값을 Rank로 받고 '일치 개수에 따른 로또 수'를 구할 때 어떻게 증감할 것인지 고민이 다시 뒤따랐는데요. HashMap에서 get()
을 하고 value에 대해 바로 ++하면 증감이 안되더라구요. 그러니까 다음과 같이는 안되더라구요.
@Test
public void HashMap_Value값_증감_Test() {
Map<Rank, Integer> map = new HashMap<>();
map.put(Rank.FIRST, 0);
map.get(Rank.FIRST)++; // 이렇게 증감이 안됨!!
map.put(Rank.FIRST, map.get(Rank.FIRST) + 1); // 증가된 값을 다시 넣도록 할 것
}
따라서 위와 같이 get()
으로 값을 받은 다음 그 값을 증가시킨 다음 다시 put()
하는 과정이 필요하였습니다.
그리고 또 다른 고민은 HashMap 초기화와 관련된 부분인데요. 이 부분이 왜 문제가 되냐하면 초기화를 안해두면 '일치 개수에 따른 로또 수'가 없는 경우, 예를들어 로또들 중 WinningLotto
와 5개가 일치하는 로또가 하나도 없을 때 Map
에 넣어둔 값이 없어 null
값이 출력되기 때문입니다. 그렇다고 출력부에서 null
값에 대한 분기는 View영역에 있으면 안된다고 생각했습니다. 그 뿐만 아니라 초기화가 없으면 key
유무를 조사해 있으면 증감시키고, 없으면 새로 put()
해줘야 하는 불필요한 분기가 필요합니다. 따라서 initGameResult()
메서드를 통해 다음과 같이 0으로 초기화했습니다.
private void initGameResult(Map<Rank, Integer> gameResult) {
for (Rank rank : Rank.values()) {
gameResult.put(rank, 0);
}
}
마지막으로 ResultDto
을 만들 때 하나의 메서드에서 '등수와 등수 개수에 관련된 Map'과 '수익률' 계산 후 담았는데요. 이렇게 되니 메서드에서 하는 일이 2개 이상으로 커지고 역할만큼이나 길이가 길어졌습니다. 따라서 '등수와 등수 개수에 관련된 Map'를 처리하는 makeGameResult()
와 수익률을 계산하는 calculateProfitRate()
를 만들고 Controller에서 그 값들을 받아 ResultDto를 구성해 바로 출력부를 넘겨주는 작업을 하였습니다.
어제 설계를 안하고 바로 시작하기도 하였고 enum개념도 익숙하지 않아 많은 어려움이 있었지만 오늘 enum활용에 대한 힌트와 설계에 대한 고민과 그 해결책을 기록해가면서 진행하니 방향이 어느정도 보였습니다. 테스트 코드를 통해 여러 실험(?!)을 통해서도 배울 수 있는 부분도 많았구요. 또 다시 어려움을 만나면 막히겠지만 그것을 뚫을 저만의 방법을 좀 더 연마해야겠습니다.
'TIL' 카테고리의 다른 글
Today's Dev Notes(2018-10-28) (0) | 2018.10.29 |
---|---|
Today's Dev Notes(2018-10-25) (0) | 2018.10.25 |
Today's Dev Notes(2018-10-23) (2) | 2018.10.24 |
Today's Dev Notes(2018-10-19) (0) | 2018.10.20 |
Today's Dev Notes(2018-10-18) (0) | 2018.10.19 |