-
Notifications
You must be signed in to change notification settings - Fork 2
2주차 미션 다시 구현 #11
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: oliviarla
Are you sure you want to change the base?
2주차 미션 다시 구현 #11
Changes from all commits
2c8a53d
72369e7
9ee8c96
44464e3
bf277a4
2cfa9fb
15c6b05
01dd974
8024763
eb92b3d
a9185d4
5f8046b
b294dca
19461c4
be73fe2
8829032
967f871
a1bb02d
f13c04b
01e34cb
5b260ec
6c2a62c
65d6ef8
bbf4efa
f7a0fa7
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,37 @@ | ||
| # 숫자야구게임 | ||
| ### 룰 | ||
| 1부터 9까지 서로 다른 수로 이루어진 3자리의 수를 맞추는 게임 | ||
|
|
||
| 같은 수가 같은 자리에 있으면 `스트라이크` | ||
|
|
||
| 같은 수가 다른 자리에 있으면 `볼` | ||
|
|
||
| 같은 수가 전혀 없으면 `포볼 또는 낫싱` | ||
|
|
||
| <b>여러번 반복해 정보를 얻으며 상대방(컴퓨터)의 수를 맞추면 승리</b> | ||
|
|
||
| e.g. 상대방(컴퓨터)의 수가 425일 때 123을 제시한 경우 : `1스트라이크`, 456을 제시한 경우 : `1볼 1스트라이크`, 789를 제시한 경우 : `낫싱` | ||
|
|
||
| ### 작동 방식 | ||
|
|
||
| 1) 컴퓨터는 1에서 9까지 서로 다른 임의의 수 3개를 선택 | ||
| 2) 사용자는 컴퓨터가 생각하고 있는 3개의 숫자를 입력하고, 컴퓨터는 입력한 숫자에 대한 결과 출력 | ||
| 3) 같은 과정을 반복해 컴퓨터가 선택한 3개의 숫자를 모두 맞히면 게임 종료 | ||
| 4) 게임을 종료한 후 게임을 다시 시작하거나 완전히 종료할 수 있다 | ||
|
|
||
| ### 기능 목록 | ||
| <b>엔티티</b> | ||
| - [x] Ball: 세자리 숫자 저장하는 엔티티 클래스 | ||
| - [x] BallStatus: 유저 Ball과 랜덤 Ball 비교한 결과를 저장하는 엔티티 클래스 | ||
| - [x] Action: 동작들을 담는 enum 클래스 | ||
|
|
||
| <b>서비스</b> | ||
| - [x] BaseballService | ||
| - isBall: 볼 여부 판별 | ||
| - isStrike: 스트라이크 여부 판별 | ||
| - compare: RandomNum, UserNum 비교해 BallStatus 반환 | ||
| - [x] RandomNumGenerator: 임의의 서로다른 3자리 수 생성 | ||
|
|
||
| <b>뷰</b> | ||
| - [x] InputView: 사용자로부터 입력 받는 뷰 담당하는 클래스 | ||
| - [x] OutputView: 출력 하는 뷰 담당하는 클래스 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| package baseball; | ||
|
|
||
| import baseball.domain.Ball; | ||
| import baseball.domain.Baseball; | ||
| import baseball.domain.BaseballStatus; | ||
| import baseball.service.BaseballService; | ||
| import baseball.service.RandomBallGenerator; | ||
| import baseball.view.InputView; | ||
| import baseball.view.OutputView; | ||
|
|
||
| import java.util.stream.Collectors; | ||
|
|
||
| public class application { | ||
| public static void main(String[] args) throws Exception { | ||
| InputView inputView = new InputView(); | ||
| OutputView outputView = new OutputView(); | ||
| RandomBallGenerator randomBallGenerator = new RandomBallGenerator(); | ||
| BaseballService baseballService = new BaseballService(); | ||
|
|
||
| do { | ||
| Baseball randomBaseball = new Baseball(randomBallGenerator.makeNum()); | ||
| BaseballStatus baseballStatus; | ||
| do { | ||
| Baseball userBaseball = inputView.inputBall(); | ||
| baseballStatus = baseballService.compare(userBaseball, randomBaseball); | ||
| outputView.printBaseballStatus(baseballStatus); | ||
| } while (!outputView.exitGame(baseballStatus)); | ||
|
|
||
| }while(inputView.resumeGame()); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| package baseball.domain; | ||
|
|
||
| public enum Action { | ||
| 볼, 스트라이크, 낫싱 | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| package baseball.domain; | ||
|
|
||
| public class Ball { | ||
|
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. 추가적으로 Ball의 생애주기와 Baseball의 생애주기가 같은지에 대해서도 고민해보시는 걸 추천 드립니다! 힌트를 드리자면, 엔티티와 값 객체의 차이가 되겠군요! 👍 |
||
| private final int number; | ||
|
|
||
| public Ball(final int number) { | ||
| this.number = number; | ||
| } | ||
|
|
||
| public int getNumber() { | ||
| return number; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| package baseball.domain; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.Collections; | ||
| import java.util.List; | ||
|
|
||
| public class Baseball { | ||
|
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. InputView에서 Balseball의 리스르 크기 검사를 Baseball 구현체에서 진행하는건 어떨까요? |
||
| private final List<Ball> baseballs; | ||
|
|
||
| public Baseball(final List<Ball> ballList){ | ||
| this.baseballs= new ArrayList<>(ballList); | ||
| isDistinct(); | ||
| } | ||
|
|
||
| public List<Ball> getBaseballs() { | ||
| return Collections.unmodifiableList(baseballs); | ||
|
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 isDistinct(){ | ||
|
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. 해당 메서드의 depth가 4칸 정도 되는 것 같아요! depth를 낮추는 방법이 있을까요? |
||
| for(int i=0;i<this.baseballs.size();i++){ | ||
| for(int j=0;j<i;j++){ | ||
| if(this.baseballs.get(i).getNumber() == this.baseballs.get(j).getNumber()){ | ||
| throw new RuntimeException("중복된 값이 있습니다."); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| package baseball.domain; | ||
|
|
||
| public class BaseballStatus { | ||
| private final int ball; | ||
| private final int strike; | ||
|
|
||
| public BaseballStatus(int ball, int strike) throws Exception { | ||
| this.ball = ball; | ||
| this.strike = strike; | ||
| this.isValid(); | ||
|
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 isValid() throws Exception{ | ||
| if (!this.existsBall() && !this.existsStrike() && !this.nothing()) | ||
| throw new Exception("결과를 반환할 수 없습니다"); | ||
| } | ||
|
|
||
|
|
||
| public int getBall() { | ||
| return ball; | ||
| } | ||
|
|
||
| public int getStrike() { | ||
| return strike; | ||
| } | ||
|
|
||
| @Override | ||
| public String toString() { | ||
| return "BaseballStatus{" + | ||
| "ball=" + ball + | ||
| ", strike=" + strike + | ||
| '}'; | ||
| } | ||
|
|
||
| public boolean nothing() { | ||
| return strike == 0 && ball == 0; | ||
| } | ||
|
|
||
| public boolean existsBall() { | ||
| return ball > 0; | ||
| } | ||
|
|
||
| public boolean existsStrike() { | ||
| return strike > 0; | ||
| } | ||
|
|
||
| public boolean exitGame() { | ||
| return getStrike() == 3; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| package baseball.service; | ||
|
|
||
| import baseball.domain.Action; | ||
| import baseball.domain.Ball; | ||
| import baseball.domain.Baseball; | ||
| import baseball.domain.BaseballStatus; | ||
|
|
||
| public class BaseballService { | ||
| public boolean isStrike(Ball userBall, Ball randomBall) { | ||
| return userBall.getNumber() == randomBall.getNumber(); | ||
| } | ||
|
|
||
| public boolean isBall(Ball userBall, Baseball randomBall) { | ||
| return randomBall.getBaseballs().stream().anyMatch(ball -> ball.getNumber()==userBall.getNumber()); | ||
| } | ||
|
|
||
| public BaseballStatus compare(Baseball userBall, Baseball randomBall) throws Exception { | ||
| int strike = 0; | ||
| int ball = 0; | ||
| for (int i = 0; i < userBall.getBaseballs().size(); i++) { | ||
| if (isStrike(userBall.getBaseballs().get(i), randomBall.getBaseballs().get(i))) { | ||
| strike++; | ||
| continue; | ||
| } | ||
| if (isBall(userBall.getBaseballs().get(i), randomBall)) { | ||
| ball++; | ||
| } | ||
| } | ||
| return new BaseballStatus(ball, strike); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| package baseball.service; | ||
|
|
||
| import baseball.domain.Ball; | ||
|
|
||
| import java.util.*; | ||
|
|
||
| public class RandomBallGenerator { | ||
| static int MAX_SIZE = 3; | ||
| static int MAX_NUM = 9; | ||
|
Comment on lines
+8
to
+9
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. static을 사용해서 변수를 공유할 수 있게 됐군요! 그리고 추가적으로 접근 제어자를 선언하지 않는다면 기본적으로 설정되는 접근 제어자가 무엇일지 고민해봐도 좋을 것 같습니다. |
||
|
|
||
| public List<Ball> makeNum(){ | ||
| Random random = new Random(); | ||
| Set<Integer> set = new HashSet<>(); | ||
|
|
||
| while(set.size()<MAX_SIZE){ | ||
| int num = random.nextInt(MAX_NUM)+1; | ||
| set.add(num); | ||
| } | ||
|
|
||
| List<Ball> balls = new ArrayList<>(); | ||
| for(int e: set){ | ||
| Ball ball = new Ball(e); | ||
| balls.add(ball); | ||
| } | ||
| return balls; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| package baseball.view; | ||
|
|
||
| import baseball.domain.Ball; | ||
| import baseball.domain.Baseball; | ||
|
|
||
| import java.util.*; | ||
| import java.util.stream.Collectors; | ||
|
|
||
|
|
||
| public class InputView { | ||
| static int MAX_SIZE = 3; | ||
|
|
||
| private void isValidInput(List<Ball> ballList) throws RuntimeException{ | ||
| if(ballList.size()!=MAX_SIZE){ | ||
|
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. 줄 정렬이 필요해보이는 코드이군요 |
||
| throw new RuntimeException("3자리 숫자여야 합니다."); | ||
| } | ||
| if(!ballList.stream().allMatch(ball -> ball.getNumber()>0 && ball.getNumber()<=9)){ | ||
| throw new RuntimeException("범위를 벗어납니다."); | ||
| } | ||
| } | ||
|
|
||
| public Baseball inputBall() throws RuntimeException { | ||
| Scanner scanner = new Scanner(System.in); | ||
| System.out.print("숫자를 입력해 주세요 : "); | ||
| String input = scanner.next(); | ||
| List<Ball> list = Arrays.stream(input.split("")).mapToInt(Integer::parseInt).mapToObj(Ball::new).collect(Collectors.toList()); | ||
| isValidInput(list); | ||
| return new Baseball(list); | ||
| } | ||
|
|
||
| public boolean resumeGame() { | ||
| Scanner scanner = new Scanner(System.in); | ||
| System.out.println("게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요."); | ||
| int input = scanner.nextInt(); | ||
| return input == 1; | ||
| } | ||
|
|
||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| package baseball.view; | ||
|
|
||
| import baseball.domain.Action; | ||
| import baseball.domain.BaseballStatus; | ||
|
|
||
| public class OutputView { | ||
| public String outputBaseballStatus(BaseballStatus baseballStatus) { | ||
| String result = ""; | ||
|
|
||
| if (baseballStatus.existsBall()) { | ||
| result += baseballStatus.getBall() + Action.볼.toString() + " "; | ||
| } | ||
| if (baseballStatus.existsStrike()) { | ||
| result += baseballStatus.getStrike() + Action.스트라이크.toString(); | ||
| } | ||
| if (baseballStatus.nothing()) { | ||
| return Action.낫싱.toString(); | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| public void printBaseballStatus(BaseballStatus baseballStatus) { | ||
| System.out.println(outputBaseballStatus(baseballStatus)); | ||
| } | ||
|
|
||
| public boolean exitGame(BaseballStatus ballStatus) { | ||
| if (ballStatus.exitGame()) { | ||
| System.out.println("3개의 숫자를 모두 맞히셨습니다! 게임 종료"); | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| package baseball.service; | ||
|
|
||
| import baseball.domain.Ball; | ||
| import baseball.domain.Baseball; | ||
| import org.junit.jupiter.api.BeforeEach; | ||
| import org.junit.jupiter.api.Test; | ||
| import org.junit.jupiter.params.ParameterizedTest; | ||
| import org.junit.jupiter.params.provider.CsvSource; | ||
|
|
||
| import java.util.Arrays; | ||
|
|
||
| import static org.assertj.core.api.Assertions.assertThat; | ||
|
|
||
| public class BaseballServiceTest { | ||
| BaseballService baseballService; | ||
|
|
||
| @BeforeEach | ||
| void setUp(){ | ||
| baseballService= new BaseballService(); | ||
| } | ||
|
|
||
| @Test | ||
| void isStrikeTest(){ | ||
| Baseball userBall = new Baseball(Arrays.asList(new Ball(1),new Ball(2),new Ball(3))); | ||
| Baseball randomBall = new Baseball(Arrays.asList(new Ball(1),new Ball(2),new Ball(3))); | ||
| for(int i=0;i<3;i++){ | ||
| assertThat(baseballService.isStrike(userBall.getBaseballs().get(i), randomBall.getBaseballs().get(i))).isTrue(); | ||
| } | ||
| } | ||
|
|
||
| @Test | ||
| void isBallTest(){ | ||
| Baseball userBall = new Baseball(Arrays.asList(new Ball(1),new Ball(3),new Ball(2))); | ||
| Baseball randomBall = new Baseball(Arrays.asList(new Ball(2),new Ball(1),new Ball(3))); | ||
| for(int i=0;i<3;i++){ | ||
| assertThat(baseballService.isBall(userBall.getBaseballs().get(i), randomBall)).isTrue(); | ||
| } | ||
| } | ||
|
|
||
| @ParameterizedTest | ||
| @CsvSource({"1,2,6,1,9,2,1,1", "3,7,5,7,3,9,2,0", "1,2,3,4,5,6,0,0", "3,4,8,3,4,2,0,2"}) | ||
| void compareTest(int u1,int u2,int u3,int r1,int r2,int r3, int e1, int e2) throws Exception { | ||
| Baseball userBall = new Baseball(Arrays.asList(new Ball(u1),new Ball(u2),new Ball(u3))); | ||
| Baseball randomBall = new Baseball(Arrays.asList(new Ball(r1),new Ball(r2),new Ball(r3))); | ||
|
|
||
| assertThat(new int[]{baseballService.compare(userBall, randomBall).getBall(), baseballService.compare(userBall, randomBall).getStrike()}).isEqualTo(new int[]{e1, e2}); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| package baseball.service; | ||
|
|
||
| import baseball.domain.Ball; | ||
| import baseball.domain.Baseball; | ||
| import org.junit.jupiter.api.BeforeEach; | ||
| import org.junit.jupiter.api.DisplayName; | ||
| import org.junit.jupiter.api.Test; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| import static org.assertj.core.api.Assertions.assertThat; | ||
|
|
||
| public class RandomBallTest { | ||
| RandomBallGenerator randomBallGenerator; | ||
|
|
||
| @BeforeEach | ||
| public void setUp() { | ||
| randomBallGenerator = new RandomBallGenerator(); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("3개의 서로다른 숫자가 생성되는지 확인하는 테스트입니다.") | ||
| public void makeNumTest() { | ||
| List<Ball> list = randomBallGenerator.makeNum(); | ||
|
|
||
| Baseball randomNum = new Baseball(list); | ||
| assertThat(randomNum.getBaseballs().size()).isEqualTo(3); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("1부터 9 사이의 값인지 확인하는 테스트입니다.") | ||
| public void rangeTest() { | ||
| List<Ball> list = randomBallGenerator.makeNum(); | ||
|
|
||
| assertThat(list.stream().filter(ball -> ball.getNumber() >= 1 && ball.getNumber() <= 9).count()).isEqualTo(3); | ||
| } | ||
| } |
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 객체를 구현하셨군요!! 👍
해당 Ball 객체를 구현하면서 들어가게 되는 숫자의 유효성 검사를 해당 Ball에서 구현하는건 어떨까요?
즉, InputView에서 Balseball에 들어가는 숫자들의 유효성 검사를 Ball 구현체에서 진행하게 된다면 InputView와 그 외 객체들의 결합력을 낮추고 Ball이라는 구현체의 응집도를 높일 수 있을 것 같아요!