미션 간단 설명
사용자에서 경주차의 이름과 반복 횟수를 물어보고 해당 횟수만큼 전진 여부를 결정한다. 4/9 확률로 전진하며, 경주를 마치고 최종 우승자들을 출력하면 되는 문제이다.
요구사함
기능 요구사항
-
입력
- 경주차 이름 입력
한 줄의 입력으로 받으며, 문자열로 받는다.
- 시도할 횟수
int 형으로 받는다.
- ,를 구분자로 문자열 분리
-
출력
- 경주차 이름 입력을 요구
경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)
- 횟수를 입력 요구
시도할 횟수는 몇 회인가요?
- 차수별 실행 결과
객체 배열로 이름(문자열)과, 현재 상태(int)를 전달
- 우승자 출력
배열로 입력을 받아서, 배열에 있는 모든 문자열을 출력; 한명 이상일시 ,로 구분
-
랜덤 함수
0~9사이의 숫자 출력
- 에러 처리
- 빈 문자열을 받은 경우
[ERROR] 입력을 받지 못하였습니다.
- 구분자로 분리한 문자열이 빈 문자열인 경우
[ERROR] 경주 자동차의 이름이 결정되지 않았습니다.
- 이름은 5자 이하만 가능
[ERROR] 이름은 5자 이하여야 합니다.
- 입력이 숫자가 아닌 경우
[ERROR] 횟수는 숫자여야 합니다.
- 숫자가 음수인지 검증
[ERROR] 횟수는 음수일 수 없습니다.
- 경주차의 이름이 겹치는지 검증
[ERROR] 이름이 겹칩니다.
IllegalArgumentException을 발생시킨 후 애플리케이션은 종료
- Racing Car 함수
- 입력 받은 문자열 배열을 사용하여 배열을 초기화 한다.
- 입력받은 int만큼 반복문 통해 반복 실행한다.
- 배열을 전달시 iterator인 객체값을 다음 함수로 전달
객체의 멤버 함수로 이름(문자열)과, 현재 상태(int)를 전달
- 랜덤 함수에 의해 객체에서 int 값 수정
0
3일시 그대로, 49일시 +1
입출력 요구사항
입력
- 경주할 자동차 이름(이름은 쉼표(,) 기준으로 구분)
pobi,woni,jun
- 시도할 횟수
5
출력
- 차수별 실행 결과
pobi : --
woni : ----
jun : ----
우승자 출력
- 단독 우승자 안내 문구
최종 우승자 : pobi
- 공동 우승자 안내 문구
최종 우승자 : pobi, jun
실행 결과 예시
경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)
pobi,woni,jun
시도할 횟수는 몇 회인가요?
5
실행 결과
pobi : -
woni :
jun : -
pobi : --
woni : -
jun : --
pobi : ---
woni : --
jun : ---
pobi : ----
woni : ---
jun : ----
pobi : -----
woni : ----
jun : -----
최종 우승자 : pobi, jun프로그래밍 요구 사항
기존 요구사항
- JDK 21 버전에서 실행 가능해야 한다.
- 프로그램 실행의 시작점은 Application의 main()이다.
- build.gradle 파일은 변경할 수 없으며, 제공된 라이브러리 이외의 외부 라이브러리는 사용하지 않는다.
- 프로그램 종료 시 System.exit()를 호출하지 않는다.
- 프로그래밍 요구 사항에서 달리 명시하지 않는 한 파일, 패키지 등의 이름을 바꾸거나 이동하지 않는다.
- 자바 코드 컨벤션을 지키면서 프로그래밍한다.
- 기본적으로 Java Style Guide를 원칙으로 한다.
- 라이브러리 camp.nextstep.edu.missionutils에서 제공하는 Console API를 사용하여 구현해야 한다. 사용자가 입력하는 값은 camp.nextstep.edu.missionutils.Console의 readLine()을 활용한다.
추가 요구사항
-
indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다.
- 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다.
- 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메서드)를 분리하면 된다.
-
3항 연산자를 쓰지 않는다.
-
함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라.
-
JUnit 5와 AssertJ를 이용하여 정리한 기능 목록이 정상적으로 작동하는지 테스트 코드로 확인한다.
-
라이브러리 camp.nextstep.edu.missionutils에서 제공하는 Randoms 및 Console API를 사용하여 구현해야 한다. Random 값 추출은 camp.nextstep.edu.missionutils.Randoms의 pickNumberInRange()를 활용한다. 사용자가 입력하는 값은 camp.nextstep.edu.missionutils.Console의 readLine()을 활용한다. 사용 예시 : Randoms.pickNumberInRange(0, 9);
-
기본적인 Git 명령어를 숙지한다 + Git으로 관리할 자원을 고려한다
-
커밋 메시지를 의미 있게 작성한다
-
커밋 메시지에 이슈 또는 풀 리퀘스트 번호를 포함하지 않는다
-
풀 리퀘스트를 만든 후에는 닫지 말고 추가 커밋을 한다
-
오류를 찾을 때 출력 함수 대신 디버거를 사용한다
-
이름을 통해 의도를 드러낸다
-
축약하지 않는다
-
공백도 코딩 컨벤션이다
-
공백 라인을 의미 있게 사용한다
-
스페이스와 탭을 혼용하지 않는다
-
코드 포매팅을 사용한다
- intelliJ IDEA : Ctrl+Alt+L
- Eclipse : Ctrl+Shift+F
- Visual Studio Code : Shift+Alt+F
-
Java에서 제공하는 API를 적극 활용한다
-
배열 대신 컬렉션을 사용한다
프로젝트 구조
java-racingcar-8/
├── src/main/java/racingcar/
│ ├── Application.java
│ ├── controller/
│ │ └── RacingController.java
│ ├── model/
│ │ ├── Racer.java
│ │ └── RandomGenerator.java
│ └── view/
│ ├── Input.java
│ ├── Output.java
│ ├── Validator.java
│ ├── Message.java
│ └── ErrorMessage.java함수 기능 소개
Application 클래스
| 메서드 | 기능 | 접근제어자 | 반환타입 | 매개변수 |
|---|---|---|---|---|
| main() | 프로그램 진입점, RacingController 실행 | public static | void | String[] |
RacingController 클래스
| 메서드 | 기능 | 접근제어자 | 반환타입 | 매개변수 |
|---|---|---|---|---|
| run() | 게임 전체 흐름 제어 (입력→실행→출력) | public | void | - |
| makeArray() | 이름 문자열 배열을 Racer 객체 배열로 변환 | private | Racer[] | String[] |
| situation() | 게임 진행 (n회 반복, 랜덤 생성, 이동, 출력) | private | void | Racer[], int |
| moveRacer() | 랜덤값 4 이상인 경주차들 전진 | private | void | Racer[], int[] |
| findMaxPosition() | 경주차 중 최대 위치값 찾기 | private | int | Racer[] |
| findWinner() | 최대 위치의 경주차 이름들 반환 | private | String[] | Racer[] |
Racer 클래스 (Model)
| 메서드 | 기능 | 접근제어자 | 반환타입 | 매개변수 |
|---|---|---|---|---|
| Racer() | 생성자, 이름 설정 및 위치 0 초기화 | public | - | String |
| move() | 현재 위치 1 증가 | public | void | - |
| getName() | 경주차 이름 반환 | public | String | - |
| getStatus() | 경주차 현재 위치 반환 | public | int | - |
RandomGenerator 클래스 (Model)
| 메서드 | 기능 | 접근제어자 | 반환타입 | 매개변수 |
|---|---|---|---|---|
| returnArray() | 0~9 사이 랜덤 숫자 배열 생성 | public | int[] | int |
Input 클래스 (View)
| 메서드 | 기능 | 접근제어자 | 반환타입 | 매개변수 |
|---|---|---|---|---|
| readLine() | Console에서 입력 받기, 예외 처리 | private | String | - |
| Number() | 시도 횟수 입력 및 검증 (숫자, 음수) | public | int | - |
| UserName() | 경주차 이름 입력 및 검증 (빈 문자열, 길이, 중복) | public | String[] | - |
Output 클래스 (View)
| 메서드 | 기능 | 접근제어자 | 반환타입 | 매개변수 |
|---|---|---|---|---|
| init() | 경주차 이름 입력 안내 메시지 출력 | public | void | - |
| askFrequency() | 시도 횟수 입력 안내 메시지 출력 | public | void | - |
| runResult() | “실행 결과” 메시지 출력 | public | void | - |
| situation() | 각 경주차의 현재 위치 출력 | public | void | Racer[] |
| printWinner() | 최종 우승자 출력 | public | void | String[] |
Validator 클래스 (View)
| 메서드 | 기능 | 접근제어자 | 반환타입 | 매개변수 |
|---|---|---|---|---|
| parseNumber() | 문자열을 정수로 변환, 예외 처리 | public | int | String |
| hasEmptyString() | 배열 내 빈 문자열 검증 | public | boolean | String[] |
| EmptyString() | 단일 문자열이 비어있는지 검증 | private | boolean | String |
| underFive() | 배열 내 모든 문자열 길이 5 이하 검증 | public | boolean | String[] |
| checkUnderFive() | 단일 문자열 길이 5 이하 검증 | private | boolean | String |
| isNegative() | 숫자가 음수인지 검증 | public | boolean | int |
| duplicationName() | 이름 중복 검증 (HashSet 사용) | public | boolean | String[] |
Message 클래스 (Enum)
| 상수 | 값 | 용도 |
|---|---|---|
| COLON | ” : “ | 출력 포맷용 구분자 |
| HYPHEN | ”-“ | 경주차 위치 표시 |
| WINNER | ”최종 우승자” | 우승자 출력 레이블 |
| INIT | ”경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)“ | 이름 입력 안내 |
| FREQUNENCY | ”시도할 횟수는 몇 회인가요?” | 횟수 입력 안내 |
| RUNRESULT | ”실행 결과” | 실행 결과 헤더 |
ErrorMessage 클래스 (Enum)
| 상수 | 값 | 용도 |
|---|---|---|
| ERROR | ”[ERROR]“ | 에러 메시지 접두사 |
| NOINPUT | ”입력을 받지 못했습니다.” | 입력 없음 에러 |
| EMPTYSTRING | ”이름이 빈 문자열 일 수 없습니다.” | 빈 문자열 에러 |
| NEGATIVE | ”입력이 음수 입니다.” | 음수 입력 에러 |
| DUPLICATION | ”경주차의 이름이 중복되었습니다.” | 이름 중복 에러 |
| UNDERFIVE | ”이름은 다섯 글자 이하여야 합니다.” | 길이 초과 에러 |
| NOTNUMBER | ”숫자가 아닙니다.” | 숫자 형식 에러 |
다이어그램
플로우 차트
flowchart TD Start([프로그램 시작]) --> Init[경주차 이름 입력] Init --> emptyString{빈 문자열 검증} emptyString -->|성공| underFive{5글자 이하인지 검증} emptyString -->|실패| Error underFive-->|성공| duplication{중복 이름 검사} underFive-->|실패| Error duplication-->|성공| frequnency[횟수 입력] duplication-->|실패| Error frequnency --> Parse{입력이 숫자인지 검증} Parse -->|성공| Negative{음수인지 검증} Parse -->|실패| Error Negative -->|성공| makeArray[배열 생성] Negative -->|실패| Error[에러 처리] makeArray --> situation[게임 진행] situation --> chosseWinner[우승자 선별] chosseWinner--> Winner[우승자 출력] Winner --> End([종료])
클래스 다이어그램
classDiagram Application --> RacingController RacingController --> Input RacingController --> Output RacingController --> RandomGenerator RacingController --> Racer Input --> Validator class Application { +void main() } class RacingController { -Input input -Output output -RandomGenerator randomGenerator +void run() -Racer[] makeArray(String[]) -void situation(Racer[], int) -void moveRacer(Racer[], int[]) -int findMaxPosition(Racer[]) -String[] findWinner(Racer[]) } class Input{ -Validator validator -String readLine() +int Number() +String[] UserName() } class Validator{ +int parseNumber(String) +boolean hasEmptyString(String[]) -boolean EmptyString(String) +boolean underFive(String[]) -boolean checkUnderFive(String) +boolean isNegative(int) +boolean duplicationName(String[]) } class Output{ +void init() +void askFrequnency() +void runResult() +void situation(Racer[]) +void printWinner(String[]) } class RandomGenerator{ +int[] returnArray(int) } class Racer{ -String name -int status +Racer(String) +void move() +String getName() +int getStatus() }
시퀀스 다이어그램
sequenceDiagram actor User participant Application participant RacingController participant Input participant Validator participant Output participant RandomGenerator participant Racer User->>Application: 프로그램 실행 Application->>RacingController: run() Note over RacingController,Output: 경주차 이름 입력 단계 RacingController->>Output: init() Output->>User: "경주할 자동차 이름을 입력하세요" User->>Input: "pobi,woni,jun" Input->>Input: readLine() Input->>Validator: hasEmptyString(String[]) alt 빈 문자열 존재 Validator-->>Input: IllegalArgumentException Input-->>User: [ERROR] 이름이 빈 문자열 end Input->>Validator: underFive(String[]) alt 5자 초과 Validator-->>Input: IllegalArgumentException Input-->>User: [ERROR] 5자 초과 end Input->>Validator: duplicationName(String[]) alt 중복 존재 Validator-->>Input: IllegalArgumentException Input-->>User: [ERROR] 중복 end Input-->>RacingController: String[] names Note over RacingController,Output: 시도 횟수 입력 단계 RacingController->>Output: askFrequency() Output->>User: "시도할 횟수는 몇 회인가요?" User->>Input: "5" Input->>Input: readLine() Input->>Validator: parseNumber(String) alt 숫자가 아님 Validator-->>Input: IllegalArgumentException Input-->>User: [ERROR] 숫자가 아님 end Input->>Validator: isNegative(int) alt 음수 Validator-->>Input: IllegalArgumentException Input-->>User: [ERROR] 음수 end Input-->>RacingController: int frequency Note over RacingController,Racer: 게임 준비 RacingController->>Output: runResult() Output->>User: "실행 결과" RacingController->>RacingController: makeArray(String[]) loop 각 이름마다 RacingController->>Racer: new Racer(name) Racer-->>RacingController: Racer 객체 end Note over RacingController,Racer: 게임 진행 RacingController->>RacingController: situation(Racer[], int) loop frequency 횟수만큼 RacingController->>RandomGenerator: returnArray(length) RandomGenerator-->>RacingController: int[] randomValues RacingController->>RacingController: moveRacer(Racer[], int[]) loop 각 경주차마다 alt randomValue >= 4 RacingController->>Racer: move() Racer->>Racer: status++ end end RacingController->>Output: situation(Racer[]) loop 각 경주차마다 Output->>Racer: getName() Racer-->>Output: String name Output->>Racer: getStatus() Racer-->>Output: int status Output->>User: "name : ---" end Output->>User: (빈 줄) end Note over RacingController,Output: 우승자 결정 RacingController->>RacingController: findWinner(Racer[]) RacingController->>RacingController: findMaxPosition(Racer[]) loop 각 경주차마다 RacingController->>Racer: getStatus() Racer-->>RacingController: int status end RacingController->>RacingController: 최댓값과 같은 경주차 수집 loop 각 경주차마다 alt status == maxPosition RacingController->>Racer: getName() Racer-->>RacingController: String name end end RacingController->>Output: printWinner(String[]) Output->>User: "최종 우승자 : pobi, jun" RacingController-->>Application: 종료 Application-->>User: 프로그램 종료