-
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단계 - 자동차 경주 리팩토링] 연로그(권시연) 미션 제출합니다. #419
Changes from all commits
aba2e34
ea04b53
51da0e9
36efe36
0fb6314
b21e34d
1c977cb
97cb8c8
44f25fc
154e848
eea0010
57efde6
f898133
9090de1
d192408
de49f39
2528710
fb37928
70e667e
6dbe286
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 |
---|---|---|
@@ -1,10 +1,13 @@ | ||
package racingcar; | ||
|
||
import racingcar.controller.RacingController; | ||
import racingcar.domain.strategy.MoveStrategy; | ||
import racingcar.domain.strategy.RandomMoveStrategy; | ||
|
||
public class Application { | ||
public static void main(String[] args) { | ||
RacingController racingController = new RacingController(); | ||
racingController.start(); | ||
MoveStrategy moveStrategy = new RandomMoveStrategy(); | ||
racingController.start(moveStrategy); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,45 +1,47 @@ | ||
package racingcar.controller; | ||
|
||
import racingcar.model.Cars; | ||
import racingcar.model.TryCount; | ||
import racingcar.domain.Cars; | ||
import racingcar.domain.TryCount; | ||
import racingcar.domain.strategy.MoveStrategy; | ||
import racingcar.view.InputView; | ||
import racingcar.view.OutputView; | ||
|
||
public class RacingController { | ||
private Cars cars; | ||
private TryCount tryCount; | ||
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. RacingController에서 멤버 변수를 지역 변수로 변경 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. 만약에 Controller를 여러 곳에서 접근하는 경우 cars와 tryCount가 원하지 않는 상태를 갖거나 수정될 수 있다고 판단하였습니다! 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. 좋은 관점인 것 같아요🙂 다만 현재는 한 사용자가 요청할 때마다, RacingController를 생성해서 사용하는 구조인데 해당 값을 공유하는 케이스가 있을까요? 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. 현재는 없습니다! 미리 습관 들여두면 좋을 것 같아서 시도해보았습니다 ㅎㅎ |
||
public void start(MoveStrategy moveStrategy) { | ||
Cars cars = inputCars(); | ||
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. 프로그램의 흐름도가 너무 깔끔하네요🙂 |
||
TryCount tryCount = inputTryCount(); | ||
race(cars, tryCount, moveStrategy); | ||
printRaceResult(cars); | ||
} | ||
|
||
public void start() { | ||
private Cars inputCars() { | ||
try { | ||
cars = new Cars(InputView.inputCarNames()); | ||
inputTryCount(); | ||
race(); | ||
terminate(); | ||
return new Cars(InputView.inputCarNames()); | ||
} catch (IllegalArgumentException e) { | ||
OutputView.printException(e); | ||
start(); | ||
return inputCars(); | ||
} | ||
} | ||
|
||
private void inputTryCount() { | ||
private TryCount inputTryCount() { | ||
try { | ||
tryCount = new TryCount(InputView.inputTryCount()); | ||
return new TryCount(InputView.inputTryCount()); | ||
} catch (IllegalArgumentException e) { | ||
OutputView.printException(e); | ||
inputTryCount(); | ||
return inputTryCount(); | ||
} | ||
} | ||
|
||
private void race() { | ||
private void race(Cars cars, TryCount tryCount, MoveStrategy moveStrategy) { | ||
int nowTryCnt = 0; | ||
OutputView.printStartMessage(); | ||
while (tryCount.isNotSame(nowTryCnt++)) { | ||
cars.moveAll(); | ||
cars.moveAll(moveStrategy); | ||
OutputView.printCarsStatus(cars.getCars()); | ||
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를 생성한다에 대해서도 생각해보았는데요 |
||
} | ||
} | ||
|
||
private void terminate() { | ||
private void printRaceResult(Cars cars) { | ||
OutputView.printCarsStatus(cars.getCars()); | ||
OutputView.printWinners(cars.getWinners()); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,18 @@ | ||
package racingcar.model; | ||
package racingcar.domain; | ||
|
||
import static racingcar.util.StringUtils.splitByDelimiter; | ||
import static racingcar.util.StringUtils.stripStringArray; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
import racingcar.domain.strategy.MoveStrategy; | ||
import racingcar.message.ErrorMessages; | ||
import racingcar.util.RandomGenerator; | ||
import racingcar.util.RandomUtils; | ||
|
||
public class Cars { | ||
private static final String DELIMITER = ","; | ||
private static final int MIN = 0; | ||
private static final int MAX = 9; | ||
|
||
|
||
private final List<Car> cars = new ArrayList<>(); | ||
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. 미션의 범위에는 벗어나지만 조금 더 공부를 원하시면 불변객체와 방어적 복사라는 키워드로 조금 더 공부해보셔도 좋을 것 같습니다🙂 |
||
|
||
|
@@ -24,9 +24,9 @@ public Cars(String carNames) { | |
} | ||
} | ||
|
||
public void moveAll() { | ||
public void moveAll(MoveStrategy strategy) { | ||
for (Car car : cars) { | ||
car.goOrStop(RandomGenerator.generateNumber(MIN, MAX + 1)); | ||
car.go(strategy); | ||
} | ||
} | ||
|
||
|
@@ -45,4 +45,11 @@ private void validateDuplicatedName(String[] carNames) { | |
throw new IllegalArgumentException(ErrorMessages.DUPLICATED_NAME); | ||
} | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
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. toString()에 대해서 공부를 했는데 디버깅할 때 편리하고 (아직 저희 과정에서는 불필요하지만) 로그를 찍을때 자주 사용되는 것 같았습니다! |
||
return "Cars{" + | ||
"cars=" + cars + | ||
'}'; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,41 @@ | ||
package racingcar.model; | ||
package racingcar.domain; | ||
|
||
import static racingcar.message.ErrorMessages.*; | ||
import static racingcar.message.ErrorMessages.TRY_CNT_NOT_NUMBER; | ||
import static racingcar.message.ErrorMessages.TRY_CNT_NOT_VALID_NUMBER; | ||
|
||
import java.util.Objects; | ||
|
||
public class TryCount { | ||
private static final int TRY_MAX_CNT = 1000; | ||
private final int tryCount; | ||
|
||
public TryCount(String countString) { | ||
validateStringIsNumber(countString); | ||
isValidNumber(countString); | ||
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. 2147483648와 관련해서는 아예 최대 입력 가능 숫자에 대해 제한을 두었습니다! |
||
this.tryCount = convertStringToInt(countString); | ||
validatePositive(tryCount); | ||
} | ||
|
||
public boolean isNotSame(int tryCount) { | ||
return this.tryCount != tryCount; | ||
} | ||
|
||
private void validateStringIsNumber(String input) { | ||
if (!input.matches("[0-9]")) { | ||
throw new IllegalArgumentException(TRY_CNT_NOT_NUMBER); | ||
} | ||
} | ||
|
||
private void isValidNumber(String input) { | ||
long count = Long.parseLong(input); | ||
if (count < 0 || count > TRY_MAX_CNT) { | ||
throw new IllegalArgumentException(TRY_CNT_NOT_VALID_NUMBER); | ||
} | ||
} | ||
|
||
private int convertStringToInt(String string) { | ||
return Integer.parseInt(string); | ||
} | ||
|
||
@Override | ||
public boolean equals(Object obj) { | ||
if (this == obj) { | ||
|
@@ -33,17 +53,10 @@ public int hashCode() { | |
return Objects.hash(tryCount); | ||
} | ||
|
||
private int convertStringToInt(String string) { | ||
try { | ||
return Integer.parseInt(string); | ||
} catch (NumberFormatException e) { | ||
throw new IllegalArgumentException(TRY_CNT_NOT_NUMBER); | ||
} | ||
} | ||
|
||
private void validatePositive(int count) { | ||
if (count < 0) { | ||
throw new IllegalArgumentException(TRY_CNT_NOT_NATURAL_NUMBER); | ||
} | ||
@Override | ||
public String toString() { | ||
return "TryCount{" + | ||
"tryCount=" + tryCount + | ||
'}'; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
package racingcar.model; | ||
package racingcar.domain; | ||
|
||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
@@ -7,20 +7,14 @@ public class Winners { | |
private static final int MIN_POSITION = 0; | ||
|
||
private final List<Car> winners; | ||
private final int maxPosition; | ||
|
||
public Winners(List<Car> cars) { | ||
maxPosition = getMaxPosition(cars); | ||
int maxPosition = getMaxPosition(cars); | ||
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.
분명 데이터가 많고 계산연산이 복잡하다면 그 값을 저장해두는 것이 성능상의 이점을 가져갈 수 있을거에요. 다만 현재시점에서는 그 부분보다는 객체지향적으로 옳은 코드가 무엇인가에 대해서 더 고민하고 그 방향으로 개발을 이어가면 좋을 것 같고, 성능상의 이슈가 발생할 여지가 보일 때 혹은 미묘하게 발생할 때 리팩토링으로 변경하는 것도 좋을 것 같습니다. |
||
winners = cars.stream() | ||
.filter(car -> car.getPosition() == maxPosition) | ||
.collect(Collectors.toList()); | ||
} | ||
|
||
|
||
public int getMaxPosition() { | ||
return maxPosition; | ||
} | ||
|
||
public List<String> getNames() { | ||
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라는 개념을 도입해서 리팩토링해봐도 좋을 것 같아요. |
||
return winners.stream() | ||
.map(Car::getName) | ||
|
@@ -34,4 +28,10 @@ private int getMaxPosition(List<Car> cars) { | |
.orElse(MIN_POSITION); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "Winners{" + | ||
"winners=" + winners + | ||
'}'; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package racingcar.domain.strategy; | ||
|
||
public class FixMoveStrategy implements MoveStrategy { | ||
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. 요 질문에 대한 제 생각은 요기 적으면 좋을 것 같아요.
|
||
private static final int MOVING_DISTANCE = 1; | ||
private static final int STANDARD_VALUE = 4; | ||
|
||
private final int distance; | ||
|
||
public FixMoveStrategy(int distance) { | ||
this.distance = distance; | ||
} | ||
|
||
@Override | ||
public int move() { | ||
if (distance >= STANDARD_VALUE) { | ||
return MOVING_DISTANCE; | ||
} | ||
return 0; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package racingcar.domain.strategy; | ||
|
||
public interface MoveStrategy { | ||
int move(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package racingcar.domain.strategy; | ||
|
||
import racingcar.util.RandomUtils; | ||
|
||
public class RandomMoveStrategy implements MoveStrategy { | ||
private static final int MOVE_MIN = 0; | ||
private static final int MOVE_MAX = 9; | ||
private static final int STANDARD_VALUE = 4; | ||
private static final int MOVING_DISTANCE = 1; | ||
|
||
@Override | ||
public int move() { | ||
int random = RandomUtils.generateNumber(MOVE_MIN, MOVE_MAX + 1); | ||
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. 전략패턴을 잘 사용해주셨네요👏 조금 더 나아가서 현재 해당 전략들은 관점에 따라 아래와 같은 2가지 일을 하고 있는데요. 이 두가지 모두가 해당 전략의 역할로 간주했을 때와, 움직이는 범위는 자동차가 결정하는 것 중 현재 방법을 택한 이유가 있을까요?
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. Car에서 move할 거리를 받고 바로 움직이고 싶어서 Strategy에 책임을 위임했습니다! |
||
if (random >= STANDARD_VALUE) { | ||
return MOVING_DISTANCE; | ||
} | ||
return 0; | ||
} | ||
} |
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.
코드는 요구사항이 변경됨에 따라 항상 최신화되지만 문서는 그렇지 않은 경향이 많아요. (안 바꿔도 티가 안 나니까..) 그렇지만 연로그는 같이 최신화해주셨네요👏