-
Notifications
You must be signed in to change notification settings - Fork 455
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단계 - 자동차 경주 리팩터링] 페드로(류형욱) 미션 제출합니다. #757
Changes from all commits
3771b25
6f956b9
84e5cda
213cea5
bbba032
238304c
7a6cda9
5864569
f38b89a
f40aba2
7085a22
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,33 +5,35 @@ | |
import java.util.Map; | ||
import racingcar.model.Car; | ||
import racingcar.model.CarGroup; | ||
import racingcar.model.RacingGame; | ||
import racingcar.utils.NameParser; | ||
import racingcar.utils.InputValidator; | ||
import racingcar.view.InputView; | ||
import racingcar.view.OutputView; | ||
|
||
public class GameController { | ||
private final CarGroup carGroup = new CarGroup(); | ||
private List<String> names; | ||
private int moveCount; | ||
private RacingGame racingGame; | ||
|
||
public void init() throws IOException { | ||
readCarNames(); | ||
readMoveCount(); | ||
initCars(names); | ||
List<String> names; | ||
int moveCount; | ||
|
||
do { | ||
names = readCarNames(); | ||
} while (!isNameValid(names)); | ||
do { | ||
moveCount = readMoveCount(); | ||
} while (!isMoveCountValid(moveCount)); | ||
|
||
racingGame = new RacingGame(createCarGroup(names), moveCount); | ||
} | ||
|
||
private void readCarNames() throws IOException { | ||
boolean isValid = false; | ||
while (!isValid) { | ||
isValid = doReadCarNames(); | ||
} | ||
private List<String> readCarNames() throws IOException { | ||
return NameParser.parse(InputView.inputNames()); | ||
} | ||
|
||
private boolean doReadCarNames() throws IOException { | ||
private boolean isNameValid(List<String> names) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이름을 검증하는 방식을 Controller가 알고 있어야 할까요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 재 입력 루프를 만들기 위한 메서드였긴 한데, 검증 로직을 컨트롤러가 가지지 않고 validator를 호출만 하고 있는데 이러한 구조도 Controller가 검증하는 방식을 알고 있다고 보는 건가요..? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Controller 입장에서는 검증한다는 사실 조차 알 필요가 없지 않을까요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 확실히 컨트롤러의 역할을 생각해보면 부가적인 부분인 것 같아요. 별도의 validator를 두기보다는 아예 객체 생성 시 검증하는 형태가 더 나을 수도 있겠네요. 다음 미션 설계 시 고려해 보겠습니다! |
||
try { | ||
OutputView.printlnInputName(); | ||
names = NameParser.parse(InputView.inputNames()); | ||
InputValidator.validateCarName(names); | ||
} catch (IllegalArgumentException e) { | ||
OutputView.printException(e.getMessage()); | ||
|
@@ -40,17 +42,8 @@ private boolean doReadCarNames() throws IOException { | |
return true; | ||
} | ||
|
||
private void readMoveCount() throws IOException { | ||
boolean isValid = false; | ||
while (!isValid) { | ||
isValid = doReadMoveCount(); | ||
} | ||
} | ||
|
||
private boolean doReadMoveCount() throws IOException { | ||
private boolean isMoveCountValid(int moveCount) { | ||
try { | ||
OutputView.printlnInputMoveCount(); | ||
moveCount = InputView.inputMoveCount(); | ||
InputValidator.validateMoveCount(moveCount); | ||
} catch (IllegalArgumentException e) { | ||
OutputView.printException(e.getMessage()); | ||
|
@@ -59,22 +52,27 @@ private boolean doReadMoveCount() throws IOException { | |
return true; | ||
} | ||
|
||
private void initCars(List<String> carNames) { | ||
private int readMoveCount() throws IOException { | ||
return InputView.inputMoveCount(); | ||
} | ||
|
||
private CarGroup createCarGroup(List<String> carNames) { | ||
CarGroup carGroup = new CarGroup(); | ||
|
||
for (String name : carNames) { | ||
carGroup.add(new Car(name)); | ||
} | ||
return carGroup; | ||
} | ||
|
||
public void play() { | ||
OutputView.printResultDescription(); | ||
for (int i = 0; i < moveCount; i++) { | ||
Map<String, Integer> raceResponse = carGroup.race(); | ||
OutputView.printPosition(raceResponse); | ||
} | ||
List<Map<String, Integer>> raceResponse = racingGame.race(); | ||
OutputView.printPosition(raceResponse); | ||
Comment on lines
+70
to
+71
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more. 저도 지금은 DTO까지 고려할 구조는 아니라고 생각합니다. DTO 이야기는 나중에 레벨2 때 실컷 하실 것이기 때문에 그때까지 아껴놓기로해요 ㅎㅎ |
||
} | ||
|
||
public void finish() { | ||
List<String> winners = carGroup.findWinners(); | ||
List<String> winners = racingGame.findWinners(); | ||
if (winners.isEmpty()) { | ||
OutputView.printNoWinner(); | ||
return; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package racingcar.model; | ||
|
||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.stream.IntStream; | ||
|
||
public class RacingGame { | ||
CarGroup cars; | ||
int moveCount; | ||
|
||
public RacingGame(CarGroup cars, int moveCount) { | ||
this.cars = cars; | ||
this.moveCount = moveCount; | ||
} | ||
|
||
public List<Map<String, Integer>> race() { | ||
return IntStream.range(0, moveCount) | ||
.mapToObj(c -> cars.playRound()) | ||
.toList(); | ||
} | ||
Comment on lines
+16
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
동시에 여러 코드를 보다보니 RacingGame 제가 피드백 드리려고 했던 부분은 외부에서 각 라운드마다 처리하고 매번 출력을 하면 이중 Collection을 제거할 수 있기 때문에 그쪽으로 안내해드리려고 한 의도였습니다. |
||
|
||
public List<String> findWinners() { | ||
return cars.findWinners(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,12 @@ | |
import java.util.List; | ||
|
||
public class InputValidator { | ||
private static final int NAME_LENGTH_LIMIT = 5; | ||
private static final String NAME_REGEX_PATTERN = "^[a-zA-Z]*$"; | ||
|
||
private InputValidator() { | ||
} | ||
|
||
Comment on lines
5
to
+11
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 해당 클래스는 util성 클래스 인가요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. util 패키지에는 특정 도메인에 종속적이지 않으면서, 프로젝트 전반적으로 사용하는 클래스들을 담아두었던 것 같아요. 특정 클래스를 검증하는 validator가 있다면 util에 있으면 안 되겠지만, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
저는 util 클래스의 사용을 최대한 지양합니다. 혹여나 사용을 하더라도 어느정도 규모가 커졌을 때 반복적으로 구현해야하는 기능들을 생겼을 때 만드는 편이에요. 그마저도 예를 들어 참고자료: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. '특정' 도메인에 종속된다 가 아니라 '어떤(any)' 도메인에 종속되는지 여부가 초점이였군요. 생각해 보면 프리코스 때와 마찬가지로 큰 고민 없이 |
||
public static void validateCarName(List<String> names) { | ||
validateDuplicateNames(names); | ||
for (String name : names) { | ||
|
@@ -12,7 +18,7 @@ public static void validateCarName(List<String> names) { | |
} | ||
|
||
private static void validateNameLength(String name) { | ||
if (name.length() > 5) { | ||
if (name.length() > NAME_LENGTH_LIMIT) { | ||
throw new IllegalArgumentException("자동차의 이름은 5글자를 초과할 수 없습니다."); | ||
} | ||
} | ||
|
@@ -28,7 +34,7 @@ private static void validateDuplicateNames(List<String> names) { | |
} | ||
|
||
private static void validateNameCharacters(String name) { | ||
if (!name.matches("^[a-zA-Z]*$")) { | ||
if (!name.matches(NAME_REGEX_PATTERN)) { | ||
throw new IllegalArgumentException("자동차의 이름은 영어로만 이루어져야 합니다."); | ||
} | ||
} | ||
|
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.
값을 불러오는 기능이 별개의 메서드로 왜 빠지지 못했을까요?
현재는
값을 선언해주는 부분
과값을 불러오는 부분
이 같이 있어요 ->names = readCarNames();
선언 부분과 호출 부분을 분리하면 별개의 메서드로 분리할 수 있지 않을까요?
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.
어.. 무슨 말씀이신지 잘 이해하지 못했습니다. 반복 구조를 별개의 메서드(
foo()
)로 빼고,names = foo()
형태로 구현하라는 말씀이실까요?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.
제가 모호하게 설명을 드렸었네요.
GameContoller의
init()
메서드 안에 해당 구조에 대한 내용입니다.do-while 문이 두번이나 반복되기 때문에 가독성이 떨어져, 메서드를 분리하고자 하는 목적에서 피드백을 드렸습니다.
이 상태에서
do~while
구문을 메서드로 뽑아내려고 하면 어렵겠죠.그런데 만약
do~while
구문이 값을 선언하는 것이 아니라return
하는 형태가 된다면 분리가 가능하겠죠.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단계 초기 구현은 말씀하신 대로 구현했다가 단순히 반복해 주는 일만 하는 메서드인 것 같아서 다시
init()
안에서 구현했었는데, do while이 여러 번 반복되면 가독성이 떨어질 수 있겠네요.