목차
1. GitHub 저장소
2. 새로운 요구사항
3. 리팩토링
4. 생각하기
1. GitHub 저장소
step 1(리팩토링 전): https://github.com/yeon-06/java-lotto/tree/step1
step 2(리팩토링 후): https://github.com/yeon-06/java-lotto/tree/step2
Pull Request: https://github.com/woowacourse/java-lotto/pull/454
2. 새로운 요구사항
수동 로또
- (e) 구입한 금액보다 많이 구매했는지 확인
- 수동으로 구매할 로또 개수 입력
- 수동 로또 번호 입력
3. 리팩토링
3-1. String.format() 사용
int MIN = 1;
int MAX = 45;
// AS-IS
String NUMBER_RANGE_ERROR = "로또 숫자는 " + MIN + " 이상 " + MAX +" 이하의 숫자만 가능합니다.";
// TO-BE
String NUMBER_RANGE_ERROR = String.format("로또 숫자는 %d 이상 %d 이하의 숫자만 가능합니다.", MIN, MAX);
3-2. 캐싱된 인스턴스의 자료형 Map으로 변경
// AS-IS
private static final LottoNumber[] cacheLottoNumber = new LottoNumber[MAX + 1];
// TO-BE
private static final Map<Integer, LottoNumber> cacheLottoNumber = new HashMap<>();
👉 현재는 저장되는 값도 저장하는 값의 의미도 숫자다.
하지만 특정 문자열 등이 캐싱되어야 하는 경우 등에서 Array나 List 등은 값을 꺼내올 때 의미를 분명히 하는데 한계가 있다고 판단했다.
3-3. 테스트 코드 given, when, then 분리
- given: 테스트를 위한 준비 과정
- when: 테스트를 하고자 하는 행위(기능)
- then: 검증
예제
@Test
@DisplayName("LottoTicket 추가")
void addLottoTicket() {
// given
LottoTicket lottoTicket1 = LottoTicket.createAutoLottoTicket(3);
LottoTicket lottoTicket2 = LottoTicket.createAutoLottoTicket(2);
// when
lottoTicket1.addLottoTicket(lottoTicket2);
// then
int lottoCount = lottoTicket1.getLottoTicket().size();
assertThat(lottoCount).isEqualTo(5);
}
3-4. @ParameterizedTest와 파라미터 여러개 받기
별도로 작성한 포스팅이 있어 생략한다.
👉 https://yeonyeon.tistory.com/198
3-5. Predicate의 활용
별도로 작성한 포스팅이 있어 생략한다.
👉 https://yeonyeon.tistory.com/200
4. 생각하기
📑: 상황, 🙋♀️: 연로그(본인), 🙍♂️: 닉(리뷰어)
4-1. 정적 팩토리 메소드 - 이름 붙이기
📑 LottoTicket의 발급 방법이 수동, 자동 두 가지가 생겼다.
여기서 자동 발급을 하는 경우 new 키워드를 통해 생성자 호출할 때 자동 발급할 티켓 개수를 넘겨주었다.
🙍♂️ LottoTicket을 만드는데 생성자에 단순한 숫자를 넣어주니까 의미 전달이 조금 약한 것 같아요.
(물론 autoTicketCount 라는 변수명으로 어느정도 유추할 수는 있겠지만)
정적 팩토리 메서드를 사용해서 의미를 명확하게 해보면 어떨까요? :)
🙋♀️ 리팩토링 된 코드.
자동 로또를 발급 받는지 수동 로또를 발급 받는지 의미가 명확해졌다.
public class LottoTicket {
private final List<LottoNumbers> lottoTicket;
private LottoTicket(final List<LottoNumbers> lottoTicket) {
this.lottoTicket = lottoTicket;
}
public static LottoTicket createAutoLottoTicket(int count) {
return new LottoTicket(generateTickets(count));
}
private static List<LottoNumbers> generateTickets(int count) { ... }
public static LottoTicket createManualLottoTicket(List<LottoNumbers> lottoTicket) {
return new LottoTicket(lottoTicket);
}
}
4-2. 테스트 확인 또 확인
📑 누락된 테스트 케이스를 급하게 추가하다가 다른 테스트 케이스가 깨지는 경우가 생겼다.
제출 전 전체 테스트 다 통과하는지 꼭꼭 확인 또 확인하기😭
4-3. 적절한 VO의 생성
🙋♀️ 수동 로또 개수를 검증하는 로직을 분리하고 싶어서 ManualLottoCount라는 vo를 생성했습니다.
다만 ManualLottoCount에 저장한 값이 계속 필요해서 사용할때마다 getValue()를 호출하게 되네요.
이게 적절한 VO의 생성이 맞나?라는 의문이 계속 들어요😂
🙍♂️ 값을 사용해야 하는 시점에서는 어쩔 수 없죠 ㅎㅎ
그게 아니라면 public 메서드를 열어서 내부에서 처리하게끔 해야하는데, 아래 count 계산 로직의 경우 책임 분리의 관점에서 애매한 부분이 있을수도 있겠네요 :)
4-4. protected와 default
🙋♀️ default와 protected 중 고민했으나 나중에 LottoNumber을 상속하는 클래스가 생기면 해당 클래스의 패키지에서도 사용할 일이 생기지 않을까? 라고 예상했습니다.
하지만 LottoNumber을 상속하는 경우가 생기지 않을 것 같기도 하고... 뭘 사용해야 더 적절한지에 대한 고민이 생겼습니다.
닉은 default와 protected를 사용하는 기준 같은게 따로 있으실까요?
🙍♂️ 일단 저는 상수 같은 경우는 public으로 열긴 하는데요. (불변 상수기 때문에 접근제어를 fit하게 해봤자 큰 의미 없다고 생각해서)
저도 해당 시점에 판단해서 상속이 필요할 것 같으면 protected, 패키지 내에서만 사용할 것 같으면 default로 둡니다.
사실 자바에서 패키지 기반으로 접근 제어를 한다는 사실이 맘에 들진 않아요 ㅋㅋ (코틀린을 쓰면 그런 고민할 필요가 없습니다 갓틀린 짱짱..)
답은 없으니 각 제어자 목적에 맞게 고민하고 판단하셔서 결정하시면 됩니다 :)
👉 이 부분에 대해서는 별도로 좀 더 자세히 공부해보았다.
https://yeonyeon.tistory.com/203
'Memo > 우테코 4기' 카테고리의 다른 글
[우테코] 블랙잭 미션 2단계 학습 로그 (2) | 2022.03.20 |
---|---|
[우테코] 블랙잭 미션 1단계 학습 로그 (0) | 2022.03.15 |
[우테코] 로또 미션 1단계 학습 로그 (0) | 2022.02.28 |
[우테코] 자동차 경주 2단계 학습 로그 (4) | 2022.02.23 |
[우테코] 자동차 경주 1단계 학습 로그 (0) | 2022.02.16 |