-
Notifications
You must be signed in to change notification settings - Fork 0
숫자 야구 코드 리뷰용 #1
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
base: main
Are you sure you want to change the base?
숫자 야구 코드 리뷰용 #1
Changes from all commits
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,13 @@ | ||
| ## 기능 목록 | ||
|
|
||
| ### 데이터 처리 | ||
| - [x] 컴퓨터 수 생성 | ||
| - [x] 1 ~ 9 범위의 랜덤 수 한 개 생성 | ||
| - [x] 중복되지 않는 수 3개 생성 | ||
| - [x] 생성된 수로 컴퓨터 수 생성 | ||
| - [x] 비교 결과 생성 | ||
| - [x] 볼, 스트라이크 개수 구하기 | ||
| - [x] 공 한 개가 볼인지 스트라이크인지 판정하기 | ||
|
|
||
| ### 고민한 부분 | ||
| - [x] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,21 @@ | ||
| package baseball; | ||
|
|
||
| import baseball.controller.BaseballGameController; | ||
| import baseball.model.BaseballGame; | ||
| import baseball.model.ComputerBallsGenerator; | ||
|
|
||
| public class Application { | ||
| public static void main(String[] args) { | ||
| // TODO: 프로그램 구현 | ||
| BaseballGameController baseballGameController = new BaseballGameController(baseballGame()); | ||
| baseballGameController.run(); | ||
| } | ||
|
|
||
| private static BaseballGame baseballGame() { | ||
| return new BaseballGame(computerBallsGenerator()); | ||
| } | ||
|
|
||
| private static ComputerBallsGenerator computerBallsGenerator() { | ||
| return new ComputerBallsGenerator(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| package baseball.controller; | ||
|
|
||
| import baseball.model.BaseballGame; | ||
| import baseball.model.GameResult; | ||
| import baseball.view.InputView; | ||
| import baseball.view.OutputView; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public class BaseballGameController { | ||
| private final InputView inputView = InputView.getInstance(); | ||
| private final OutputView outputView = OutputView.getInstance(); | ||
| private final BaseballGame baseballGame; | ||
|
|
||
| public BaseballGameController(BaseballGame baseballGame) { | ||
| this.baseballGame = baseballGame; | ||
| } | ||
|
|
||
| public void run() { | ||
| initializeGame(); | ||
| while (baseballGame.isContinuing()) { | ||
| playAGame(); | ||
| baseballGame.checkRegame(inputView.inputGameCommand()); | ||
| } | ||
| } | ||
|
|
||
| private void initializeGame() { | ||
| outputView.printStartingMessage(); | ||
| baseballGame.init(); | ||
| } | ||
|
|
||
| private void playAGame() { | ||
| while (baseballGame.isContinuing()) { | ||
| List<Integer> inputNumbers = inputView.inputNumbers(); | ||
| GameResult gameResult = baseballGame.compare(inputNumbers); | ||
| outputView.printResult(gameResult); | ||
| } | ||
| outputView.printWinningMessage(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| package baseball.model; | ||
|
|
||
| public class Ball { | ||
| private final BallNumber number; | ||
| private final Position position; | ||
|
|
||
| public Ball(BallNumber number, Position position) { | ||
| this.number = number; | ||
| this.position = position; | ||
| } | ||
|
|
||
| public static Ball from(int number, int position) { | ||
| return new Ball(BallNumber.valueOf(number), Position.valueOf(position)); | ||
| } | ||
|
|
||
| public boolean isBall(Ball other) { | ||
| return !position.equals(other.position) && number.equals(other.number); | ||
| } | ||
|
|
||
| public boolean isStrike(Ball other) { | ||
| return position.equals(other.position) && number.equals(other.number); | ||
| } | ||
|
|
||
| public BallNumber getNumber() { | ||
| return number; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| package baseball.model; | ||
|
|
||
| import java.util.HashMap; | ||
| import java.util.Map; | ||
| import java.util.Objects; | ||
|
|
||
| public class BallNumber { | ||
| public static final int LOWER_BOUNDS = 1; | ||
| public static final int UPPER_BOUNDS = 9; | ||
| private static final Map<Integer, BallNumber> CACHE = new HashMap<>(); | ||
| private final int number; | ||
|
|
||
| static { | ||
| for (int number = LOWER_BOUNDS; number <= UPPER_BOUNDS; number++) { | ||
| CACHE.put(number, new BallNumber(number)); | ||
| } | ||
| } | ||
|
|
||
| private BallNumber(int number) { | ||
| validate(number); | ||
| this.number = number; | ||
| } | ||
|
|
||
| private void validate(int ballNumber) { | ||
| if (ballNumber < LOWER_BOUNDS || ballNumber > UPPER_BOUNDS) { | ||
| throw new IllegalArgumentException(); | ||
| } | ||
| } | ||
|
|
||
| public static BallNumber valueOf(int number) { | ||
| BallNumber ballNumber = CACHE.get(number); | ||
| if (Objects.isNull(ballNumber)) { | ||
| ballNumber = new BallNumber(number); | ||
| } | ||
| return ballNumber; | ||
| } | ||
|
|
||
|
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. 정적 팩터리 메서드를 볼 때 생성자를 호출하는 부분이 validate를 위한게 아니라 인스턴스를 생성한다는 느낌을 받았어요!
Owner
Author
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 int getNumber() { | ||
| return number; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| package baseball.model; | ||
|
|
||
| import java.util.List; | ||
| import java.util.function.Predicate; | ||
| import java.util.stream.Collectors; | ||
| import java.util.stream.IntStream; | ||
|
|
||
| public class Balls { | ||
| public static final int SIZE = 3; | ||
|
|
||
| private final List<Ball> balls; | ||
|
|
||
| public static Balls from(List<Integer> numbers) { | ||
| List<Ball> ballList = IntStream.range(0, numbers.size()) | ||
| .mapToObj(i -> Ball.from(numbers.get(i), i)) | ||
| .collect(Collectors.toList()); | ||
| return new Balls(ballList); | ||
| } | ||
|
|
||
| public Balls(List<Ball> balls) { | ||
| validate(balls); | ||
| this.balls = balls; | ||
| } | ||
|
|
||
| private void validate(List<Ball> balls) { | ||
| validateSize(balls); | ||
| validateDuplication(balls); | ||
| } | ||
|
|
||
| private void validateSize(List<Ball> balls) { | ||
| if (balls.size() != SIZE) { | ||
| throw new IllegalArgumentException(); | ||
| } | ||
| } | ||
|
|
||
| private void validateDuplication(List<Ball> balls) { | ||
| long uniqueBallsCount = balls.stream() | ||
| .map(Ball::getNumber) | ||
| .distinct() | ||
| .count(); | ||
|
|
||
| if (uniqueBallsCount != SIZE) { | ||
| throw new IllegalArgumentException(); | ||
| } | ||
| } | ||
|
|
||
| public GameResult compare(Balls other) { | ||
| int ballCount = countFiltered(other, this::ballPredicate); | ||
| int strikeCount = countFiltered(other, this::strikePredicate); | ||
| return GameResult.of(ballCount, strikeCount); | ||
| } | ||
|
|
||
| private int countFiltered(Balls other, Predicate<Ball> predicate) { | ||
| return (int) other.balls.stream() | ||
| .filter(predicate) | ||
| .count(); | ||
| } | ||
|
|
||
| private boolean ballPredicate(Ball otherBall) { | ||
| return balls.stream() | ||
| .anyMatch(ball -> ball.isBall(otherBall)); | ||
| } | ||
|
|
||
| private boolean strikePredicate(Ball otherBall) { | ||
| return balls.stream() | ||
| .anyMatch(ball -> ball.isStrike(otherBall)); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| package baseball.model; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public class BaseballGame { | ||
| private final ComputerBallsGenerator computerBallsGenerator; | ||
| private GameStatus gameStatus; | ||
| private Balls computerBalls; | ||
|
|
||
| public BaseballGame(ComputerBallsGenerator computerBallsGenerator) { | ||
| this.computerBallsGenerator = computerBallsGenerator; | ||
| } | ||
|
|
||
| public void init() { | ||
| gameStatus = GameStatus.CONTINUING; | ||
| computerBalls = computerBallsGenerator.generate(); | ||
| } | ||
|
|
||
| public boolean isContinuing() { | ||
| return gameStatus.isContinuing(); | ||
| } | ||
|
|
||
| public GameResult compare(List<Integer> inputNumbers) { | ||
| Balls userBalls = Balls.from(inputNumbers); | ||
| GameResult result = computerBalls.compare(userBalls); | ||
| checkUserWin(result); | ||
| return result; | ||
| } | ||
|
|
||
| private void checkUserWin(GameResult result) { | ||
| if (result.isUserWin()) { | ||
| gameStatus = GameStatus.STOP; | ||
| } | ||
| } | ||
|
|
||
| public void checkRegame(GameCommand gameCommand) { | ||
| if (gameCommand.isRestart()) { | ||
| gameStatus = GameStatus.CONTINUING; | ||
| computerBalls = computerBallsGenerator.generate(); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| package baseball.model; | ||
|
|
||
| import camp.nextstep.edu.missionutils.Randoms; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.HashSet; | ||
| import java.util.List; | ||
| import java.util.Set; | ||
| import java.util.stream.IntStream; | ||
|
|
||
| import static java.util.stream.Collectors.collectingAndThen; | ||
| import static java.util.stream.Collectors.toList; | ||
|
|
||
| public class ComputerBallsGenerator { | ||
| public Balls generate() { | ||
| List<Integer> uniqueNumbers = generateUniqueNumbers(); | ||
| return IntStream.rangeClosed(Position.LOWER_BOUNDS, Position.UPPER_BOUNDS) | ||
| .mapToObj(position -> Ball.from(uniqueNumbers.get(position), position)) | ||
| .collect(collectingAndThen(toList(), Balls::new)); | ||
| } | ||
|
|
||
| private List<Integer> generateUniqueNumbers() { | ||
| Set<Integer> uniqueNumbers = new HashSet<>(); | ||
| while (hasEnough(uniqueNumbers)) { | ||
| int number = Randoms.pickNumberInRange(BallNumber.LOWER_BOUNDS, BallNumber.UPPER_BOUNDS); | ||
| uniqueNumbers.add(number); | ||
| } | ||
| return new ArrayList<>(uniqueNumbers); | ||
| } | ||
|
|
||
|
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. Streamg.generate를 사용한다면 조금 더 간단하게 메서드를 작성할 수 있을 것 같아요!
Owner
Author
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 boolean hasEnough(Set<Integer> uniqueNumbers) { | ||
| return uniqueNumbers.size() < Balls.SIZE; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| package baseball.model; | ||
|
|
||
| import java.util.Arrays; | ||
|
|
||
| public enum GameCommand { | ||
| RESTART(1), | ||
| TERMINATION(2); | ||
|
|
||
| private final int code; | ||
|
|
||
| GameCommand(int code) { | ||
| this.code = code; | ||
| } | ||
|
|
||
| public static GameCommand from(int code) { | ||
| return Arrays.stream(values()) | ||
| .filter(gameCommand -> gameCommand.code == code) | ||
| .findAny() | ||
| .orElseThrow(() -> new IllegalArgumentException()); | ||
| } | ||
|
|
||
| public boolean isRestart() { | ||
| return this.equals(RESTART); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| package baseball.model; | ||
|
|
||
| import java.util.Arrays; | ||
|
|
||
| public enum GameResult { | ||
| STRIKE_3_BALL_0(3, 0, "3스트라이크"), | ||
| STRIKE_1_BALL_2(1, 2, "2볼 1스트라이크"), | ||
| STRIKE_0_BALL_3(0, 3, "3볼"), | ||
| STRIKE_2_BALL_0(2, 0, "2스트라이크"), | ||
| STRIKE_1_BALL_1(1, 1, "1볼 1스트라이크"), | ||
| STRIKE_0_BALL_2(0, 2, "2볼"), | ||
| STRIKE_1_BALL_0(1, 0, "1스트라이크"), | ||
| STRIKE_0_BALL_1(0, 1, "1볼"), | ||
| NOTHING(0, 0, "낫싱"); | ||
|
|
||
| private final int strikeCount; | ||
| private final int ballCount; | ||
| private final String output; | ||
|
|
||
| GameResult(int strikeCount, int ballCount, String output) { | ||
| this.strikeCount = strikeCount; | ||
| this.ballCount = ballCount; | ||
| this.output = output; | ||
| } | ||
|
|
||
| public static GameResult of(int ballCount, int strikeCount) { | ||
| return Arrays.stream(values()) | ||
| .filter(gameResult -> gameResult.matches(ballCount, strikeCount)) | ||
| .findAny() | ||
| .orElseThrow(() -> new IllegalArgumentException()); | ||
| } | ||
|
|
||
| private boolean matches(int ballCount, int strikeCount) { | ||
| return this.strikeCount == strikeCount && this.ballCount == ballCount; | ||
| } | ||
|
|
||
| public boolean isUserWin() { | ||
| return this.equals(STRIKE_3_BALL_0); | ||
| } | ||
|
|
||
| public String getOutput() { | ||
| return output; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| package baseball.model; | ||
|
|
||
| public enum GameStatus { | ||
| CONTINUING, | ||
| STOP; | ||
|
|
||
| public boolean isContinuing() { | ||
| return this.equals(CONTINUING); | ||
| } | ||
| } |
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.
Position을 따로 만들어준게 인상깊어요 👍
다만 클래스명이 볼카운트의 볼과 동일하여 혼동의 여지가 있을 것 같아요!
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.
별 생각없이 이름 지었는데 Ball, Strike를 표현하는 객체로 오인할 수 있겠네요:)