-
Notifications
You must be signed in to change notification settings - Fork 452
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[자동차 경주 2단계] 카피 step2 미션 제출합니다 #789
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
안녕하세요 카피, 2단계 구현도 수고하셨습니다! 이미 1단계에서 MVC 구조로 시도했어서 큰 구조 변경이 존재하진 않네요! 👍🏿
고민해볼만한 부분들에 대해서 코멘트 남겼으니 확인 부탁드려요! 💪
public enum ErrorMessage { | ||
MESSAGE_NO_DUPLICATE_CAR_NAMES("중복된 자동차 이름은 사용할 수 없습니다."), | ||
MESSAGE_LENGTH_OF_CAR_NAME("자동차 이름은 1자 이상 5자 이하여야 합니다."), | ||
MESSAGE_NO_SPACE("중복된 자동차 이름은 사용할 수 없습니다."), | ||
MESSAGE_ONLY_NATURAL_NUMBER("시도할 회수는 자연수를 입력해 주세요."), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
에러 메시지를 enum으로 하셨네요! 어떤 장점이 있으셨나요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
가장 먼저 에러 메시지를 구조화 할 수 있다는 장점이 있는 것 같습니다. 상수의 타입을 정의하여 컴파일 시에 데이터의 값을 체크할 수 있습니다!
에러 메시지를 enum 클래스로 분리하면서 가독성이 좋아진 것 같습니다. 에러 메시지에 대한 관리를 할 때 하나의 클래스만 신경써주면 되기 때문에 유지보수도 좋을 것 같습니다!
MESSAGE_ONLY_NATURAL_NUMBER("시도할 회수는 자연수를 입력해 주세요."), | ||
MESSAGE_ONLY_NUMBER("시도할 회수는 숫자여야만 가능합니다."); | ||
|
||
private String message; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
재할당 해도 되는 변수일까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
에러 메시지는 실행될 때 변경이 필요가 없을 것 같습니다!
final을 추가하여 의도치 않은 변경을 막을 수 있을 것 같습니다😀
private Cars makeNewCars(List<String> carNames) { | ||
return new Cars(carNames.stream() | ||
.map(Car::new) | ||
.toList()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이름 list를 받아서 Cars
를 생성하는 로직이네요! Cars가 해당 책임을 가져가는건 어떨까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cars
를 생성하는 로직은 Cars가 책임을 가져가는 것이 좋을 것 같습니다.
새로 Cars를 생성하는 로직이기 때문에 static 메서드로 생성하여 클래스를 통해 직접 호출할 수 있게 하였습니다!
|
||
import static racingcar.message.ErrorMessage.*; | ||
|
||
public class InputValidator { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
각각의 검증이 필요한 도메인 클래스들이 검증 로직을 나눠 가져가는게 좀 더 응집성 있는 역할분리가 되지 않을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
input, output 같은 view에 대한 검증 클래스를 각각 생성하는 것보다 이는 공용 Validator 클래스를 생성하고, 도메인을 검증하기 위한 추가 검증 클래스를 생성하는 것이 역할 분리 측면에서 좋을 것 같습니다!
public List<Car> getCars() { | ||
return cars; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
만약 이 메소드의 응답을 받은 곳에서 cars에 add()나 remove() 메소드를 호출하면 어떻게 되나요? Cars의 상태가 변경되는걸 방지할 방법은 없을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
현재 방식으로는 외부에서 반환받은 cars 리스트에 원소를 추가, 삭제 할 수 있어 상태가 변경 될 가능성이 있습니다.
따라서 반환받은 리스트에 대한 수정을 막기 위해 copyOf
메서드를 추가했습니다!
private void moveCars(Cars cars) { | ||
for (Car car : cars.getCars()) { | ||
moveCar(car); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 동작도 Cars가 가져갈 수 있지 않을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cars
에 메서드를 포함하게 하는 것이 구조적으로 맞는 것 같습니다!
코드를 바꾸면서 moveCar 메서드를 Car에 같이 포함시켰습니다.
하지만 기존에 Car
의 move 메서드에 random을 포함시키지 않고 distance++ 자체만을 하게 하였는데, 랜덤하게 움직이는 moveRandomly
메서드를 Car
에 추가하는 것이 괜찮은 방법인지에 대한 의문이 들어 여쭤봅니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moveRandomly
메소드가 Car에 있으면 Car가 랜덤에 의해 움직인다는걸 알게 되는거 아닐까요? 차가 굳이 랜덤에 의해 움직인다는걸 알 필요가 없을 것 같아요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
핵심 도메인인 Car가 moveRandomly
를 아는 것 보다 차라리 일급 컬렉션인 Cars가 가지고 있는 것이 좋을 것 같아 수정하였습니다! 완벽하게 만족하는 건 아니지만 수정해봤습니다!
private void moveCar(Car car) { | ||
if (RandomGenerator.generateRandomNumber(MAX_RANDOM_NUMBER + 1) >= MINIMUM_MOVEMENT_CONDITION) { | ||
car.moveCar(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
해당 동작을 테스트하려면 어떻게 하면 좋을까요? 🤔
힌트를 드리자면 인터페이스를 활용해보면 좋을 것 같아요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MoveCondition
이라는 인터페이스를 만들어 자동차의 움직임 여부를 주입받도록 변경하였습니다.
Random 숫자를 테스트 하기 어렵기에 인터페이스로 분리해봤는데 정상적으로 테스트는 완료되지만 주먹구구식으로 구현하여서 구조상으로는 완벽하지 않은 것 같습니다😥
|
||
public class GameManager { | ||
|
||
private static final int MAX_RANDOM_NUMBER = 9; | ||
private static final int MINIMUM_MOVEMENT_CONDITION = 4; | ||
|
||
OutputView outputView = new OutputView(); | ||
InputView inputView = new InputView(); | ||
|
||
public void run() { | ||
outputView.printCarNameInputMessage(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
입력을 받기 위한 메소드인 것 같은데 InputView가 가져가는게 어떨까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
InputView로 해당 메서드를 분리하여 같이 사용하게 하였습니다!
public enum ErrorMessage { | ||
MESSAGE_NO_DUPLICATE_CAR_NAMES("중복된 자동차 이름은 사용할 수 없습니다."), | ||
MESSAGE_LENGTH_OF_CAR_NAME("자동차 이름은 1자 이상 5자 이하여야 합니다."), | ||
MESSAGE_NO_SPACE("중복된 자동차 이름은 사용할 수 없습니다."), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
에러 메시지가 이게 맞을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
혹시 어떤 부분을 고치면 좋을지 조금 더 알려주실 수 있나요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Enum의 이름은 MESSAGE_NO_SPACE
인데 에러 메시지는 이름이 중복되었다는 내용인 것 같아서요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
헉 이런 실수를.. 지적해주셔서 감사합니다!
@@ -1,19 +1,17 @@ | |||
package racingcar.view; | |||
|
|||
import racingcar.message.ErrorMessage; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
사용하지 않는 import문은 제거해주세요! 보이스카우트 규칙!
public List<Car> getCars() { | ||
return List.copyOf(cars); | ||
} | ||
|
||
public int calculateLongestDistance() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
일반적인 관례상 getter의 경우 다른 public 메소드들보다 아래에 선언하곤 합니다!
메소드 나열하는 순서에 대해서도 고민해보면 좋을 것 같아요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
그런 관례가 있었군요! 앞으로 getter는 마지막에 위치시켜야겠습니다😀
public static Cars makeNewCars(List<String> carNames) { | ||
return new Cars(carNames.stream() | ||
.map(Car::new) | ||
.toList()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
정적 팩토리 메소드의 위치도 어디에 있으면 좋을지 고민해봐요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
정적 팩토리 메서드는 사용자가 어떻게 사용하는지 빠르게 알려주면 좋기 때문에 메서드 정의 부분의 맨 윗부분에 위치하는게 좋을 것 같습니다!
|
||
import java.util.List; | ||
|
||
import static racingcar.domain.Cars.makeNewCars; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
static 메소드를 import 해주셨네요! 정적 팩토리 메소드의 경우 어떤 클래스의 팩토리 메소드인지를 호출 부분에서 파악하는게 가독성이 좋다고 생각하는데 카피의 생각은 어떠신가요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
정적 팩토리 메서드는 코드를 봤을 때 한번에 어떤 클래스에 속해있는지 바로 파악할 수 있도록 import 하는게 좋을 것 같습니다! 무의식적으로 import를 한 것 같습니다😅
public class Cars { | ||
|
||
private static final String MESSAGE_NOT_EXIST_CAR = "생성된 자동차가 없습니다."; | ||
private List<Car> cars; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 인스턴스 변수는 재할당해도 괜찮도록 설계된 클래스인가요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
경주할 자동차는 처음 입력에서 정해지기 때문에 final을 붙여 불변으로 하는 것이 좋을 것 같습니다!
private void moveCars(Cars cars) { | ||
for (Car car : cars.getCars()) { | ||
moveCar(car); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moveRandomly
메소드가 Car에 있으면 Car가 랜덤에 의해 움직인다는걸 알게 되는거 아닐까요? 차가 굳이 랜덤에 의해 움직인다는걸 알 필요가 없을 것 같아요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
리뷰 반영 수고하셨습니다 카피! 고민해볼만한 부분들 몇 가지 남겨두었으니 확인 부탁드려요!😁
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
첫 미션 수고하셨습니다 카피! 이번 미션에서 고민할만한 것들은 충분히 하셨다고 보여서 이만 머지하도록 할게요! 앞으로의 우테코 생활도 화이팅입니다!💪
지난 피드백을 반영하면서 한 클래스가 여러 역할을 하는 부분을 분리하려고 노력했습니다!
키워드로 주신 일급 컬렉션을 공부하여 적용해보려고도 하였는데, 아직 미숙하여 부족한 부분이 있는지 의견을 묻고 싶습니다!