Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
2c8a53d
docs README.md
oliviarla May 22, 2022
72369e7
feat: inputView, resultView
oliviarla May 22, 2022
9ee8c96
docs: README.md 수정
oliviarla May 22, 2022
44464e3
test: BaseballTest 구현
oliviarla May 22, 2022
bf277a4
feat: main 클래스 추가
oliviarla May 22, 2022
2cfa9fb
feat: Baseball 구현 완료
oliviarla May 22, 2022
15c6b05
docs(baseball): README.md 기능 목록 다시 작성
oliviarla May 26, 2022
01dd974
feat(baseball): InputView 구현
oliviarla May 26, 2022
8024763
test(baseball): InputView test
oliviarla May 26, 2022
eb92b3d
feat(baseball): 엔티티 클래스 구현
oliviarla May 26, 2022
a9185d4
docs(baseball): update README.md
oliviarla May 26, 2022
5f8046b
feat(baseball): OutputView 구현
oliviarla May 27, 2022
b294dca
test(baseball): OutputViewTest 구현
oliviarla May 27, 2022
19461c4
feat(baseball): OutputView 구현
oliviarla May 27, 2022
be73fe2
feat(baseball): RandomNumGenerator 구현
oliviarla May 27, 2022
8829032
test(baseball): RandomNumGenerator 테스트
oliviarla May 27, 2022
967f871
docs(baseball): RandomNumGenerator 구현
oliviarla May 27, 2022
a1bb02d
refactor(baseball): Ball 객체의 혼동 방지 위해 Baseball로 이름 변경 + Baseball에서 순서…
oliviarla May 27, 2022
f13c04b
feat(baseball): BaseballService 구현
oliviarla May 27, 2022
01e34cb
docs(baseball): BaseballService 구현
oliviarla May 27, 2022
5b260ec
test(baseball): BaseballService 테스트 구현
oliviarla May 27, 2022
6c2a62c
feat(baseball): 전체 애플리케이션 구현 완료
oliviarla May 27, 2022
65d6ef8
refactor(baseball): 전체 애플리케이션 수정 및 구현 완료
oliviarla May 27, 2022
bbf4efa
refactor(baseball): 사용자 입력 숫자 개수 예외처리
oliviarla May 27, 2022
f7a0fa7
refactor(baseball): 코드 리뷰에 따라 불변 객체로 수정, Ball 엔티티 추가, 서비스 로직 변경 완료
oliviarla Jun 4, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions src/main/java/baseball/README.md
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: 출력 하는 뷰 담당하는 클래스
31 changes: 31 additions & 0 deletions src/main/java/baseball/application.java
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());
}
}
5 changes: 5 additions & 0 deletions src/main/java/baseball/domain/Action.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package baseball.domain;

public enum Action {
볼, 스트라이크, 낫싱
}
13 changes: 13 additions & 0 deletions src/main/java/baseball/domain/Ball.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package baseball.domain;

public class Ball {

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이라는 구현체의 응집도를 높일 수 있을 것 같아요!

Choose a reason for hiding this comment

The 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;
}
}
28 changes: 28 additions & 0 deletions src/main/java/baseball/domain/Baseball.java
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 {

Choose a reason for hiding this comment

The 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);
Copy link

@this-is-spear this-is-spear Jun 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

리스트를 조회할 때, unmodifiableList 메서드를 사용해 수정할 수 없도록 구현하셨군요!
좋은 방법입니다. 👍
하지만 unmodifiableList 메서드만 사용해서 리스트를 호출하게 되면 정말 수정이 불가능할까요? ☺️
이 부분에 대해서는 수정이 필요하진 않지만, 생각해보는걸 추천드려요! 👍

}

public void isDistinct(){

Choose a reason for hiding this comment

The 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("중복된 값이 있습니다.");
}
}
}
}
}
49 changes: 49 additions & 0 deletions src/main/java/baseball/domain/BaseballStatus.java
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();

Choose a reason for hiding this comment

The 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;
}
}
31 changes: 31 additions & 0 deletions src/main/java/baseball/service/BaseballService.java
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);
}
}
27 changes: 27 additions & 0 deletions src/main/java/baseball/service/RandomBallGenerator.java
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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

static을 사용해서 변수를 공유할 수 있게 됐군요!
하지만 값이 변경될 수 있기 때문에 이슈가 발생할 수 있는 코드입니다. 😱
final 변수를 생성할 때에는 static은 선택적이지만, static일 때에는 final은 필수일 것 같아요! ☺️

그리고 추가적으로 접근 제어자를 선언하지 않는다면 기본적으로 설정되는 접근 제어자가 무엇일지 고민해봐도 좋을 것 같습니다. ☺️ 물론 생략은 좋지 않아요 👍


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;
}
}
39 changes: 39 additions & 0 deletions src/main/java/baseball/view/InputView.java
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){
Copy link

@this-is-spear this-is-spear Jun 4, 2022

Choose a reason for hiding this comment

The 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;
}


}
33 changes: 33 additions & 0 deletions src/main/java/baseball/view/OutputView.java
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;
}
}
48 changes: 48 additions & 0 deletions src/test/java/baseball/service/BaseballServiceTest.java
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});
}
}
37 changes: 37 additions & 0 deletions src/test/java/baseball/service/RandomBallTest.java
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);
}
}
Loading