[2단계 - 자동차 경주 리팩터링] 오잉(이하늘) 미션 제출합니다.#588
Conversation
(기존) RacingCar객체의 디폴트 위치가 1 -> getCurrentRound 메소드에서 RacingCar들의 현재 상태 반환 (수정) RacingCar객체의 디폴트 위치가 0 -> getStartStatus 메소드에서 RacingCar들을 한칸씩 전진시킨 후 RacingCar들의 현재 상태 반환
1. validator를 인스턴수 변수로 수정 2. validate 검사 후 재입력 로직을 반복문에서 재귀로 수정
1. AdvanceJudgementTest 전진 불가능 경우 테스트에 @DisplayName 추가 2. CarNamesValidatorTest와 TryCountValidatorTest 통합
|
안녕하세요 오잉, 리뷰어 카프카입니다. 객체지향 생활 체조를 보면 getter/setter 사용을 지양하라는 원칙이 있지요?
위의 두 가지 예시를 보면 느낌이 오시죠? 제가 생각하는 기준은 다음과 같습니다.
추가로, 리뷰에 적기 길어서 RacingCar 개선에 대한 코멘트도 여기 남겨두겠습니다. RacingCar 클래스를 개선한 걸 봤는데, 좋은 부분은 다음과 같습니다.
다만 아쉬운 부분도 있었습니다. |
include42
left a comment
There was a problem hiding this comment.
안녕하세요, 오잉! 리뷰어 카프카입니다.
리팩토링한 내용 잘 확인하였습니다. 코드가 이전보다 많이 개선된 걸 느낄 수 있었습니다. 💯
아직 수정이 필요한 부분에 코멘트 달아두었으니, 확인을 부탁드립니다.
질문주신 내용 + 도메인 코드 리팩토링에 대한 조언은 코멘트를 별도로 달아두었으니 확인해주세요.
조금만 더 힘내봅시다! 고생 많으셨습니다 💪
|
|
||
| private void plusPoint() { | ||
| point++; | ||
| position++; |
There was a problem hiding this comment.
이부분에서는 두 가지 개선이 필요해 보입니다.
- 아직 plusPoint라는 메소드명이 변경되지 않았습니다.
- ++ 연산 대신, 위에 상수를 정의해두고 해당 값을 사용하면 어떨까요?
| private final int max; | ||
|
|
||
| public Range(int min, int max) { | ||
| if (max < min) { |
There was a problem hiding this comment.
이 부분은 저번 리뷰때 언급된 부분이네요.
물론 swap을 하는 것도 좋지만, 일단 이 상황이 발생했다는건 코드에 뭔가 심각한 실수가 있다는 뜻이죠. 그래서 그걸 명확히 알 수 있다면 좋을 것 같습니다.
그래서 제 생각엔, 예외를 발생시키는게 좋지 않을까 싶습니다.
There was a problem hiding this comment.
말씀하신대로 예외를 던지고 프로그램이 종료되도록 수정했습니다.
근데 어떠한 예외를 발생시키는 것이 좋을지 고민이 됩니다.
저는 IllegalArgumentException을 발생시켰는데, 좀 더 적절한 예외가 있을까요..?!
There was a problem hiding this comment.
IllegalArgumentException이면 적절하다고 생각합니다! 😄
|
|
||
| outputView.printResultHeader(); | ||
| outputView.printRoundResult(roundManager.getCurrentRound()); | ||
| outputView.printRoundResult(roundManager.getStartStatus()); |
There was a problem hiding this comment.
혹시 RoundManager.getStartStatus 메소드의 역할이 무엇일까요?
해당 메소드가 필요한 이유에 대해 고민해보면 좋을 것 같네요.
There was a problem hiding this comment.
제시된 실행결과를 보면 시도할 횟수+1회가 출력이 되는데, 그 중 첫번째 출력값은 기본세팅(모든 차들이 1에 위치)이더라구요! 그래서 본격적인 게임 시작전에 기본세팅+기본세팅출력을 위해 해당 메소드를 구현했습니다.
| private static final String PARSING_EXCEPTION_MESSAGE = "빈 입력입니다."; | ||
|
|
||
| public static List<String> parsing(String text, String delimiter) { | ||
| if (text == null || text.equals("")) { |
There was a problem hiding this comment.
isBlank에 대해 검색해보시면 더 간단하게 리팩토링이 가능해 보입니다.
There was a problem hiding this comment.
그리고 delimiter를 외부에서 전달받고 있는데, parser 가 가져도 괜찮지 않을까요?
책임을 어느 쪽에서 가진다고 보면 좋을지 고민해봅시다. 😄
There was a problem hiding this comment.
자동차 경주 프로그램 이외에도 Parser가 사용될 수 있다고 생각해서 delimiter를 외부에서 전달받은 방식으로 구현했습니다!
하지만 이번 미션에서는 이름은 쉼표(,)를 기준으로 구분한다는 요구사항이 정확히 있으므로, 말씀하신대로 Parser가 delimiter를 클래스 변수로 가지고 있어도 좋을 것 같습니다 😄
There was a problem hiding this comment.
생각해보신 기준도 적절하다고 생각합니다. 다만 말씀해 주신대로 이번 미션에서는 delimiter가 가지고 있는 편이 좋을 것 같네요 👍
다음 미션에서 유사하게 Parser를 구현한다면 입력값에 따라 여러 delimiter가 쓰일 수도 있을듯한데, 이때엔 어떻게 하면 좋을지 페어와 논의를 해보기를 권해요 😄
| private static final String NAME_DUPLICATE_EXCEPTION_MESSAGE = "자동차 이름은 중복될 수 없습니다."; | ||
| private static final String TRYCOUNT_NUMERIC_EXCEPTION_MESSAGE = "시도할 횟수는 자연수만 가능합니다."; | ||
|
|
||
| public void validateNames(List<String> names) { |
There was a problem hiding this comment.
이름에 대해 종합적으로 검증해주도록 구조를 잘 짜 주셨습니다.
이렇게 작성해두면 이후 검증 항목이 추가되거나 삭제되어도 유지보수하기 어렵지 않겠네요. 💯
| @@ -22,6 +22,7 @@ class AdvanceJudgementTest { | |||
|
|
|||
There was a problem hiding this comment.
혹시 커밋하기 전에 all test를 돌려보셨나요?
제가 돌려봤을때에는 RoundManagerTest에서 2건 실패하는 테스트가 있네요.
해당 실패한 테스트는 기존 코드가 수정된 것을 테스트에 반영하지 않아서 실패한 것인데,
이러한 경우 (물론 TDD로 개발하면 제일 좋지만) 바로 테스트를 코드와 동기화해줘야 이후 생길 버그를 방지할 수 있습니다.
실패하는 테스트가 프로젝트에 있어서는 안됩니다. 이 부분은 앞으로 미션 진행하시면서, 꼼꼼하게 확인하는 습관을 권합니다 😄
|
|
||
| @ParameterizedTest() | ||
| @DisplayName("횟수 유효성 검사 테스트") | ||
| @ValueSource(strings = {"1", "3", "9", "100", "300", "500"}) |
There was a problem hiding this comment.
성공하는 케이스에 대해 적절하게 테스트 잘 작성해 주셨습니다. 👍
include42
left a comment
There was a problem hiding this comment.
안녕하세요, 오잉! 리뷰어 카프카입니다.
이번 리뷰에 대해 수정을 잘 해주셔서, 다시 확인해봤을때 크게 걸리는 부분이 없었습니다. 👍
단위 테스트도 잘 작성해 주셨고, DTO의 적용도 바로 잘 해주셨네요.
이번 미션에서 DTO에 대해, 그리고 getter, setter의 사용에 대해 고민을 많이 하셨다고 생각합니다.
새로 고민하고 학습한 부분에 대해 정리를 잘 해두시고, 다음 미션에서도 잘 활용하시면 좋겠습니다.
이번 미션은 여기서 approve 하겠습니다. 고생 많으셨습니다 😄
|
|
||
| public List<String> getCurrentRound() { | ||
| List<String> roundResult = new ArrayList<>(); | ||
| public List<RacingCarDto> getStartStatus() { |
There was a problem hiding this comment.
반환값을 RacingCarDto 로 변경해 주신 부분 좋습니다.
OutputView에서도 해당 DTO를 통해 값 출력이 잘 되는것을 확인했습니다 😄
안녕하세요 카프카!
1단계와 2단계를 혼동하여 1단계를 제대로 진행하지 못한 것 같아 굉장히 아쉬움이 큽니다 🥲
1단계에서 말씀해주신 피드백들을 반영하여 리팩토링을 진행했습니다!
잘 부탁드립니다 🙇♀️
주요 수정 사항
궁금한 점
getter 사용은 최대한 지양해야한다라는 객체지향 생활 체조 원칙을 최대한 지키기 위해RacingCar클래스에서
등의 방법을 사용했습니다.
하지만 어떠한 글에서는
view에서 값을 출력하기 위해 getter를 사용하는 것은 괜찮다라는 내용이 있었습니다.카프카는 해당 문제에 대해 어떻게 생각하시는지 궁금합니다. 감사합니다!