-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
Final practice #2387
base: main
Are you sure you want to change the base?
Final practice #2387
Changes from all commits
49162f5
0461bf3
4bc3767
9332a03
49777f2
dfa13b8
d371887
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 |
---|---|---|
@@ -0,0 +1,75 @@ | ||
# 자동차 경주 게임 | ||
|
||
## 어플리케이션 흐름 | ||
|
||
1. 자동차 이름 입력 | ||
1. 자동차 이름은 ,로 구분 | ||
- 입력이 `,`로 끝나는 경우 예외 발생 | ||
2. 각 이름은 5자 이하만 가능 | ||
3. 자동차 이름은 중복될 수 없다. | ||
2. 이동 시도 횟수 입력 | ||
1. 숫자 입력시 예외 발생 | ||
2. 0, 또는 음수 입력시 예외 발생 | ||
3. 각 라운드 별 이동 시도 후 결과 출력 | ||
4. 최종 우승자 출력 | ||
|
||
## 어플리케이션 구성 | ||
|
||
### domain | ||
|
||
#### \[Car] | ||
|
||
#### Car | ||
|
||
이름과 위치 정보를 가진다. | ||
|
||
- 기능 | ||
- 숫자를 입력받아 이동 가능하다면 위치 정보를 업데이트 후 이동 여부를 반환한다. | ||
|
||
#### Cars | ||
|
||
자동차 객체들을 관리한다. | ||
|
||
- 기능 | ||
- 숫자를 생성해 각 자동차 객체를 이동시킨다. | ||
- 자동차들의 진행 결과를 받아온다. | ||
- 우승자의 이름을 반환한다. | ||
|
||
#### \[numberGenerator] | ||
|
||
#### MoveNumber | ||
|
||
이동에 사용할 숫자를 래핑한다. | ||
|
||
#### NumberGenerator | ||
|
||
- 기능 | ||
- 숫자 하나를 반환한다. | ||
|
||
#### RandomNumberGenerator | ||
|
||
- 기능 | ||
- 0~9 사이에서 랜덤으로 숫자를 생성해 반환한다. | ||
|
||
### view | ||
|
||
#### InputView | ||
|
||
- 기능 | ||
- 자동차 이름 입력 | ||
- `,`를 기준으로 나눠서 List<String> 반환 | ||
- 시도할 횟수 입력 | ||
- 시도 횟수가 0 이하라면 예외 발생 | ||
|
||
#### OutputView | ||
|
||
- 기능 | ||
- 라운드 결과 출력 | ||
- 최종 우승자 출력 | ||
|
||
## 고민 | ||
|
||
자동차의 위치 정보는 어느 곳에서 저장하는 것이 가장 자연스러울까? | ||
-> 출력에서도 Car 객체를 그대로 넘기자. 그렇기에 자동차의 위치 정보 또한 자동차에서 관리하자 | ||
우승자 비교는 어떤 식으로 구현하는 것이 가장 자연스러울까? | ||
-> 자동차들의 경주 결과 비교는 자동차의 책임이 아닌, 게임 스태프의 책임으로 생각했음 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# 최종 코테를 대비하기 위해 어떤 준비가 가능할까 | ||
|
||
- 필요없는 고민을 줄이기 | ||
- 어떤 메서드에만 `static`을 사용할 지 기준 미리 정하기 | ||
- DTO를 사용할지, getter를 사용할지 미리 정하기 | ||
- 나는 getter를 사용하기로 결정 | ||
- 검증 로직을 Validator로 분리할지, 도메인 내부에서 모두 검증할지 정하기 | ||
- 전체 어플리케이션 흐름 관리를 어디서 할지 정하기 | ||
- Application.main() 활용할 것 같음 | ||
- 상수 추출은 어떻게 할지 기준 세우기 | ||
- `enum`은 언제 사용할지 | ||
- 각 클래스 내부 VS 상수 클래스 | ||
- 상수 클래스 VS 인터페이스 | ||
- 의존성 주입 -> 별도의 Config 클래스 만들지 말기 (그냥 파라미터로 주입정도만) | ||
- 각 디자인 패턴을 사용할 기준 정하기 | ||
- 빌더 패턴은 시간이 오래 걸리니 사용 X | ||
- 전략 패턴 | ||
- 예외 처리 | ||
- 정적 팩토리 메서드 | ||
- 생성자의 파라미터로 필드값 자체를 전달해 주는 것이 아니라 로직이 필요한 경우 | ||
- 출력 메시지 상수로 관리할 지 정하기 -> 상수로 X | ||
- Javadoc -> X | ||
- 인터페이스 VS 추상클래스 | ||
- 상속 VS 조합 | ||
- Optional VS enum의 "유효하지 않음" 값 | ||
- 리팩토링 우선순위 정하기 | ||
- README.md 작성 연습 | ||
- 정규 표현식 연습하기 | ||
|
||
- 리팩토링 리스트 | ||
- 단위 테스트 | ||
- 상수 추출 | ||
- 메서드 추출 | ||
- 원시값 래핑 | ||
- 추상화 제공 | ||
- 네이밍 | ||
- 네이밍에 변수 타입 넣지 말기 | ||
- 통합 테스트 | ||
|
||
## 기억할 것 | ||
|
||
- 꾸준히 기능 단위로 커밋하기 | ||
- 변수는 선언시 무조건 `private final`, 나중에 필요하면 변경 | ||
- 조건 연산자를 사용한 코드는 조건 연산자 앞에서 줄바꿈 | ||
- Console 닫아주기 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,23 @@ | ||
package racingcar; | ||
|
||
import racingcar.domain.InitController; | ||
import racingcar.domain.PlayController; | ||
import racingcar.domain.car.Cars; | ||
|
||
public class Application { | ||
private static final InitController initController = new InitController(); | ||
private static final PlayController playController = new PlayController(); | ||
|
||
public static void main(String[] args) { | ||
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. 이 부분에 대해 정~~말 많은 고민이 아직까지도 진행중인데요. 자동차 게임의 경우 그래도 어플리케이션이 비교적 단순하지만, 크리스마스와 같이 책임이 여러 가지로 구성되어 있는 상황에서 전체 흐름을 하나의 많은 분들의 코드를 보니 하나의 흐름 제어를 위한 @zxcev 님은 하나의 컨트롤러가 전체의 흐름을 제어하고, 각각의 책임은 세부 책임을 가지는 도메인 객체의 메서드를 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. 네. 간단한 프로그램의 경우 굳이 크리스마스같이 도메인 및 처리할 로직이 다수인 경우는 우아한 테크코스에서 제출하는 미션이 모두 조금씩 벗어나는 예외 시 재입력, 상태 선택, 처리 도중 출력 등의 패턴이 종종 보이기도 하는데 대부분의 경우, 이렇게 구조를 정해놓는 경우 하향식으로 코드를 작성할 때, 틀 잡기가 좀 더 수월했으며 전체 흐름은 요청 및 응답이 1:1로 대응되는 웹 MVC와 흐름이 완전히 같지는 않고, CLI 환경에서 작업을 해본 적이 없어서 더 좋은 방법이 분명히 있겠지만, 단기간에 하기엔 익숙한 형태에 접목시키는 것이 더 수월할 것이라고 생각했습니다. 미션에 직접 사용했던 크리스마스 및 로또 // 크리스마스 미션
public final class OrderController {
// ...
public void run() {
final OrderDiscounting orderDiscounting = inputView.inputOrder();
final OrderDto orderDto = orderService.order(orderDiscounting);
outputView.printOrderDto(orderDto);
}
} // 로또 미션
/**
* 입력, 실행, 출력 3단계의 흐름 제어 역할을 맡는 Controller 계층입니다.
* <p>
* 실제 로직이 작성된다기보다는, 전체적인 흐름 및 기능 목록을 한 눈에 볼 수 있도록,
* <p>
* 하위 계층에게 대부분의 처리를 위임하여 적은 코드량을 유지하여 가독성을 높이는 것이 좋다고 생각했습니다.
*/
public final class LottoController {
private final InputView inputView;
private final OutputView outputView;
private final LottoService lottoService;
public LottoController(
final InputView inputView,
final OutputView outputView,
final LottoService lottoService
) {
this.inputView = inputView;
this.outputView = outputView;
this.lottoService = lottoService;
}
/**
* 로또 프로그램은 총 2회의 입출력으로 이루어져 있음. 첫 번째는 금액 입력 및 로또 구매 후, 구매한 로또 번호 출력 두 번째는 당첨 번호 및 보너스 번호 입력 및 당첨 결과 확인, 그리고 당첨 통계
* 출력
* <p>
* 이를 2개의 메소드로 나누어 흐름을 보기 좋게 정리
*/
public void run() {
buyLottos();
drawLottos();
}
/**
* 첫 번째 입출력을 처리
* <p>
* 1. 로또 구매 금액 입력을 받는다.
* <p>
* 2. 로직 처리를 서비스에 위임한다
* <p>
* 3. 로또 구매 결과를 출력한다.
*/
private void buyLottos() {
final BuyLottosInput inputDto = inputView.inputBuyLottos();
final BuyLottosOutput outputDto = lottoService.buyLottos(inputDto);
outputView.printLottosBuyingResult(outputDto);
}
/**
* 두 번째 입출력을 처리
* <p>
* 1. 로또 당첨 번호 및 보너스 번호를 입력 받는다.
* <p>
* -> 웹 페이지로 생각하면, 로그인 시에 이메일을 입력하고 다음 버튼을 누르면 페이지 이동이 아닌, 클라이언트 사이드 렌더링이 이루어 진 뒤, url 이동 없이 비밀번호 입력 창으로 살짝 전환되는
* 느낌으로 하나의 페이지에서 2개의 입력을 모아서 받는다고 생각하여 1개의 컨트롤러에서 처리. 2. 로직 처리를 서비스에 위임한다 3. 로또 구매 결과를 출력한다.
* <p>
* 2. 저장된 모든 로또에 대한 추첨 로직 처리를 서비스에 위임한다.
* <p>
* 3. 로또 당첨 통계 결과를 출력한다.
*/
private void drawLottos() {
final DrawLottosInput inputDto = inputView.inputDrawLottos();
final DrawLottosOutput outputDto = lottoService.drawLottos(inputDto);
outputView.printLottosDrawingResult(outputDto);
}
} 정답이 아닌 하나의 방법일 뿐이며, 한번 시도해보시면 인사이트를 얻을 수도 있겠다 싶어서 말씀드렸습니다.😊 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. @zangsu 저도 간단하게 의견을 공유하고 싶어요. 프로젝트가 고도화되고 규모가 확장되면 Controller 분리를 고민해야 하는 시점이 찾아온다고 생각해요. 그런데 이번 6기 프리코스에서 출제된 미션은 모두 하나의 목적을 구현하는 방식이라고 이해했습니다.
따라서 하나의 Controller에서 구현했고 책임이 과도해질 때 Service로 분리하는 방식으로 구현하고 있어요. 물론 최종에서 여러 가지 목적을 구현해야 하는 미션이 출제될 수도 있지만 고민하고 계신 부분에 도움이 되고 싶어 말씀드려 봅니다. 😄 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. @Hugh-KR 덕분에 앞으로는 하나의 Controller에서 각각의 메서드로 동작만 구분시켜 구현을 하게 될 것 같습니다! |
||
// TODO: 프로그램 구현 | ||
Cars cars = initController.initCars(); | ||
int rounds = initController.getRounds(); | ||
initController.closeConsole(); | ||
|
||
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. 충분히 분리하려면 분리하셨을 것 같은데, 시간 관리를 염두하여 main 문에서 처리하도록 하신걸까요 ?! 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의 역할과도 부합하고 2개 생성되고 있는 컨트롤러의 생성 비용을 1개로 줄일 수 있으니까요. Application 클래스에서 직관적으로 동작을 보여주고 싶다면 통합된 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. 아직 " 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.
어떤 의도로 구현하셨는지, 의도를 알게 되었네요!! 저는 크리스마스 프로모션 미션을 진행하며, |
||
playController.printResultTitle(); | ||
for (int round = 0; round < rounds; round++) { | ||
playController.move(cars); | ||
playController.printRoundResult(cars); | ||
} | ||
playController.printWinner(cars); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package racingcar; | ||
|
||
public interface RacingConst { | ||
int RACING_NUMBER_START = 0; | ||
int RACING_NUMBER_END = 9; | ||
int RACING_NUMBER_MOVE = 4; | ||
|
||
String DELIMITER = ","; | ||
String WINNER_DELIMITER = ", "; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package racingcar.domain; | ||
|
||
import java.util.List; | ||
import racingcar.domain.car.Cars; | ||
import racingcar.domain.numbergenerator.RandomNumberGenerator; | ||
import racingcar.view.InputView; | ||
|
||
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. InitController를 구현하신게 상당히 신박한 방법같습니다! 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. 4주간의 미션을 진행하며 시간 관리를 생각한다면 하나의 |
||
public class InitController { | ||
private final InputView inputView = new InputView(); | ||
|
||
public Cars initCars() { | ||
List<String> names = inputView.getNames(); | ||
return new Cars(names, new RandomNumberGenerator()); | ||
} | ||
|
||
public int getRounds() { | ||
return inputView.getRoundNumber(); | ||
} | ||
|
||
public void closeConsole() { | ||
inputView.closeConsole(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package racingcar.domain; | ||
|
||
import java.util.List; | ||
import racingcar.domain.car.Car; | ||
import racingcar.domain.car.Cars; | ||
import racingcar.view.OutputView; | ||
|
||
public class PlayController { | ||
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 final OutputView outputView = new OutputView(); | ||
|
||
public void move(Cars cars) { | ||
cars.move(); | ||
} | ||
|
||
public void printResultTitle() { | ||
outputView.printResultTitle(); | ||
} | ||
|
||
public void printRoundResult(Cars cars) { | ||
List<Car> roundResult = cars.getCars(); | ||
outputView.printRountResult(roundResult); | ||
} | ||
|
||
public void printWinner(Cars cars) { | ||
List<String> winnerNames = cars.getWinner(); | ||
outputView.printWinners(winnerNames); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package racingcar.domain.car; | ||
|
||
import racingcar.domain.numbergenerator.MoveNumber; | ||
|
||
public class Car { | ||
private static final int INIT_POSITION = 0; | ||
private static final int DISTANCE_INCREASE = 1; | ||
private final CarName name; | ||
private int position; | ||
|
||
public Car(String name) { | ||
this.name = new CarName(name); | ||
this.position = INIT_POSITION; | ||
} | ||
|
||
public boolean move(MoveNumber number) { | ||
if (number.canMove()) { | ||
this.position += DISTANCE_INCREASE; | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
public boolean at(int position) { | ||
return this.position == position; | ||
} | ||
|
||
public String getName() { | ||
return name.getName(); | ||
} | ||
|
||
public int getPosition() { | ||
return position; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package racingcar.domain.car; | ||
|
||
import racingcar.exception.RacingException; | ||
|
||
public class CarName { | ||
public static final int NAME_MAX_LEN = 5; | ||
private final String name; | ||
|
||
public CarName(String name) { | ||
validateName(name); | ||
this.name = name; | ||
} | ||
|
||
private void validateName(String name) { | ||
if (name.isBlank() || name.length() > NAME_MAX_LEN) { | ||
throw RacingException.INVALID_CARS_NAME.makeException(); | ||
} | ||
} | ||
|
||
public String getName() { | ||
return name; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package racingcar.domain.car; | ||
|
||
import java.util.List; | ||
import racingcar.domain.numbergenerator.NumberGenerator; | ||
import racingcar.exception.RacingException; | ||
|
||
public class Cars { | ||
private final List<Car> cars; | ||
private final NumberGenerator numberGenerator; | ||
|
||
public Cars(List<String> carNames, NumberGenerator numberGenerator) { | ||
validateCars(carNames); | ||
cars = carNames.stream() | ||
.map(Car::new) | ||
.toList(); | ||
this.numberGenerator = numberGenerator; | ||
} | ||
Comment on lines
+11
to
+17
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. 해당 로직을 조금 더 효율적으로 만드는 방법을 고민해 봤어요. Cars 객체는 생성과 동시에 validateCars(carNames);를 통해 비어있는 경우, 중복되는 경우를 검증하고 있습니다. 그렇다면 아래의 Cars 객체가 생성과 동시에 리스트에 Car 객체를 생성 및 할당하고 Car 객체 에서도 빈값을 검증하고 있기에, 입력값이 ""인 경우와 pobi,"",Jon 인 경우를 한 번에 처리할 수 있습니다. 이로써 얻어지는 이점은 다음과 같습니다.
이에 대해 의견을 나누고 싶습니다. 혁수님은 어떻게 생각하시나요? 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 void validateCars(List<String> carNames) { | ||
validateNotEmpty(carNames); | ||
validateNotDuplicated(carNames); | ||
} | ||
|
||
private void validateNotDuplicated(List<String> carNames) { | ||
int distinctSize = (int) carNames.stream() | ||
.distinct() | ||
.count(); | ||
if (distinctSize != carNames.size()) { | ||
throw RacingException.DUPLICATE_CARS_NAME.makeException(); | ||
} | ||
} | ||
|
||
private void validateNotEmpty(List<String> carNames) { | ||
if (carNames.isEmpty()) { | ||
throw RacingException.NO_CAR_FOUNDED.makeException(); | ||
} | ||
} | ||
|
||
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. 설계에 대해 의견을 나누고 싶어 코멘트 드립니다!! 레이싱 차들을 움직이는 협력 에 대한 설계가 아래와 같은 구조인 것으로 보입니다. Refactor Ver1
더 나은 설계인지는 저도 물음표네요 ㅎㅎ,, 그저 설계에 대한 다양한 의견들을 나누고 싶어 코멘트 드립니다 😊 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. 정말 "좋은 설계" 에 대한 고민은 정답도 없을 뿐더러, 각각의 설계에 대한 장단점이 존재하기에 너무 어려운 것 같아요...ㅎㅎ 제가 위와 같이 설계를 하게 된 이유는 "자동차가 움직인다" 라는 기능을 테스트 할 때 외부에서 값을 받아 오는 편이 더 테스트 하기 쉬울 것이라 생각했기 때문이에요. 다만, @june-777 님의 의견처럼, 제 설계가 "이동의 책임을 가지고 있는 자동차가 자기 책임을 온전히 다하지 않는다"고 생각이 되기도 하네요. 그리고 이 방법 역시 책임이 집중되는 것이긴 하나, 해당 어플리케이션에서의 중심이 되는 클래스이기에 어느 정도는 어쩔 수 없는 부분이지 않나 라는 생각도 들고요! ㅎㅎ 결론만 정리하자면, @june-777 님이 의견 주신 설계 정말 괜찮은 것 같습니다!! 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 move() { | ||
cars.forEach(car -> car.move(numberGenerator.generate())); | ||
} | ||
|
||
public List<Car> getCars() { | ||
return cars; | ||
} | ||
|
||
public List<String> getWinner() { | ||
int winnersPosition = getWinnersPosition(); | ||
return cars.stream() | ||
.filter(car -> car.at(winnersPosition)) | ||
.map(Car::getName) | ||
.toList(); | ||
} | ||
|
||
private int getWinnersPosition() { | ||
return cars.stream() | ||
.mapToInt(Car::getPosition) | ||
.max() | ||
.getAsInt(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package racingcar.domain.numbergenerator; | ||
|
||
import racingcar.RacingConst; | ||
import racingcar.exception.RacingException; | ||
|
||
public class MoveNumber { | ||
private final int number; | ||
|
||
public MoveNumber(int number) { | ||
validateNumber(number); | ||
this.number = number; | ||
} | ||
|
||
private void validateNumber(int number) { | ||
if (number < RacingConst.RACING_NUMBER_START || number > RacingConst.RACING_NUMBER_END) { | ||
throw RacingException.INVALID_NUMBERS.makeException(); | ||
} | ||
} | ||
|
||
public boolean canMove() { | ||
return this.number >= RacingConst.RACING_NUMBER_MOVE; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package racingcar.domain.numbergenerator; | ||
|
||
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. default method를 구현하신 이유가 궁금합니다! 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. 처음 제가 구현했던 방법은 이동에 사용되는 "숫자 자체"를 도메인에서 사용하는 방법이었습니다! 리팩토링을 하며 이동에 사용되는 숫자를 클래스로 래핑하게 되었는데요. 그러나 이 상황 역시 "숫자 생성"에 대해서만 추상화를 제공하고, 생성된 숫자를 "MoveNumber"로 만드는 부분 부터는 중복되는 것으로 보였습니다. |
||
@FunctionalInterface | ||
public interface NumberGenerator { | ||
default MoveNumber generate() { | ||
return new MoveNumber(pickNumber()); | ||
} | ||
|
||
int pickNumber(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package racingcar.domain.numbergenerator; | ||
|
||
import camp.nextstep.edu.missionutils.Randoms; | ||
import racingcar.RacingConst; | ||
|
||
public class RandomNumberGenerator implements NumberGenerator { | ||
|
||
@Override | ||
public int pickNumber() { | ||
return Randoms.pickNumberInRange(RacingConst.RACING_NUMBER_START, | ||
RacingConst.RACING_NUMBER_END); | ||
} | ||
|
||
} |
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 ~ 4 주차 모든 공통 피드백 및 기능 요구사항을 확인할 것 같습니다!
기능 단위 커밋도 시간이 오래걸리는 일이기 때문에, 이 점도 함께 연습하시면 시간 관리 연습에 도움이 되실 것 같습니다 😊
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.
맞아요, 이번 구현에서 정말 아~무 생각 없이 구현을 시작하다 보니 놓쳤던 부분이라 저도 뼈저리게 아쉬운 부분입니다ㅠㅠ
감사합니다!