From 41671e2be6315dcdd0447b9f1579df85ba4c0fa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=B5=9C=EC=A4=80=ED=98=B8?= Date: Mon, 26 Jun 2023 09:25:49 +0900 Subject: [PATCH 01/23] Update README.md --- README.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 86699576c..d75fb0d07 100644 --- a/README.md +++ b/README.md @@ -9,12 +9,10 @@ ## ✉️ 미션 제출 방법 - 미션 구현을 완료한 후 GitHub을 통해 제출해야 한다. - - GitHub을 활용한 제출 방법은 [프리코스 과제 제출 문서](https://github.com/woowacourse/woowacourse-docs/tree/master/precourse) 를 참고해 제출한다. -- GitHub에 미션을 제출한 후 [우아한테크코스 지원 플랫폼](https://apply.techcourse.co.kr) 에 접속하여 프리코스 과제를 제출한다. - - 자세한 방법은 [링크](https://github.com/woowacourse/woowacourse-docs/tree/master/precourse#제출-가이드) 를 참고한다. - - **Pull Request만 보내고, 지원 플랫폼에서 과제를 제출하지 않으면 최종 제출하지 않은 것으로 처리되니 주의한다.** + - 본인의 GitHub ID와 동일한 이름의 브랜치에 코드를 제출한다. + - main Branch로 Pull Request를 생성한다. -## ✔️ 과제 제출 전 체크리스트 - 0점 방지 +## ✔️ 과제 제출 전 체크리스트 - 터미널에서 `java -version`을 실행해 자바 8인지 확인한다. 또는 Eclipse, IntelliJ IDEA와 같은 IDE의 자바 8로 실행하는지 확인한다. - 터미널에서 맥 또는 리눅스 사용자의 경우 `./gradlew clean test`, 윈도우 사용자의 경우 `gradlew.bat clean test` 명령을 실행했을 때 모든 테스트가 아래와 같이 통과하는지 확인한다. @@ -164,4 +162,3 @@ public enum Coin { - **기능을 구현하기 전에 java-vendingmachine-precourse/docs/README.md 파일에 구현할 기능 목록을 정리**해 추가한다. - **Git의 커밋 단위는 앞 단계에서 README.md 파일에 정리한 기능 목록 단위**로 추가한다. - [AngularJS Commit Message Conventions](https://gist.github.com/stephenparish/9941e89d80e2bc58a153) 참고해 commit log를 남긴다. -- 과제 진행 및 제출 방법은 [프리코스 과제 제출 문서](https://github.com/woowacourse/woowacourse-docs/tree/master/precourse) 를 참고한다. From f1197e6637dcd991fa2b7a0bc989f5e88d63b7f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=B5=9C=EC=A4=80=ED=98=B8?= Date: Tue, 26 Sep 2023 02:52:33 +0900 Subject: [PATCH 02/23] Update README.md --- README.md | 48 ++++++++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index d75fb0d07..44ff8ba0c 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,28 @@ # 미션 - 자판기 -## 🔍 진행방식 +## 🔍 진행 방식 -- 미션은 **기능 요구사항, 프로그래밍 요구사항, 과제 진행 요구사항** 세 가지로 구성되어 있다. -- 세 개의 요구사항을 만족하기 위해 노력한다. 특히 기능을 구현하기 전에 기능 목록을 만들고, 기능 단위로 커밋 하는 방식으로 진행한다. -- 기능 요구사항에 기재되지 않은 내용은 스스로 판단하여 구현한다. +- 미션은 **기능 요구 사항, 프로그래밍 요구 사항, 과제 진행 요구 사항** 세 가지로 구성되어 있다. +- 세 개의 요구 사항을 만족하기 위해 노력한다. 특히 기능을 구현하기 전에 기능 목록을 만들고, 기능 단위로 커밋 하는 방식으로 진행한다. +- 기능 요구 사항에 기재되지 않은 내용은 **스스로 판단하여 구현한다.** -## ✉️ 미션 제출 방법 +## 📮 미션 제출 방법 - 미션 구현을 완료한 후 GitHub을 통해 제출해야 한다. - - 본인의 GitHub ID와 동일한 이름의 브랜치에 코드를 제출한다. - - main Branch로 Pull Request를 생성한다. + - GitHub을 활용한 제출 방법은 [우아한테크코스 프리코스 과제 제출](https://github.com/woowacourse/woowacourse-docs/tree/master/precourse) 문서를 참고해 제출한다. +- GitHub에 미션을 제출한 후 슬랙 `#미션-제출` 채널에 PR 주소를 올린다. + - **Pull Request만 보내고 Slack에 메시지를 보내지 않으면 리뷰를 하지 않으므로 주의한다.** -## ✔️ 과제 제출 전 체크리스트 +## 🚨 과제 제출 전 체크 리스트 -- 터미널에서 `java -version`을 실행해 자바 8인지 확인한다. 또는 Eclipse, IntelliJ IDEA와 같은 IDE의 자바 8로 실행하는지 확인한다. -- 터미널에서 맥 또는 리눅스 사용자의 경우 `./gradlew clean test`, 윈도우 사용자의 경우 `gradlew.bat clean test` 명령을 실행했을 때 모든 테스트가 아래와 같이 통과하는지 확인한다. +- 기능 구현을 모두 정상적으로 했더라도 **요구 사항에 명시된 출력값 형식을 지켜야한다.** +- 기능 구현을 완료한 뒤 아래 가이드에 따라 테스트를 실행했을 때 모든 테스트가 성공하는지 확인한다. + +### 테스트 실행 가이드 + +- 터미널에서 `java -version`을 실행하여 Java 버전이 11인지 확인한다. 또는 Eclipse 또는 IntelliJ IDEA와 같은 IDE에서 Java 11로 실행되는지 확인한다. +- 터미널에서 Mac 또는 Linux 사용자의 경우 `./gradlew clean test` 명령을 실행하고, + Windows 사용자의 경우 `gradlew.bat clean test` 명령을 실행할 때 모든 테스트가 아래와 같이 통과하는지 확인한다. ``` BUILD SUCCESSFUL in 0s @@ -110,10 +117,10 @@ BUILD SUCCESSFUL in 0s ## 🎱 프로그래밍 요구사항 -- 프로그램을 실행하는 시작점은 `Application`의 `main()`이다. -- JDK 8 버전에서 실행 가능해야 한다. **JDK 8에서 정상 동작하지 않을 경우 0점 처리**한다. -- 자바 코드 컨벤션을 지키면서 프로그래밍한다. - - https://naver.github.io/hackday-conventions-java +- JDK 11 버전에서 실행 가능해야 한다. +- 프로그램 실행의 시작점은 `Application`의 `main()`이다. +- `build.gradle` 파일을 변경할 수 없고, 외부 라이브러리를 사용하지 않는다. +- [Java 코드 컨벤션](https://github.com/woowacourse/woowacourse-docs/tree/master/styleguide/java) 가이드를 준수하며 프로그래밍한다. - indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다. - 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다. - 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메소드)를 분리하면 된다. @@ -152,13 +159,14 @@ public enum Coin { - JDK에서 기본 제공하는 Random, Scanner API 대신 `camp.nextstep.edu.missionutils`에서 제공하는 `Randoms`, `Console` API를 활용해 구현해야 한다. - Random 값 추출은 `camp.nextstep.edu.missionutils.Randoms`의 `pickNumberInList()`를 활용한다. - 사용자가 입력하는 값은 `camp.nextstep.edu.missionutils.Console`의 `readLine()`을 활용한다. -- 프로그램 구현을 완료했을 때 `src/test/java` 디렉터리의 `ApplicationTest`에 있는 모든 테스트 케이스가 성공해야 한다. **테스트가 실패할 경우 0점 처리한다.** +- 프로그램 구현을 완료했을 때 `src/test/java` 디렉터리의 `ApplicationTest`에 있는 모든 테스트 케이스가 성공해야 한다. --- -## 📈 과제 진행 요구사항 +## ✏️ 과제 진행 요구 사항 -- 미션은 [java-vendingmachine-precourse](https://github.com/woowacourse/java-vendingmachine-precourse) 저장소를 Fork/Clone해 시작한다. -- **기능을 구현하기 전에 java-vendingmachine-precourse/docs/README.md 파일에 구현할 기능 목록을 정리**해 추가한다. -- **Git의 커밋 단위는 앞 단계에서 README.md 파일에 정리한 기능 목록 단위**로 추가한다. - - [AngularJS Commit Message Conventions](https://gist.github.com/stephenparish/9941e89d80e2bc58a153) 참고해 commit log를 남긴다. +- 미션은 [java-vendingmachine](https://github.com/grow-up-study/java-vendingmachine) 저장소를 Fork & Clone해 시작한다. +- 과제 진행 및 제출 방법은 [우아한테크코스 프리코스 과제 제출](https://github.com/woowacourse/woowacourse-docs/tree/master/precourse) 문서를 참고한다. +- **기능을 구현하기 전 `docs/README.md`에 구현할 기능 목록을 정리**해 추가한다. +- **Git의 커밋 단위는 앞 단계에서 `docs/README.md`에 정리한 기능 목록 단위**로 추가한다. + - [커밋 메시지 컨벤션](https://gist.github.com/stephenparish/9941e89d80e2bc58a153) 가이드를 참고해 커밋 메시지를 작성한다. From 2fb1cc7c13272c8c8ccb9b0f28b23b870069fd27 Mon Sep 17 00:00:00 2001 From: HyeonsuLee Date: Sun, 1 Dec 2024 13:41:45 +0900 Subject: [PATCH 03/23] =?UTF-8?q?docs:=20=EA=B8=B0=EB=8A=A5=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/README.md b/README.md index 44ff8ba0c..4a87494ff 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,82 @@ BUILD SUCCESSFUL in 0s --- +## 🚀 기능 목록 +### 예외 +- 잘못된 금액 입력시 `[ERROR] 금액은 숫자여야 합니다.`예외 발생 +- 상품명, 가격, 수량입력 형식이 잘못된 경우 + - `[ERROR] {상품명, 가격, 수량} 입력 형식을 확인해주세요.` 예외 발생 + +### 금액 +- 금액은 숫자이다. +- 금액은 1원 단위가 될 수 없다.(ex 1001원) + +### 가격 +- 가격은 숫자이다. +- 가격은 100원 이상이고 10원으로 나누어 떨어져야 한다. + +### 수량 +- 수량은 0개보다 작을 수 없다. + +### 주문 +- 사용자가 구매하고자 하는 상품의 이름으로 주문한다. + +### 상품 +- 상품은 이름, 가격, 수량이 있다. +- 상품의 가격은 100원 이상이고, 10원으로 나누어 떨어져야 한다. + +### 잔돈 +- 잔돈은 동전으로 이루어져 있다. +- 제공된 Coin enum을 사용한다. + +### 자판기 +- 잔돈을 가지고 있다. +- 자판기에 상품을 채워넣을 수 있다. +- 사용자가 금액을 투입할 수 있다. +- 남은 금액이 상품의 최저 가격보다 적거나 모든 상품이 소진된 경우 바로 잔돈을 돌려준다. +- 잔돈을 반환할 수 없는 경우 잔돈으로 반환할 수 있는 금액만 반환한다. + +### 입력 +- 자판기가 보유하고 있는 금액을 입력한다. +```angular2html +자판기가 보유하고 있는 금액을 입력해 주세요. +450 +``` +- 상품명, 가격, 수량은 쉼표로, 개별 상품은 대괄호 []로 묶어서 세미콜론;으로 구분한다. +```angular2html +[콜라,1500,20];[사이다,1000,10] +``` +- 투입 금액을 입력한다. +```angular2html +투입 금액을 입력해 주세요. +3000 +``` +- 구매할 상품명을 입력한다. +```angular2html +구매할 상품명을 입력해 주세요. +사이다 +``` + +### 출력 +- 자판기가 보유한 동전을 출력한다. +```angular2html +자판기가 보유한 동전 +500원 - 0개 +100원 - 4개 +50원 - 1개 +10원 - 0개 +``` +- 투입 금액을 출력한다. +```angular2html +투입 금액: 3000원 +``` +- 잔돈을 출력한다. +```angular2html +잔돈 +100원 - 4개 +50원 - 1개 +``` + ## 🚀 기능 요구사항 반환되는 동전이 최소한이 되는 자판기를 구현한다. From f7acdff35045a7cdc8fefbc74cea1a57262a5c0e Mon Sep 17 00:00:00 2001 From: HyeonsuLee Date: Sun, 1 Dec 2024 13:42:24 +0900 Subject: [PATCH 04/23] =?UTF-8?q?feat:=20=EC=BB=A4=EC=8A=A4=ED=85=80=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/ExceptionMessage.java | 20 +++++++++++++++++++ .../IllegalArgumentBaseException.java | 8 ++++++++ .../exception/PriceNumberFormatException.java | 8 ++++++++ .../ProductNameNotBlankException.java | 8 ++++++++ .../ProductPriceFromatException.java | 8 ++++++++ .../ProductQuantityFormatException.java | 8 ++++++++ 6 files changed, 60 insertions(+) create mode 100644 src/main/java/vendingmachine/exception/ExceptionMessage.java create mode 100644 src/main/java/vendingmachine/exception/IllegalArgumentBaseException.java create mode 100644 src/main/java/vendingmachine/exception/PriceNumberFormatException.java create mode 100644 src/main/java/vendingmachine/exception/ProductNameNotBlankException.java create mode 100644 src/main/java/vendingmachine/exception/ProductPriceFromatException.java create mode 100644 src/main/java/vendingmachine/exception/ProductQuantityFormatException.java diff --git a/src/main/java/vendingmachine/exception/ExceptionMessage.java b/src/main/java/vendingmachine/exception/ExceptionMessage.java new file mode 100644 index 000000000..d581f3f6e --- /dev/null +++ b/src/main/java/vendingmachine/exception/ExceptionMessage.java @@ -0,0 +1,20 @@ +package vendingmachine.exception; + +public enum ExceptionMessage { + + PRICE_NUMBER_FORMAT("금액은 숫자여야 합니다."), + PRODUCT_PRICE_FORMAT("상품 가격은 100원 이상 10으로 나누어 떨어져야 합니다."), + PRODUCT_QUANTITY_FORMAT("상품의 개수는 0개 이상이여야 합니다."), + PRODUCT_NAME_NOT_BLANK("상품의 이름은 공백일 수 없습니다."), + ; + + private final String message; + + ExceptionMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } +} diff --git a/src/main/java/vendingmachine/exception/IllegalArgumentBaseException.java b/src/main/java/vendingmachine/exception/IllegalArgumentBaseException.java new file mode 100644 index 000000000..50563462b --- /dev/null +++ b/src/main/java/vendingmachine/exception/IllegalArgumentBaseException.java @@ -0,0 +1,8 @@ +package vendingmachine.exception; + +public class IllegalArgumentBaseException extends IllegalArgumentException { + + protected IllegalArgumentBaseException(ExceptionMessage exceptionMessage) { + super(String.format("[ERROR] %s", exceptionMessage.getMessage())); + } +} diff --git a/src/main/java/vendingmachine/exception/PriceNumberFormatException.java b/src/main/java/vendingmachine/exception/PriceNumberFormatException.java new file mode 100644 index 000000000..5ed47f26f --- /dev/null +++ b/src/main/java/vendingmachine/exception/PriceNumberFormatException.java @@ -0,0 +1,8 @@ +package vendingmachine.exception; + +public class PriceNumberFormatException extends IllegalArgumentException { + + protected PriceNumberFormatException(ExceptionMessage exceptionMessage) { + super(ExceptionMessage.PRICE_NUMBER_FORMAT.getMessage()); + } +} diff --git a/src/main/java/vendingmachine/exception/ProductNameNotBlankException.java b/src/main/java/vendingmachine/exception/ProductNameNotBlankException.java new file mode 100644 index 000000000..1a2fa7949 --- /dev/null +++ b/src/main/java/vendingmachine/exception/ProductNameNotBlankException.java @@ -0,0 +1,8 @@ +package vendingmachine.exception; + +public class ProductNameNotBlankException extends IllegalArgumentException { + + protected ProductNameNotBlankException(ExceptionMessage exceptionMessage) { + super(ExceptionMessage.PRODUCT_NAME_NOT_BLANK.getMessage()); + } +} diff --git a/src/main/java/vendingmachine/exception/ProductPriceFromatException.java b/src/main/java/vendingmachine/exception/ProductPriceFromatException.java new file mode 100644 index 000000000..50ca59eec --- /dev/null +++ b/src/main/java/vendingmachine/exception/ProductPriceFromatException.java @@ -0,0 +1,8 @@ +package vendingmachine.exception; + +public class ProductPriceFromatException extends IllegalArgumentException { + + protected ProductPriceFromatException(ExceptionMessage exceptionMessage) { + super(ExceptionMessage.PRODUCT_PRICE_FORMAT.getMessage()); + } +} diff --git a/src/main/java/vendingmachine/exception/ProductQuantityFormatException.java b/src/main/java/vendingmachine/exception/ProductQuantityFormatException.java new file mode 100644 index 000000000..a46cf37dd --- /dev/null +++ b/src/main/java/vendingmachine/exception/ProductQuantityFormatException.java @@ -0,0 +1,8 @@ +package vendingmachine.exception; + +public class ProductQuantityFormatException extends IllegalArgumentException { + + protected ProductQuantityFormatException(ExceptionMessage exceptionMessage) { + super(ExceptionMessage.PRODUCT_QUANTITY_FORMAT.getMessage()); + } +} From 83f2a76f3a5a275d76b97a6b3189b7840efa488b Mon Sep 17 00:00:00 2001 From: HyeonsuLee Date: Sun, 1 Dec 2024 13:45:58 +0900 Subject: [PATCH 05/23] =?UTF-8?q?feat:=20=EA=B8=88=EC=95=A1=20=ED=98=95?= =?UTF-8?q?=EC=8B=9D=20=EC=98=88=EC=99=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/AmountNumberFormatException.java | 8 ++++++++ .../java/vendingmachine/exception/ExceptionMessage.java | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 src/main/java/vendingmachine/exception/AmountNumberFormatException.java diff --git a/src/main/java/vendingmachine/exception/AmountNumberFormatException.java b/src/main/java/vendingmachine/exception/AmountNumberFormatException.java new file mode 100644 index 000000000..3691f9b69 --- /dev/null +++ b/src/main/java/vendingmachine/exception/AmountNumberFormatException.java @@ -0,0 +1,8 @@ +package vendingmachine.exception; + +public class AmountNumberFormatException extends IllegalArgumentException { + + protected AmountNumberFormatException(ExceptionMessage exceptionMessage) { + super(ExceptionMessage.AMOUNT_NUMBER_FORMAT.getMessage()); + } +} diff --git a/src/main/java/vendingmachine/exception/ExceptionMessage.java b/src/main/java/vendingmachine/exception/ExceptionMessage.java index d581f3f6e..495a62633 100644 --- a/src/main/java/vendingmachine/exception/ExceptionMessage.java +++ b/src/main/java/vendingmachine/exception/ExceptionMessage.java @@ -2,7 +2,8 @@ public enum ExceptionMessage { - PRICE_NUMBER_FORMAT("금액은 숫자여야 합니다."), + PRICE_NUMBER_FORMAT("가격은 숫자여야 합니다."), + AMOUNT_NUMBER_FORMAT("금액은 숫자여야 합니다."), PRODUCT_PRICE_FORMAT("상품 가격은 100원 이상 10으로 나누어 떨어져야 합니다."), PRODUCT_QUANTITY_FORMAT("상품의 개수는 0개 이상이여야 합니다."), PRODUCT_NAME_NOT_BLANK("상품의 이름은 공백일 수 없습니다."), From 25fd7989bde5b91e1bc3ccb669391c52ffd84055 Mon Sep 17 00:00:00 2001 From: HyeonsuLee Date: Sun, 1 Dec 2024 13:47:20 +0900 Subject: [PATCH 06/23] =?UTF-8?q?refactor:=20=EC=98=88=EC=99=B8=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=EC=9E=90=20=EC=9D=B8=EC=9E=90=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vendingmachine/exception/AmountNumberFormatException.java | 2 +- .../vendingmachine/exception/PriceNumberFormatException.java | 2 +- .../vendingmachine/exception/ProductNameNotBlankException.java | 2 +- .../vendingmachine/exception/ProductPriceFromatException.java | 2 +- .../exception/ProductQuantityFormatException.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/vendingmachine/exception/AmountNumberFormatException.java b/src/main/java/vendingmachine/exception/AmountNumberFormatException.java index 3691f9b69..51dc19d98 100644 --- a/src/main/java/vendingmachine/exception/AmountNumberFormatException.java +++ b/src/main/java/vendingmachine/exception/AmountNumberFormatException.java @@ -2,7 +2,7 @@ public class AmountNumberFormatException extends IllegalArgumentException { - protected AmountNumberFormatException(ExceptionMessage exceptionMessage) { + protected AmountNumberFormatException() { super(ExceptionMessage.AMOUNT_NUMBER_FORMAT.getMessage()); } } diff --git a/src/main/java/vendingmachine/exception/PriceNumberFormatException.java b/src/main/java/vendingmachine/exception/PriceNumberFormatException.java index 5ed47f26f..cb03558a1 100644 --- a/src/main/java/vendingmachine/exception/PriceNumberFormatException.java +++ b/src/main/java/vendingmachine/exception/PriceNumberFormatException.java @@ -2,7 +2,7 @@ public class PriceNumberFormatException extends IllegalArgumentException { - protected PriceNumberFormatException(ExceptionMessage exceptionMessage) { + protected PriceNumberFormatException() { super(ExceptionMessage.PRICE_NUMBER_FORMAT.getMessage()); } } diff --git a/src/main/java/vendingmachine/exception/ProductNameNotBlankException.java b/src/main/java/vendingmachine/exception/ProductNameNotBlankException.java index 1a2fa7949..e1dec4cc1 100644 --- a/src/main/java/vendingmachine/exception/ProductNameNotBlankException.java +++ b/src/main/java/vendingmachine/exception/ProductNameNotBlankException.java @@ -2,7 +2,7 @@ public class ProductNameNotBlankException extends IllegalArgumentException { - protected ProductNameNotBlankException(ExceptionMessage exceptionMessage) { + protected ProductNameNotBlankException() { super(ExceptionMessage.PRODUCT_NAME_NOT_BLANK.getMessage()); } } diff --git a/src/main/java/vendingmachine/exception/ProductPriceFromatException.java b/src/main/java/vendingmachine/exception/ProductPriceFromatException.java index 50ca59eec..3a79a60dd 100644 --- a/src/main/java/vendingmachine/exception/ProductPriceFromatException.java +++ b/src/main/java/vendingmachine/exception/ProductPriceFromatException.java @@ -2,7 +2,7 @@ public class ProductPriceFromatException extends IllegalArgumentException { - protected ProductPriceFromatException(ExceptionMessage exceptionMessage) { + protected ProductPriceFromatException() { super(ExceptionMessage.PRODUCT_PRICE_FORMAT.getMessage()); } } diff --git a/src/main/java/vendingmachine/exception/ProductQuantityFormatException.java b/src/main/java/vendingmachine/exception/ProductQuantityFormatException.java index a46cf37dd..d18e3a738 100644 --- a/src/main/java/vendingmachine/exception/ProductQuantityFormatException.java +++ b/src/main/java/vendingmachine/exception/ProductQuantityFormatException.java @@ -2,7 +2,7 @@ public class ProductQuantityFormatException extends IllegalArgumentException { - protected ProductQuantityFormatException(ExceptionMessage exceptionMessage) { + protected ProductQuantityFormatException() { super(ExceptionMessage.PRODUCT_QUANTITY_FORMAT.getMessage()); } } From 21f32905c7f6a96d9c699aed5ca0e9220dcc2d78 Mon Sep 17 00:00:00 2001 From: HyeonsuLee Date: Sun, 1 Dec 2024 13:48:09 +0900 Subject: [PATCH 07/23] =?UTF-8?q?refactor:=20=EC=BB=A4=EC=8A=A4=ED=85=80?= =?UTF-8?q?=20=EC=98=88=EC=99=B8=20=EC=83=9D=EC=84=B1=EC=9E=90=20=EC=A0=91?= =?UTF-8?q?=EA=B7=BC=EC=A0=9C=EC=96=B4=20public=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vendingmachine/exception/AmountNumberFormatException.java | 2 +- .../vendingmachine/exception/PriceNumberFormatException.java | 2 +- .../vendingmachine/exception/ProductNameNotBlankException.java | 2 +- .../vendingmachine/exception/ProductPriceFromatException.java | 2 +- .../exception/ProductQuantityFormatException.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/vendingmachine/exception/AmountNumberFormatException.java b/src/main/java/vendingmachine/exception/AmountNumberFormatException.java index 51dc19d98..a441efd5d 100644 --- a/src/main/java/vendingmachine/exception/AmountNumberFormatException.java +++ b/src/main/java/vendingmachine/exception/AmountNumberFormatException.java @@ -2,7 +2,7 @@ public class AmountNumberFormatException extends IllegalArgumentException { - protected AmountNumberFormatException() { + public AmountNumberFormatException() { super(ExceptionMessage.AMOUNT_NUMBER_FORMAT.getMessage()); } } diff --git a/src/main/java/vendingmachine/exception/PriceNumberFormatException.java b/src/main/java/vendingmachine/exception/PriceNumberFormatException.java index cb03558a1..1cfda6721 100644 --- a/src/main/java/vendingmachine/exception/PriceNumberFormatException.java +++ b/src/main/java/vendingmachine/exception/PriceNumberFormatException.java @@ -2,7 +2,7 @@ public class PriceNumberFormatException extends IllegalArgumentException { - protected PriceNumberFormatException() { + public PriceNumberFormatException() { super(ExceptionMessage.PRICE_NUMBER_FORMAT.getMessage()); } } diff --git a/src/main/java/vendingmachine/exception/ProductNameNotBlankException.java b/src/main/java/vendingmachine/exception/ProductNameNotBlankException.java index e1dec4cc1..edfe34381 100644 --- a/src/main/java/vendingmachine/exception/ProductNameNotBlankException.java +++ b/src/main/java/vendingmachine/exception/ProductNameNotBlankException.java @@ -2,7 +2,7 @@ public class ProductNameNotBlankException extends IllegalArgumentException { - protected ProductNameNotBlankException() { + public ProductNameNotBlankException() { super(ExceptionMessage.PRODUCT_NAME_NOT_BLANK.getMessage()); } } diff --git a/src/main/java/vendingmachine/exception/ProductPriceFromatException.java b/src/main/java/vendingmachine/exception/ProductPriceFromatException.java index 3a79a60dd..ce3e7cedf 100644 --- a/src/main/java/vendingmachine/exception/ProductPriceFromatException.java +++ b/src/main/java/vendingmachine/exception/ProductPriceFromatException.java @@ -2,7 +2,7 @@ public class ProductPriceFromatException extends IllegalArgumentException { - protected ProductPriceFromatException() { + public ProductPriceFromatException() { super(ExceptionMessage.PRODUCT_PRICE_FORMAT.getMessage()); } } diff --git a/src/main/java/vendingmachine/exception/ProductQuantityFormatException.java b/src/main/java/vendingmachine/exception/ProductQuantityFormatException.java index d18e3a738..e2ec71b81 100644 --- a/src/main/java/vendingmachine/exception/ProductQuantityFormatException.java +++ b/src/main/java/vendingmachine/exception/ProductQuantityFormatException.java @@ -2,7 +2,7 @@ public class ProductQuantityFormatException extends IllegalArgumentException { - protected ProductQuantityFormatException() { + public ProductQuantityFormatException() { super(ExceptionMessage.PRODUCT_QUANTITY_FORMAT.getMessage()); } } From 020c67f8da4a59a7de1fee82f530bd61a7be9017 Mon Sep 17 00:00:00 2001 From: HyeonsuLee Date: Sun, 1 Dec 2024 13:58:39 +0900 Subject: [PATCH 08/23] =?UTF-8?q?feat:=20=EA=B8=88=EC=95=A1=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vendingmachine/exception/AmountFormatException.java | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/main/java/vendingmachine/exception/AmountFormatException.java diff --git a/src/main/java/vendingmachine/exception/AmountFormatException.java b/src/main/java/vendingmachine/exception/AmountFormatException.java new file mode 100644 index 000000000..3e13bec3d --- /dev/null +++ b/src/main/java/vendingmachine/exception/AmountFormatException.java @@ -0,0 +1,8 @@ +package vendingmachine.exception; + +public class AmountFormatException extends IllegalArgumentException { + + public AmountFormatException() { + super(ExceptionMessage.AMOUNT_FORMAT.getMessage()); + } +} From d1e13780e03e6556112f0ceea33ad8862d3eef0c Mon Sep 17 00:00:00 2001 From: HyeonsuLee Date: Sun, 1 Dec 2024 13:58:58 +0900 Subject: [PATCH 09/23] =?UTF-8?q?feat:=20=EA=B8=88=EC=95=A1=20Model=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/vendingmachine/model/Amount.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/main/java/vendingmachine/model/Amount.java diff --git a/src/main/java/vendingmachine/model/Amount.java b/src/main/java/vendingmachine/model/Amount.java new file mode 100644 index 000000000..7f5d57390 --- /dev/null +++ b/src/main/java/vendingmachine/model/Amount.java @@ -0,0 +1,19 @@ +package vendingmachine.model; + +import vendingmachine.exception.AmountFormatException; +import vendingmachine.exception.AmountNumberFormatException; + +public class Amount { + private final int amount; + + public Amount(int amount) { + validateAmount(amount); + this.amount = amount; + } + + private void validateAmount(int amount) { + if (amount % 10 != 0) { + throw new AmountFormatException(); + } + } +} From 2732ceb27cf0933e57a6c3b9d50549f5c5922b3a Mon Sep 17 00:00:00 2001 From: HyeonsuLee Date: Sun, 1 Dec 2024 13:59:09 +0900 Subject: [PATCH 10/23] =?UTF-8?q?feat:=20=EA=B0=80=EA=B2=A9=20Model=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/vendingmachine/model/Price.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/main/java/vendingmachine/model/Price.java diff --git a/src/main/java/vendingmachine/model/Price.java b/src/main/java/vendingmachine/model/Price.java new file mode 100644 index 000000000..60d66c27c --- /dev/null +++ b/src/main/java/vendingmachine/model/Price.java @@ -0,0 +1,18 @@ +package vendingmachine.model; + +import vendingmachine.exception.ProductPriceFromatException; + +public class Price { + private final int price; + + public Price(int price) { + validateAmount(price); + this.price = price; + } + + private void validateAmount(int price) { + if (price < 100 || price % 10 != 0) { + throw new ProductPriceFromatException(); + } + } +} From 333488036e631d430c5623ecd55c999fdb54fd10 Mon Sep 17 00:00:00 2001 From: HyeonsuLee Date: Sun, 1 Dec 2024 13:59:19 +0900 Subject: [PATCH 11/23] =?UTF-8?q?feat:=20=EC=88=98=EB=9F=89=20Model=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/vendingmachine/model/Quantity.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/main/java/vendingmachine/model/Quantity.java diff --git a/src/main/java/vendingmachine/model/Quantity.java b/src/main/java/vendingmachine/model/Quantity.java new file mode 100644 index 000000000..586a2e0f1 --- /dev/null +++ b/src/main/java/vendingmachine/model/Quantity.java @@ -0,0 +1,18 @@ +package vendingmachine.model; + +import vendingmachine.exception.ProductQuantityFormatException; + +public class Quantity { + private final int quantity; + + public Quantity(int quantity) { + validateQuantity(quantity); + this.quantity = quantity; + } + +private void validateQuantity(int quantity) { + if (quantity < 0) { + throw new ProductQuantityFormatException(); + } + } +} From eb9898f8699e38007e754314b3610c39b56f1fc6 Mon Sep 17 00:00:00 2001 From: HyeonsuLee Date: Sun, 1 Dec 2024 13:59:27 +0900 Subject: [PATCH 12/23] =?UTF-8?q?feat:=20=EC=A3=BC=EB=AC=B8=20Model=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/vendingmachine/model/Order.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/java/vendingmachine/model/Order.java diff --git a/src/main/java/vendingmachine/model/Order.java b/src/main/java/vendingmachine/model/Order.java new file mode 100644 index 000000000..ccbc9ef96 --- /dev/null +++ b/src/main/java/vendingmachine/model/Order.java @@ -0,0 +1,9 @@ +package vendingmachine.model; + +public class Order { + private final Name productName; + + public Order(Name productName) { + this.productName = productName; + } +} From e23b4f940c0a65b7393051e25beeae98ef0bd64a Mon Sep 17 00:00:00 2001 From: HyeonsuLee Date: Sun, 1 Dec 2024 13:59:54 +0900 Subject: [PATCH 13/23] =?UTF-8?q?feat:=20=EC=83=81=ED=92=88=20Model=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/vendingmachine/model/Product.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/main/java/vendingmachine/model/Product.java diff --git a/src/main/java/vendingmachine/model/Product.java b/src/main/java/vendingmachine/model/Product.java new file mode 100644 index 000000000..e202ec105 --- /dev/null +++ b/src/main/java/vendingmachine/model/Product.java @@ -0,0 +1,14 @@ +package vendingmachine.model; + +public class Product { + private final Name name; + private final Price price; + private final Quantity quantity; + + public Product(Name name, Price price, Quantity quantity) { + this.name = name; + this.price = price; + this.quantity = quantity; + } + +} From 0bc1d826c61941a69ec865876511d452dad76c1d Mon Sep 17 00:00:00 2001 From: HyeonsuLee Date: Sun, 1 Dec 2024 14:00:23 +0900 Subject: [PATCH 14/23] =?UTF-8?q?feat:=20=EC=83=81=ED=92=88=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20Model=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/vendingmachine/model/Name.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/main/java/vendingmachine/model/Name.java diff --git a/src/main/java/vendingmachine/model/Name.java b/src/main/java/vendingmachine/model/Name.java new file mode 100644 index 000000000..5f2036b78 --- /dev/null +++ b/src/main/java/vendingmachine/model/Name.java @@ -0,0 +1,18 @@ +package vendingmachine.model; + +import vendingmachine.exception.ProductNameNotBlankException; + +public class Name { + private final String name; + + public Name(String name) { + validateName(name); + this.name = name; + } + + private void validateName(String name) { + if (name.replace(" ","").isEmpty()) { + throw new ProductNameNotBlankException(); + } + } +} From 6f2659a560251f0d1082807cba856938d73e167b Mon Sep 17 00:00:00 2001 From: HyeonsuLee Date: Sun, 1 Dec 2024 14:10:49 +0900 Subject: [PATCH 15/23] =?UTF-8?q?feat:=20=EC=9E=94=EB=8F=88=20Model=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/vendingmachine/model/Change.java | 17 +++++++++++++++++ .../java/vendingmachine/{ => model}/Coin.java | 6 +++++- 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 src/main/java/vendingmachine/model/Change.java rename src/main/java/vendingmachine/{ => model}/Coin.java (71%) diff --git a/src/main/java/vendingmachine/model/Change.java b/src/main/java/vendingmachine/model/Change.java new file mode 100644 index 000000000..290b6d5bb --- /dev/null +++ b/src/main/java/vendingmachine/model/Change.java @@ -0,0 +1,17 @@ +package vendingmachine.model; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class Change { + private final Map changeMoney = new LinkedHashMap<>(); + + public Change(int money) { + int remainMoney = money; + for (Coin coin : Coin.values()) { + int coinCount = remainMoney % coin.getAmount(); + changeMoney.put(coin, coinCount); + remainMoney -= coin.getAmount() * coinCount; + } + } +} diff --git a/src/main/java/vendingmachine/Coin.java b/src/main/java/vendingmachine/model/Coin.java similarity index 71% rename from src/main/java/vendingmachine/Coin.java rename to src/main/java/vendingmachine/model/Coin.java index c76293fbc..e5d161ab5 100644 --- a/src/main/java/vendingmachine/Coin.java +++ b/src/main/java/vendingmachine/model/Coin.java @@ -1,4 +1,4 @@ -package vendingmachine; +package vendingmachine.model; public enum Coin { COIN_500(500), @@ -12,5 +12,9 @@ public enum Coin { this.amount = amount; } + public int getAmount() { + return amount; + } + // 추가 기능 구현 } From 4c824c751814222ecd05203bb2aeaba63903cc28 Mon Sep 17 00:00:00 2001 From: HyeonsuLee Date: Sun, 1 Dec 2024 14:45:42 +0900 Subject: [PATCH 16/23] =?UTF-8?q?feat:=20=EC=9E=94=EB=8F=88=20=EB=B0=98?= =?UTF-8?q?=ED=99=98=20=EA=B8=B0=EB=8A=A5=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/AmountNotEnoughException.java | 8 ++++ .../ProductNameNotFoundException.java | 8 ++++ .../java/vendingmachine/model/Amount.java | 11 ++++- .../java/vendingmachine/model/Change.java | 7 ++++ src/main/java/vendingmachine/model/Order.java | 4 ++ src/main/java/vendingmachine/model/Price.java | 4 ++ .../java/vendingmachine/model/Product.java | 13 ++++++ .../java/vendingmachine/model/Products.java | 35 ++++++++++++++++ .../vendingmachine/model/VendingMachine.java | 41 +++++++++++++++++++ 9 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 src/main/java/vendingmachine/exception/AmountNotEnoughException.java create mode 100644 src/main/java/vendingmachine/exception/ProductNameNotFoundException.java create mode 100644 src/main/java/vendingmachine/model/Products.java create mode 100644 src/main/java/vendingmachine/model/VendingMachine.java diff --git a/src/main/java/vendingmachine/exception/AmountNotEnoughException.java b/src/main/java/vendingmachine/exception/AmountNotEnoughException.java new file mode 100644 index 000000000..1f08e1f89 --- /dev/null +++ b/src/main/java/vendingmachine/exception/AmountNotEnoughException.java @@ -0,0 +1,8 @@ +package vendingmachine.exception; + +public class AmountNotEnoughException extends IllegalArgumentException { + + public AmountNotEnoughException() { + super(ExceptionMessage.AMOUNT_NOT_ENOUGH.getMessage()); + } +} diff --git a/src/main/java/vendingmachine/exception/ProductNameNotFoundException.java b/src/main/java/vendingmachine/exception/ProductNameNotFoundException.java new file mode 100644 index 000000000..91520a8e2 --- /dev/null +++ b/src/main/java/vendingmachine/exception/ProductNameNotFoundException.java @@ -0,0 +1,8 @@ +package vendingmachine.exception; + +public class ProductNameNotFoundException extends IllegalArgumentException { + + public ProductNameNotFoundException() { + super(ExceptionMessage.PRODUCT_NAME_NOT_FOUND.getMessage()); + } +} diff --git a/src/main/java/vendingmachine/model/Amount.java b/src/main/java/vendingmachine/model/Amount.java index 7f5d57390..9aa9ea4e1 100644 --- a/src/main/java/vendingmachine/model/Amount.java +++ b/src/main/java/vendingmachine/model/Amount.java @@ -1,10 +1,9 @@ package vendingmachine.model; import vendingmachine.exception.AmountFormatException; -import vendingmachine.exception.AmountNumberFormatException; public class Amount { - private final int amount; + private int amount; public Amount(int amount) { validateAmount(amount); @@ -16,4 +15,12 @@ private void validateAmount(int amount) { throw new AmountFormatException(); } } + + public int getAmount() { + return amount; + } + + public void decrease(int amount) { + this.amount -= amount; + } } diff --git a/src/main/java/vendingmachine/model/Change.java b/src/main/java/vendingmachine/model/Change.java index 290b6d5bb..f5f103180 100644 --- a/src/main/java/vendingmachine/model/Change.java +++ b/src/main/java/vendingmachine/model/Change.java @@ -14,4 +14,11 @@ public Change(int money) { remainMoney -= coin.getAmount() * coinCount; } } + + public int getTotalChange() { + return changeMoney.entrySet() + .stream() + .mapToInt(changeEntry -> changeEntry.getKey().getAmount() * changeEntry.getValue()) + .sum(); + } } diff --git a/src/main/java/vendingmachine/model/Order.java b/src/main/java/vendingmachine/model/Order.java index ccbc9ef96..dddedc66d 100644 --- a/src/main/java/vendingmachine/model/Order.java +++ b/src/main/java/vendingmachine/model/Order.java @@ -6,4 +6,8 @@ public class Order { public Order(Name productName) { this.productName = productName; } + + public Name name() { + return new Name(productName.getName()); + } } diff --git a/src/main/java/vendingmachine/model/Price.java b/src/main/java/vendingmachine/model/Price.java index 60d66c27c..3e3135abb 100644 --- a/src/main/java/vendingmachine/model/Price.java +++ b/src/main/java/vendingmachine/model/Price.java @@ -15,4 +15,8 @@ private void validateAmount(int price) { throw new ProductPriceFromatException(); } } + + public int getPrice() { + return price; + } } diff --git a/src/main/java/vendingmachine/model/Product.java b/src/main/java/vendingmachine/model/Product.java index e202ec105..6531abb02 100644 --- a/src/main/java/vendingmachine/model/Product.java +++ b/src/main/java/vendingmachine/model/Product.java @@ -1,5 +1,7 @@ package vendingmachine.model; +import vendingmachine.exception.AmountNotEnoughException; + public class Product { private final Name name; private final Price price; @@ -11,4 +13,15 @@ public Product(Name name, Price price, Quantity quantity) { this.quantity = quantity; } + public Name getName() { + return name; + } + + public boolean isBuyable(Amount amount) { + return price.getPrice() <= amount.getAmount(); + } + + public int getPrice() { + return price.getPrice(); + } } diff --git a/src/main/java/vendingmachine/model/Products.java b/src/main/java/vendingmachine/model/Products.java new file mode 100644 index 000000000..eada6769e --- /dev/null +++ b/src/main/java/vendingmachine/model/Products.java @@ -0,0 +1,35 @@ +package vendingmachine.model; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.function.Consumer; + +public class Products implements Iterable { + private final List products; + + public Products(List products) { + this.products = new ArrayList<>(products); + } + + public Optional find(Name name) { + return products.stream().filter(product -> product.getName().equals(name)).findFirst(); + } + + @Override + public Iterator iterator() { + return products.iterator(); + } + + @Override + public void forEach(Consumer action) { + Iterable.super.forEach(action); + } + + public int findMinPrice() { + return products.stream() + .mapToInt(Product::getPrice) + .min().getAsInt(); + } +} diff --git a/src/main/java/vendingmachine/model/VendingMachine.java b/src/main/java/vendingmachine/model/VendingMachine.java new file mode 100644 index 000000000..d7c8d34a4 --- /dev/null +++ b/src/main/java/vendingmachine/model/VendingMachine.java @@ -0,0 +1,41 @@ +package vendingmachine.model; + +import vendingmachine.exception.AmountNotEnoughException; +import vendingmachine.exception.ProductNameNotFoundException; + +public class VendingMachine { + private final Change change; + private final Products products; + private final Amount amount; + + public VendingMachine(Change change, Products products, Amount amount) { + this.change = change; + this.products = products; + this.amount = amount; + } + + public Change getChange() { + if (change.getTotalChange() <= amount.getAmount()) { + return change; + } + return new Change(amount.getAmount()); + } + + public boolean isBuyableAnyProduct() { + return products.findMinPrice() <= amount.getAmount(); + } + + public void buy(Order order) { + Product product = validateOrder(order); + amount.decrease(product.getPrice()); + + } + + private Product validateOrder(Order order) { + Product product = products.find(order.name()).orElseThrow(ProductNameNotFoundException::new); + if (!product.isBuyable(amount)) { + throw new AmountNotEnoughException(); + } + return product; + } +} From 483be2d25cfff6bd4ad94ff713182f76639b04f9 Mon Sep 17 00:00:00 2001 From: HyeonsuLee Date: Sun, 1 Dec 2024 16:25:09 +0900 Subject: [PATCH 17/23] =?UTF-8?q?refactor:=20base=EC=98=88=EC=99=B8?= =?UTF-8?q?=EB=A5=BC=20=EC=83=81=EC=86=8D=EB=B0=9B=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vendingmachine/exception/AmountFormatException.java | 4 ++-- .../exception/AmountNotEnoughException.java | 4 ++-- .../exception/AmountNumberFormatException.java | 4 ++-- .../java/vendingmachine/exception/ExceptionMessage.java | 5 +++++ .../exception/PriceNumberFormatException.java | 4 ++-- .../vendingmachine/exception/ProductFormatException.java | 8 ++++++++ .../exception/ProductNameNotBlankException.java | 4 ++-- .../exception/ProductNameNotFoundException.java | 4 ++-- .../exception/ProductPriceFromatException.java | 4 ++-- .../exception/ProductQuantityFormatException.java | 4 ++-- .../exception/QuantityNumberFormatException.java | 8 ++++++++ 11 files changed, 37 insertions(+), 16 deletions(-) create mode 100644 src/main/java/vendingmachine/exception/ProductFormatException.java create mode 100644 src/main/java/vendingmachine/exception/QuantityNumberFormatException.java diff --git a/src/main/java/vendingmachine/exception/AmountFormatException.java b/src/main/java/vendingmachine/exception/AmountFormatException.java index 3e13bec3d..8f2d0551a 100644 --- a/src/main/java/vendingmachine/exception/AmountFormatException.java +++ b/src/main/java/vendingmachine/exception/AmountFormatException.java @@ -1,8 +1,8 @@ package vendingmachine.exception; -public class AmountFormatException extends IllegalArgumentException { +public class AmountFormatException extends IllegalArgumentBaseException { public AmountFormatException() { - super(ExceptionMessage.AMOUNT_FORMAT.getMessage()); + super(ExceptionMessage.AMOUNT_FORMAT); } } diff --git a/src/main/java/vendingmachine/exception/AmountNotEnoughException.java b/src/main/java/vendingmachine/exception/AmountNotEnoughException.java index 1f08e1f89..3f3b86d51 100644 --- a/src/main/java/vendingmachine/exception/AmountNotEnoughException.java +++ b/src/main/java/vendingmachine/exception/AmountNotEnoughException.java @@ -1,8 +1,8 @@ package vendingmachine.exception; -public class AmountNotEnoughException extends IllegalArgumentException { +public class AmountNotEnoughException extends IllegalArgumentBaseException { public AmountNotEnoughException() { - super(ExceptionMessage.AMOUNT_NOT_ENOUGH.getMessage()); + super(ExceptionMessage.AMOUNT_NOT_ENOUGH); } } diff --git a/src/main/java/vendingmachine/exception/AmountNumberFormatException.java b/src/main/java/vendingmachine/exception/AmountNumberFormatException.java index a441efd5d..d29f17d89 100644 --- a/src/main/java/vendingmachine/exception/AmountNumberFormatException.java +++ b/src/main/java/vendingmachine/exception/AmountNumberFormatException.java @@ -1,8 +1,8 @@ package vendingmachine.exception; -public class AmountNumberFormatException extends IllegalArgumentException { +public class AmountNumberFormatException extends IllegalArgumentBaseException { public AmountNumberFormatException() { - super(ExceptionMessage.AMOUNT_NUMBER_FORMAT.getMessage()); + super(ExceptionMessage.AMOUNT_NUMBER_FORMAT); } } diff --git a/src/main/java/vendingmachine/exception/ExceptionMessage.java b/src/main/java/vendingmachine/exception/ExceptionMessage.java index 495a62633..0850e30dd 100644 --- a/src/main/java/vendingmachine/exception/ExceptionMessage.java +++ b/src/main/java/vendingmachine/exception/ExceptionMessage.java @@ -4,9 +4,14 @@ public enum ExceptionMessage { PRICE_NUMBER_FORMAT("가격은 숫자여야 합니다."), AMOUNT_NUMBER_FORMAT("금액은 숫자여야 합니다."), + QUANTITY_NUMBER_FORMAT("수량은 숫자여야 합니다."), + AMOUNT_FORMAT("금액은 10으로 나누어 떨어져야 합니다."), PRODUCT_PRICE_FORMAT("상품 가격은 100원 이상 10으로 나누어 떨어져야 합니다."), + PRODUCT_FORMAT("상품 입력 형식을 확인해주세요."), PRODUCT_QUANTITY_FORMAT("상품의 개수는 0개 이상이여야 합니다."), PRODUCT_NAME_NOT_BLANK("상품의 이름은 공백일 수 없습니다."), + PRODUCT_NAME_NOT_FOUND("존재하지 않는 상품입니다."), + AMOUNT_NOT_ENOUGH("금액이 부족합니다."), ; private final String message; diff --git a/src/main/java/vendingmachine/exception/PriceNumberFormatException.java b/src/main/java/vendingmachine/exception/PriceNumberFormatException.java index 1cfda6721..53c10dab1 100644 --- a/src/main/java/vendingmachine/exception/PriceNumberFormatException.java +++ b/src/main/java/vendingmachine/exception/PriceNumberFormatException.java @@ -1,8 +1,8 @@ package vendingmachine.exception; -public class PriceNumberFormatException extends IllegalArgumentException { +public class PriceNumberFormatException extends IllegalArgumentBaseException { public PriceNumberFormatException() { - super(ExceptionMessage.PRICE_NUMBER_FORMAT.getMessage()); + super(ExceptionMessage.PRICE_NUMBER_FORMAT); } } diff --git a/src/main/java/vendingmachine/exception/ProductFormatException.java b/src/main/java/vendingmachine/exception/ProductFormatException.java new file mode 100644 index 000000000..d31bc1279 --- /dev/null +++ b/src/main/java/vendingmachine/exception/ProductFormatException.java @@ -0,0 +1,8 @@ +package vendingmachine.exception; + +public class ProductFormatException extends IllegalArgumentException { + + public ProductFormatException() { + super(ExceptionMessage.PRODUCT_FORMAT.getMessage()); + } +} diff --git a/src/main/java/vendingmachine/exception/ProductNameNotBlankException.java b/src/main/java/vendingmachine/exception/ProductNameNotBlankException.java index edfe34381..90c9ccbe8 100644 --- a/src/main/java/vendingmachine/exception/ProductNameNotBlankException.java +++ b/src/main/java/vendingmachine/exception/ProductNameNotBlankException.java @@ -1,8 +1,8 @@ package vendingmachine.exception; -public class ProductNameNotBlankException extends IllegalArgumentException { +public class ProductNameNotBlankException extends IllegalArgumentBaseException { public ProductNameNotBlankException() { - super(ExceptionMessage.PRODUCT_NAME_NOT_BLANK.getMessage()); + super(ExceptionMessage.PRODUCT_NAME_NOT_BLANK); } } diff --git a/src/main/java/vendingmachine/exception/ProductNameNotFoundException.java b/src/main/java/vendingmachine/exception/ProductNameNotFoundException.java index 91520a8e2..411b0cfe1 100644 --- a/src/main/java/vendingmachine/exception/ProductNameNotFoundException.java +++ b/src/main/java/vendingmachine/exception/ProductNameNotFoundException.java @@ -1,8 +1,8 @@ package vendingmachine.exception; -public class ProductNameNotFoundException extends IllegalArgumentException { +public class ProductNameNotFoundException extends IllegalArgumentBaseException { public ProductNameNotFoundException() { - super(ExceptionMessage.PRODUCT_NAME_NOT_FOUND.getMessage()); + super(ExceptionMessage.PRODUCT_NAME_NOT_FOUND); } } diff --git a/src/main/java/vendingmachine/exception/ProductPriceFromatException.java b/src/main/java/vendingmachine/exception/ProductPriceFromatException.java index ce3e7cedf..4994efebc 100644 --- a/src/main/java/vendingmachine/exception/ProductPriceFromatException.java +++ b/src/main/java/vendingmachine/exception/ProductPriceFromatException.java @@ -1,8 +1,8 @@ package vendingmachine.exception; -public class ProductPriceFromatException extends IllegalArgumentException { +public class ProductPriceFromatException extends IllegalArgumentBaseException { public ProductPriceFromatException() { - super(ExceptionMessage.PRODUCT_PRICE_FORMAT.getMessage()); + super(ExceptionMessage.PRODUCT_PRICE_FORMAT); } } diff --git a/src/main/java/vendingmachine/exception/ProductQuantityFormatException.java b/src/main/java/vendingmachine/exception/ProductQuantityFormatException.java index e2ec71b81..31b7b7dbf 100644 --- a/src/main/java/vendingmachine/exception/ProductQuantityFormatException.java +++ b/src/main/java/vendingmachine/exception/ProductQuantityFormatException.java @@ -1,8 +1,8 @@ package vendingmachine.exception; -public class ProductQuantityFormatException extends IllegalArgumentException { +public class ProductQuantityFormatException extends IllegalArgumentBaseException { public ProductQuantityFormatException() { - super(ExceptionMessage.PRODUCT_QUANTITY_FORMAT.getMessage()); + super(ExceptionMessage.PRODUCT_QUANTITY_FORMAT); } } diff --git a/src/main/java/vendingmachine/exception/QuantityNumberFormatException.java b/src/main/java/vendingmachine/exception/QuantityNumberFormatException.java new file mode 100644 index 000000000..12aa03ccb --- /dev/null +++ b/src/main/java/vendingmachine/exception/QuantityNumberFormatException.java @@ -0,0 +1,8 @@ +package vendingmachine.exception; + +public class QuantityNumberFormatException extends IllegalArgumentBaseException { + + public QuantityNumberFormatException() { + super(ExceptionMessage.QUANTITY_NUMBER_FORMAT); + } +} From ddac25de33d91093059381723da8e65c2a0c9204 Mon Sep 17 00:00:00 2001 From: HyeonsuLee Date: Sun, 1 Dec 2024 16:25:34 +0900 Subject: [PATCH 18/23] =?UTF-8?q?feat:=20inputview=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vendingmachine/view/InputMessage.java | 24 +++++++++++++++++ .../java/vendingmachine/view/InputView.java | 27 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 src/main/java/vendingmachine/view/InputMessage.java create mode 100644 src/main/java/vendingmachine/view/InputView.java diff --git a/src/main/java/vendingmachine/view/InputMessage.java b/src/main/java/vendingmachine/view/InputMessage.java new file mode 100644 index 000000000..99759f40d --- /dev/null +++ b/src/main/java/vendingmachine/view/InputMessage.java @@ -0,0 +1,24 @@ +package vendingmachine.view; + +public enum InputMessage { + + INPUT_MACHINE_AMOUNT("자판기가 보유하고 있는 금액을 입력해 주세요."), + INPUT_PRODUCTS("상품명과 가격, 수량을 입력해 주세요."), + INPUT_BUY_AMOUNT("투입 금액을 입력해 주세요."), + INPUT_BUY_PRODUCT_NAME("구매할 상품명을 입력해 주세요."), + ; + + private final String message; + + InputMessage(String message) { + this.message = message; + } + + public void print(Object... values) { + System.out.println(format(values)); + } + + public String format(Object... values) { + return String.format(message, values); + } +} diff --git a/src/main/java/vendingmachine/view/InputView.java b/src/main/java/vendingmachine/view/InputView.java new file mode 100644 index 000000000..c11d09acd --- /dev/null +++ b/src/main/java/vendingmachine/view/InputView.java @@ -0,0 +1,27 @@ +package vendingmachine.view; + + +import camp.nextstep.edu.missionutils.Console; + +public class InputView { + + public String inputBuyAmount() { + InputMessage.INPUT_BUY_AMOUNT.print(); + return Console.readLine(); + } + + public String inputBuyProductName() { + InputMessage.INPUT_BUY_PRODUCT_NAME.print(); + return Console.readLine(); + } + + public String inputMachineAmount() { + InputMessage.INPUT_MACHINE_AMOUNT.print(); + return Console.readLine(); + } + + public String inputProducts() { + InputMessage.INPUT_PRODUCTS.print(); + return Console.readLine(); + } +} From 12f8b5d29313f98d039e9696f47473356561d6d4 Mon Sep 17 00:00:00 2001 From: HyeonsuLee Date: Sun, 1 Dec 2024 16:26:06 +0900 Subject: [PATCH 19/23] =?UTF-8?q?feat:=20outputView=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vendingmachine/view/OutputMessage.java | 34 +++++++++++++++++ .../java/vendingmachine/view/OutputView.java | 37 +++++++++++++++++++ .../view/StringBuilderPrinter.java | 17 +++++++++ 3 files changed, 88 insertions(+) create mode 100644 src/main/java/vendingmachine/view/OutputMessage.java create mode 100644 src/main/java/vendingmachine/view/OutputView.java create mode 100644 src/main/java/vendingmachine/view/StringBuilderPrinter.java diff --git a/src/main/java/vendingmachine/view/OutputMessage.java b/src/main/java/vendingmachine/view/OutputMessage.java new file mode 100644 index 000000000..cbec7c634 --- /dev/null +++ b/src/main/java/vendingmachine/view/OutputMessage.java @@ -0,0 +1,34 @@ +package vendingmachine.view; + +import java.util.List; + +public enum OutputMessage { + + OUTPUT_MACHINE_COIN_TITLE("자판기가 보유한 동전"), + OUTPUT_MACHINE_COIN_DETAIL("%d원 - %d개"), + OUTPUT_AMOUNT("투입 금액: %d원"), + OUTPUT_CHANGE_TITLE("잔돈"), + ; + + private final String message; + + OutputMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void print(Object... values) { + System.out.println(format(values)); + } + + public String format(Object... values) { + return String.format(message, values); + } + + public String format(List values) { + return String.format(message, values.toArray()); + } +} diff --git a/src/main/java/vendingmachine/view/OutputView.java b/src/main/java/vendingmachine/view/OutputView.java new file mode 100644 index 000000000..e2265e209 --- /dev/null +++ b/src/main/java/vendingmachine/view/OutputView.java @@ -0,0 +1,37 @@ +package vendingmachine.view; + +import static vendingmachine.view.OutputMessage.OUTPUT_AMOUNT; +import static vendingmachine.view.OutputMessage.OUTPUT_CHANGE_TITLE; +import static vendingmachine.view.OutputMessage.OUTPUT_MACHINE_COIN_DETAIL; +import static vendingmachine.view.OutputMessage.OUTPUT_MACHINE_COIN_TITLE; + +import vendingmachine.dto.ChangeDto; + +public class OutputView { + + public void printErrorMessage(String message) { + System.out.println(message); + } + + public void printMachineState(ChangeDto changeDto) { + StringBuilderPrinter printer = new StringBuilderPrinter(); + printer.appendLine(OUTPUT_MACHINE_COIN_TITLE.getMessage()); + changeDto.getChange().forEach((coin, count) -> + printer.appendLine(OUTPUT_MACHINE_COIN_DETAIL.format(coin, count)) + ); + printer.print(); + } + + public void printChange(ChangeDto changeDto) { + StringBuilderPrinter printer = new StringBuilderPrinter(); + printer.appendLine(OUTPUT_CHANGE_TITLE.getMessage()); + changeDto.getChange().forEach((coin, count) -> + printer.appendLine(OUTPUT_MACHINE_COIN_DETAIL.format(coin, count)) + ); + printer.print(); + } + + public void printRemainAmount(int amount) { + OUTPUT_AMOUNT.print(amount); + } +} diff --git a/src/main/java/vendingmachine/view/StringBuilderPrinter.java b/src/main/java/vendingmachine/view/StringBuilderPrinter.java new file mode 100644 index 000000000..4ab2ed9bd --- /dev/null +++ b/src/main/java/vendingmachine/view/StringBuilderPrinter.java @@ -0,0 +1,17 @@ +package vendingmachine.view; + +public class StringBuilderPrinter { + private final StringBuilder stringBuilder = new StringBuilder(); + + public void appendLine(String line) { + stringBuilder.append(line).append('\n'); + } + + public void appendDivideLine() { + stringBuilder.append('\n'); + } + + public void print() { + System.out.println(stringBuilder); + } +} From 18a0b940bbd2cbd182912798ccc6655f5afb004a Mon Sep 17 00:00:00 2001 From: HyeonsuLee Date: Sun, 1 Dec 2024 16:26:54 +0900 Subject: [PATCH 20/23] =?UTF-8?q?feat:=20controller,=20service=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/vendingmachine/Application.java | 5 +- .../vendingmachine/controller/Parser.java | 82 ++++++++++++++++++ .../controller/VendingMachineController.java | 86 +++++++++++++++++++ .../java/vendingmachine/dto/ChangeDto.java | 17 ++++ .../java/vendingmachine/model/Change.java | 10 ++- src/main/java/vendingmachine/model/Name.java | 22 +++++ .../java/vendingmachine/model/Quantity.java | 4 +- .../vendingmachine/model/VendingMachine.java | 7 +- .../service/VendingMachineService.java | 30 +++++++ 9 files changed, 255 insertions(+), 8 deletions(-) create mode 100644 src/main/java/vendingmachine/controller/Parser.java create mode 100644 src/main/java/vendingmachine/controller/VendingMachineController.java create mode 100644 src/main/java/vendingmachine/dto/ChangeDto.java create mode 100644 src/main/java/vendingmachine/service/VendingMachineService.java diff --git a/src/main/java/vendingmachine/Application.java b/src/main/java/vendingmachine/Application.java index 9d3be447b..4fcbe1f13 100644 --- a/src/main/java/vendingmachine/Application.java +++ b/src/main/java/vendingmachine/Application.java @@ -1,7 +1,10 @@ package vendingmachine; +import vendingmachine.controller.VendingMachineController; + public class Application { public static void main(String[] args) { - // TODO: 프로그램 구현 + VendingMachineController vendingMachineController = new VendingMachineController(); + vendingMachineController.run(); } } diff --git a/src/main/java/vendingmachine/controller/Parser.java b/src/main/java/vendingmachine/controller/Parser.java new file mode 100644 index 000000000..645c4d84a --- /dev/null +++ b/src/main/java/vendingmachine/controller/Parser.java @@ -0,0 +1,82 @@ +package vendingmachine.controller; + +import java.util.Arrays; +import java.util.stream.Collectors; +import vendingmachine.exception.AmountNumberFormatException; +import vendingmachine.exception.PriceNumberFormatException; +import vendingmachine.exception.ProductFormatException; +import vendingmachine.exception.QuantityNumberFormatException; +import vendingmachine.model.Amount; +import vendingmachine.model.MachineAmount; +import vendingmachine.model.Name; +import vendingmachine.model.Order; +import vendingmachine.model.Price; +import vendingmachine.model.Product; +import vendingmachine.model.Products; +import vendingmachine.model.Quantity; + +public class Parser { + + private Parser() { + } + + public static MachineAmount parseAmountInt(String amount) { + try { + return new MachineAmount(Integer.parseInt(amount)); + }catch (NumberFormatException exception) { + throw new AmountNumberFormatException(); + } + } + + public static Amount parseAmount(String amount) { + try { + return new Amount(Integer.parseInt(amount)); + }catch (NumberFormatException exception) { + throw new AmountNumberFormatException(); + } + } + + public static Products parseProducts(String products) { + String[] product = products.split(";"); + return new Products(Arrays.stream(product).map(Parser::parseProduct).collect(Collectors.toList())); + } + + private static Product parseProduct(String product) { + if (product.toCharArray().length <= 2) { + throw new ProductFormatException(); + } + if (!(product.charAt(0) == '[' && product.charAt(product.length() - 1) == ']')) { + throw new ProductFormatException(); + } + String replaced = product.replace("[", "").replace("]", ""); + String[] productInfo = replaced.split(","); + if (productInfo.length != 3) { + throw new ProductFormatException(); + } + return new Product( + new Name(productInfo[0]), + parsePrice(productInfo[1]), + parseQuantity(productInfo[2]) + ); + } + + private static Price parsePrice(String price) { + try { + return new Price(Integer.parseInt(price)); + }catch (NumberFormatException exception) { + throw new PriceNumberFormatException(); + } + } + + private static Quantity parseQuantity(String quantity) { + try { + return new Quantity(Integer.parseInt(quantity)); + }catch (NumberFormatException exception) { + throw new QuantityNumberFormatException(); + } + } + + public static Order parseOrder(String productName) { + return new Order(new Name(productName)); + } +} diff --git a/src/main/java/vendingmachine/controller/VendingMachineController.java b/src/main/java/vendingmachine/controller/VendingMachineController.java new file mode 100644 index 000000000..e9b8e71a5 --- /dev/null +++ b/src/main/java/vendingmachine/controller/VendingMachineController.java @@ -0,0 +1,86 @@ +package vendingmachine.controller; + +import java.util.function.Supplier; +import vendingmachine.dto.ChangeDto; +import vendingmachine.exception.IllegalArgumentBaseException; +import vendingmachine.model.Amount; +import vendingmachine.model.Change; +import vendingmachine.model.MachineAmount; +import vendingmachine.model.Order; +import vendingmachine.model.Products; +import vendingmachine.model.VendingMachine; +import vendingmachine.service.VendingMachineService; +import vendingmachine.view.InputView; +import vendingmachine.view.OutputView; + +public class VendingMachineController { + + private final InputView inputView = new InputView(); + private final OutputView outputView = new OutputView(); + private VendingMachineService vendingMachineService; + + public void run() { + Change change = handleInput(this::inputMachineAmount); + outputView.printMachineState(new ChangeDto(change)); + Products products = handleInput(this::inputProducts); + Amount amount = handleInput(this::inputAmount); + vendingMachineService = new VendingMachineService(new VendingMachine(change, products, amount)); + buy(); + + } + + private void buy() { + while(vendingMachineService.isBuyable()) { + buyProduct(); + } + printResultChange(); + } + + private void printResultChange() { + outputView.printRemainAmount(vendingMachineService.getAmount()); + outputView.printChange(vendingMachineService.getChange()); + } + + private void buyProduct() { + outputView.printRemainAmount(vendingMachineService.getAmount()); + handleInput(() -> vendingMachineService.buy(inputOrder())); + } + + private Order inputOrder() { + return Parser.parseOrder(inputView.inputBuyProductName()); + } + + private Amount inputAmount() { + return Parser.parseAmount(inputView.inputBuyAmount()); + } + + private Change inputMachineAmount() { + MachineAmount amount = Parser.parseAmountInt(inputView.inputMachineAmount()); + return new Change(amount); + } + + private Products inputProducts() { + return Parser.parseProducts(inputView.inputProducts()); + } + + private T handleInput(Supplier inputSupplier) { + while (true) { + try { + return inputSupplier.get(); + } catch (IllegalArgumentBaseException exception) { + outputView.printErrorMessage(exception.getMessage()); + } + } + } + + private void handleInput(Runnable runnable) { + while (true) { + try { + runnable.run(); + return; + } catch (IllegalArgumentBaseException exception) { + outputView.printErrorMessage(exception.getMessage()); + } + } + } +} diff --git a/src/main/java/vendingmachine/dto/ChangeDto.java b/src/main/java/vendingmachine/dto/ChangeDto.java new file mode 100644 index 000000000..46bd0d93a --- /dev/null +++ b/src/main/java/vendingmachine/dto/ChangeDto.java @@ -0,0 +1,17 @@ +package vendingmachine.dto; + +import java.util.LinkedHashMap; +import java.util.Map; +import vendingmachine.model.Change; + +public class ChangeDto { + private final Map change = new LinkedHashMap<>(); + + public ChangeDto(Change change) { + change.getChangeMoney().forEach(((coin, count) -> this.change.put(coin.getAmount(), count))); + } + + public Map getChange() { + return change; + } +} diff --git a/src/main/java/vendingmachine/model/Change.java b/src/main/java/vendingmachine/model/Change.java index f5f103180..4b08e1aa8 100644 --- a/src/main/java/vendingmachine/model/Change.java +++ b/src/main/java/vendingmachine/model/Change.java @@ -6,10 +6,10 @@ public class Change { private final Map changeMoney = new LinkedHashMap<>(); - public Change(int money) { - int remainMoney = money; + public Change(MachineAmount amount) { + int remainMoney = amount.getAmount(); for (Coin coin : Coin.values()) { - int coinCount = remainMoney % coin.getAmount(); + int coinCount = remainMoney / coin.getAmount(); changeMoney.put(coin, coinCount); remainMoney -= coin.getAmount() * coinCount; } @@ -21,4 +21,8 @@ public int getTotalChange() { .mapToInt(changeEntry -> changeEntry.getKey().getAmount() * changeEntry.getValue()) .sum(); } + + public Map getChangeMoney() { + return changeMoney; + } } diff --git a/src/main/java/vendingmachine/model/Name.java b/src/main/java/vendingmachine/model/Name.java index 5f2036b78..dc5d57ad9 100644 --- a/src/main/java/vendingmachine/model/Name.java +++ b/src/main/java/vendingmachine/model/Name.java @@ -1,5 +1,6 @@ package vendingmachine.model; +import java.util.Objects; import vendingmachine.exception.ProductNameNotBlankException; public class Name { @@ -15,4 +16,25 @@ private void validateName(String name) { throw new ProductNameNotBlankException(); } } + + public String getName() { + return name; + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!(other instanceof Name)) { + return false; + } + Name otherName = (Name) other; + return Objects.equals(getName(), otherName.getName()); + } + + @Override + public int hashCode() { + return Objects.hashCode(getName()); + } } diff --git a/src/main/java/vendingmachine/model/Quantity.java b/src/main/java/vendingmachine/model/Quantity.java index 586a2e0f1..92d32efc1 100644 --- a/src/main/java/vendingmachine/model/Quantity.java +++ b/src/main/java/vendingmachine/model/Quantity.java @@ -10,8 +10,8 @@ public Quantity(int quantity) { this.quantity = quantity; } -private void validateQuantity(int quantity) { - if (quantity < 0) { + private void validateQuantity(int quantity) { + if (quantity <= 0) { throw new ProductQuantityFormatException(); } } diff --git a/src/main/java/vendingmachine/model/VendingMachine.java b/src/main/java/vendingmachine/model/VendingMachine.java index d7c8d34a4..fe957090c 100644 --- a/src/main/java/vendingmachine/model/VendingMachine.java +++ b/src/main/java/vendingmachine/model/VendingMachine.java @@ -18,7 +18,7 @@ public Change getChange() { if (change.getTotalChange() <= amount.getAmount()) { return change; } - return new Change(amount.getAmount()); + return new Change(new MachineAmount(amount.getAmount())); } public boolean isBuyableAnyProduct() { @@ -28,7 +28,6 @@ public boolean isBuyableAnyProduct() { public void buy(Order order) { Product product = validateOrder(order); amount.decrease(product.getPrice()); - } private Product validateOrder(Order order) { @@ -38,4 +37,8 @@ private Product validateOrder(Order order) { } return product; } + + public int getAmount() { + return amount.getAmount(); + } } diff --git a/src/main/java/vendingmachine/service/VendingMachineService.java b/src/main/java/vendingmachine/service/VendingMachineService.java new file mode 100644 index 000000000..327e91ad9 --- /dev/null +++ b/src/main/java/vendingmachine/service/VendingMachineService.java @@ -0,0 +1,30 @@ +package vendingmachine.service; + +import vendingmachine.dto.ChangeDto; +import vendingmachine.model.Order; +import vendingmachine.model.VendingMachine; + +public class VendingMachineService { + + private final VendingMachine vendingMachine; + + public VendingMachineService(VendingMachine vendingMachine) { + this.vendingMachine = vendingMachine; + } + + public boolean isBuyable() { + return vendingMachine.isBuyableAnyProduct(); + } + + public void buy(Order order) { + vendingMachine.buy(order); + } + + public int getAmount() { + return vendingMachine.getAmount(); + } + + public ChangeDto getChange() { + return new ChangeDto(vendingMachine.getChange()); + } +} From 3cfdc33852b111699b969ba187ba299ab310d06b Mon Sep 17 00:00:00 2001 From: HyeonsuLee Date: Sun, 1 Dec 2024 16:27:31 +0900 Subject: [PATCH 21/23] =?UTF-8?q?feat:=20=EC=9E=90=ED=8C=90=EA=B8=B0=20?= =?UTF-8?q?=EA=B8=88=EC=95=A1=20model=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vendingmachine/model/MachineAmount.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/main/java/vendingmachine/model/MachineAmount.java diff --git a/src/main/java/vendingmachine/model/MachineAmount.java b/src/main/java/vendingmachine/model/MachineAmount.java new file mode 100644 index 000000000..a12b69ff8 --- /dev/null +++ b/src/main/java/vendingmachine/model/MachineAmount.java @@ -0,0 +1,26 @@ +package vendingmachine.model; + +import vendingmachine.exception.AmountFormatException; + +public class MachineAmount { + private int amount; + + public MachineAmount(int amount) { + validateAmount(amount); + this.amount = amount; + } + + private void validateAmount(int amount) { + if (amount < 0 || amount % 10 != 0) { + throw new AmountFormatException(); + } + } + + public int getAmount() { + return amount; + } + + public void decrease(int amount) { + this.amount -= amount; + } +} From 85c0f5471f5a0a5dbeb9971f5ae3d2a8d8152943 Mon Sep 17 00:00:00 2001 From: HyeonsuLee Date: Sun, 1 Dec 2024 17:18:11 +0900 Subject: [PATCH 22/23] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/ExceptionMessage.java | 3 +- .../ProductQuantityNotEnoughException.java | 8 ++ .../java/vendingmachine/model/Amount.java | 3 + .../java/vendingmachine/model/Product.java | 14 ++- .../java/vendingmachine/model/Products.java | 7 ++ .../java/vendingmachine/model/Quantity.java | 10 ++- .../vendingmachine/model/VendingMachine.java | 7 +- .../java/vendingmachine/model/AmountTest.java | 35 ++++++++ .../java/vendingmachine/model/ChangeTest.java | 32 +++++++ .../model/MachineAmountTest.java | 25 ++++++ .../java/vendingmachine/model/NameTest.java | 15 ++++ .../java/vendingmachine/model/PriceTest.java | 23 +++++ .../vendingmachine/model/ProductTest.java | 60 +++++++++++++ .../vendingmachine/model/ProductsTest.java | 88 +++++++++++++++++++ .../vendingmachine/model/QuantityTest.java | 19 ++++ .../model/VendingMachineTest.java | 76 ++++++++++++++++ 16 files changed, 420 insertions(+), 5 deletions(-) create mode 100644 src/main/java/vendingmachine/exception/ProductQuantityNotEnoughException.java create mode 100644 src/test/java/vendingmachine/model/AmountTest.java create mode 100644 src/test/java/vendingmachine/model/ChangeTest.java create mode 100644 src/test/java/vendingmachine/model/MachineAmountTest.java create mode 100644 src/test/java/vendingmachine/model/NameTest.java create mode 100644 src/test/java/vendingmachine/model/PriceTest.java create mode 100644 src/test/java/vendingmachine/model/ProductTest.java create mode 100644 src/test/java/vendingmachine/model/ProductsTest.java create mode 100644 src/test/java/vendingmachine/model/QuantityTest.java create mode 100644 src/test/java/vendingmachine/model/VendingMachineTest.java diff --git a/src/main/java/vendingmachine/exception/ExceptionMessage.java b/src/main/java/vendingmachine/exception/ExceptionMessage.java index 0850e30dd..ab5b224ee 100644 --- a/src/main/java/vendingmachine/exception/ExceptionMessage.java +++ b/src/main/java/vendingmachine/exception/ExceptionMessage.java @@ -5,12 +5,13 @@ public enum ExceptionMessage { PRICE_NUMBER_FORMAT("가격은 숫자여야 합니다."), AMOUNT_NUMBER_FORMAT("금액은 숫자여야 합니다."), QUANTITY_NUMBER_FORMAT("수량은 숫자여야 합니다."), - AMOUNT_FORMAT("금액은 10으로 나누어 떨어져야 합니다."), + AMOUNT_FORMAT("금액은 10으로 나누어 떨어지며 0 이상이여야 합니다."), PRODUCT_PRICE_FORMAT("상품 가격은 100원 이상 10으로 나누어 떨어져야 합니다."), PRODUCT_FORMAT("상품 입력 형식을 확인해주세요."), PRODUCT_QUANTITY_FORMAT("상품의 개수는 0개 이상이여야 합니다."), PRODUCT_NAME_NOT_BLANK("상품의 이름은 공백일 수 없습니다."), PRODUCT_NAME_NOT_FOUND("존재하지 않는 상품입니다."), + PRODUCT_QUANTITY_NOT_ENOUGH("재고가 부족합니다."), AMOUNT_NOT_ENOUGH("금액이 부족합니다."), ; diff --git a/src/main/java/vendingmachine/exception/ProductQuantityNotEnoughException.java b/src/main/java/vendingmachine/exception/ProductQuantityNotEnoughException.java new file mode 100644 index 000000000..17c118c3d --- /dev/null +++ b/src/main/java/vendingmachine/exception/ProductQuantityNotEnoughException.java @@ -0,0 +1,8 @@ +package vendingmachine.exception; + +public class ProductQuantityNotEnoughException extends IllegalArgumentBaseException { + + public ProductQuantityNotEnoughException() { + super(ExceptionMessage.PRODUCT_QUANTITY_NOT_ENOUGH); + } +} diff --git a/src/main/java/vendingmachine/model/Amount.java b/src/main/java/vendingmachine/model/Amount.java index 9aa9ea4e1..e3b717cf2 100644 --- a/src/main/java/vendingmachine/model/Amount.java +++ b/src/main/java/vendingmachine/model/Amount.java @@ -21,6 +21,9 @@ public int getAmount() { } public void decrease(int amount) { + if (this.amount - amount < 0) { + throw new AmountFormatException(); + } this.amount -= amount; } } diff --git a/src/main/java/vendingmachine/model/Product.java b/src/main/java/vendingmachine/model/Product.java index 6531abb02..cbe79015d 100644 --- a/src/main/java/vendingmachine/model/Product.java +++ b/src/main/java/vendingmachine/model/Product.java @@ -1,7 +1,5 @@ package vendingmachine.model; -import vendingmachine.exception.AmountNotEnoughException; - public class Product { private final Name name; private final Price price; @@ -24,4 +22,16 @@ public boolean isBuyable(Amount amount) { public int getPrice() { return price.getPrice(); } + + public void decreaseQuantity() { + this.quantity.decrease(); + } + + public int getQuantity() { + return quantity.getQuantity(); + } + + public boolean isEnoughQuantity() { + return quantity.getQuantity() > 0; + } } diff --git a/src/main/java/vendingmachine/model/Products.java b/src/main/java/vendingmachine/model/Products.java index eada6769e..cec19147c 100644 --- a/src/main/java/vendingmachine/model/Products.java +++ b/src/main/java/vendingmachine/model/Products.java @@ -17,6 +17,13 @@ public Optional find(Name name) { return products.stream().filter(product -> product.getName().equals(name)).findFirst(); } + public long buyableProductCount(int amount) { + return products.stream() + .filter(product -> product.getPrice() <= amount) + .filter(product -> product.getQuantity() > 0) + .count(); + } + @Override public Iterator iterator() { return products.iterator(); diff --git a/src/main/java/vendingmachine/model/Quantity.java b/src/main/java/vendingmachine/model/Quantity.java index 92d32efc1..9d8b4e188 100644 --- a/src/main/java/vendingmachine/model/Quantity.java +++ b/src/main/java/vendingmachine/model/Quantity.java @@ -3,7 +3,7 @@ import vendingmachine.exception.ProductQuantityFormatException; public class Quantity { - private final int quantity; + private int quantity; public Quantity(int quantity) { validateQuantity(quantity); @@ -15,4 +15,12 @@ private void validateQuantity(int quantity) { throw new ProductQuantityFormatException(); } } + + public void decrease() { + this.quantity--; + } + + public int getQuantity() { + return quantity; + } } diff --git a/src/main/java/vendingmachine/model/VendingMachine.java b/src/main/java/vendingmachine/model/VendingMachine.java index fe957090c..ef1a09e2d 100644 --- a/src/main/java/vendingmachine/model/VendingMachine.java +++ b/src/main/java/vendingmachine/model/VendingMachine.java @@ -2,6 +2,7 @@ import vendingmachine.exception.AmountNotEnoughException; import vendingmachine.exception.ProductNameNotFoundException; +import vendingmachine.exception.ProductQuantityNotEnoughException; public class VendingMachine { private final Change change; @@ -22,12 +23,13 @@ public Change getChange() { } public boolean isBuyableAnyProduct() { - return products.findMinPrice() <= amount.getAmount(); + return products.buyableProductCount(getAmount()) > 0; } public void buy(Order order) { Product product = validateOrder(order); amount.decrease(product.getPrice()); + product.decreaseQuantity(); } private Product validateOrder(Order order) { @@ -35,6 +37,9 @@ private Product validateOrder(Order order) { if (!product.isBuyable(amount)) { throw new AmountNotEnoughException(); } + if (!product.isEnoughQuantity()) { + throw new ProductQuantityNotEnoughException(); + } return product; } diff --git a/src/test/java/vendingmachine/model/AmountTest.java b/src/test/java/vendingmachine/model/AmountTest.java new file mode 100644 index 000000000..853d9ee53 --- /dev/null +++ b/src/test/java/vendingmachine/model/AmountTest.java @@ -0,0 +1,35 @@ +package vendingmachine.model; + +import static camp.nextstep.edu.missionutils.test.Assertions.assertSimpleTest; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import camp.nextstep.edu.missionutils.test.Assertions; +import org.junit.jupiter.api.Test; + +class AmountTest { + + @Test + void 자판기_투입_금액은_10으로_나누어_떨어지지_않으면_예외를_발생한다() { + assertThatThrownBy(() -> { + Amount amount = new Amount(1); + }).isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 자판기에_남은_금액보다_많이_사용하면_예외를_발생한다() { + assertThatThrownBy(() -> { + Amount amount = new Amount(10); + amount.decrease(15); + }).isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 자판기_투입_금액을_사용한다() { + assertSimpleTest(() -> { + Amount amount = new Amount(10); + amount.decrease(5); + assertThat(amount.getAmount()).isEqualTo(5); + }); + } +} diff --git a/src/test/java/vendingmachine/model/ChangeTest.java b/src/test/java/vendingmachine/model/ChangeTest.java new file mode 100644 index 000000000..4d873ba81 --- /dev/null +++ b/src/test/java/vendingmachine/model/ChangeTest.java @@ -0,0 +1,32 @@ +package vendingmachine.model; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import camp.nextstep.edu.missionutils.test.Assertions; +import org.junit.jupiter.api.Test; + +class ChangeTest { + + @Test + void 자판기의_잔돈을_반환한다() { + Assertions.assertSimpleTest(() -> { + Change change = new Change(new MachineAmount(450)); + assertThat(change.getTotalChange()).isEqualTo(450); + }); + } + + @Test + void 자판기에_금액을_입력하면_동전으로_환산한다() { + Assertions.assertSimpleTest(() -> { + Change change = new Change(new MachineAmount(450)); + assertThat(change.getChangeMoney().get(Coin.COIN_500)).isZero(); + assertThat(change.getChangeMoney().get(Coin.COIN_100)).isEqualTo(4); + assertThat(change.getChangeMoney().get(Coin.COIN_50)).isEqualTo(1); + assertThat(change.getChangeMoney().get(Coin.COIN_10)).isZero(); + }); + assertThatThrownBy(() -> { + MachineAmount machineAmount = new MachineAmount(-2); + }).isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/src/test/java/vendingmachine/model/MachineAmountTest.java b/src/test/java/vendingmachine/model/MachineAmountTest.java new file mode 100644 index 000000000..60801e015 --- /dev/null +++ b/src/test/java/vendingmachine/model/MachineAmountTest.java @@ -0,0 +1,25 @@ +package vendingmachine.model; + +import static camp.nextstep.edu.missionutils.test.Assertions.assertSimpleTest; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.Test; + +class MachineAmountTest { + + @Test + void 자판기의_잔돈은_0원보다_작으면_예외가_발생한다() { + assertThatThrownBy(() -> { + MachineAmount machineAmount = new MachineAmount(-2); + }).isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 자판기의_잔돈은_10으로_나누어_떨어지지_않으면_예외가_발생한다() { + assertThatThrownBy(() -> { + Amount amount = new Amount(10); + amount.decrease(15); + }).isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/src/test/java/vendingmachine/model/NameTest.java b/src/test/java/vendingmachine/model/NameTest.java new file mode 100644 index 000000000..643cc278b --- /dev/null +++ b/src/test/java/vendingmachine/model/NameTest.java @@ -0,0 +1,15 @@ +package vendingmachine.model; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.Test; + +class NameTest { + + @Test + void 상품_이름은_공백일_수_없다() { + assertThatThrownBy(() -> { + Name name = new Name(""); + }).isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/src/test/java/vendingmachine/model/PriceTest.java b/src/test/java/vendingmachine/model/PriceTest.java new file mode 100644 index 000000000..86313541f --- /dev/null +++ b/src/test/java/vendingmachine/model/PriceTest.java @@ -0,0 +1,23 @@ +package vendingmachine.model; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.Test; + +class PriceTest { + + @Test + void 제품의_가격은_100원_이상이_아니면_예외가_발생한다() { + assertThatThrownBy(() -> { + Price price = new Price(90); + }).isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 제품의_가격은_10으로_나누어_떨어지지_않으면_예외를_발생한다() { + assertThatThrownBy(() -> { + Price price = new Price(91); + }).isInstanceOf(IllegalArgumentException.class); + } + +} diff --git a/src/test/java/vendingmachine/model/ProductTest.java b/src/test/java/vendingmachine/model/ProductTest.java new file mode 100644 index 000000000..5463a7bb9 --- /dev/null +++ b/src/test/java/vendingmachine/model/ProductTest.java @@ -0,0 +1,60 @@ +package vendingmachine.model; + +import static camp.nextstep.edu.missionutils.test.Assertions.assertSimpleTest; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import camp.nextstep.edu.missionutils.test.Assertions; +import org.junit.jupiter.api.Test; + +class ProductTest { + + @Test + void 가진_금액으로_제품을_구매할수_없으면_false를_반환한다() { + assertSimpleTest(() -> { + Product product = new Product( + new Name("콜라"), + new Price(1500), + new Quantity(10) + ); + assertThat(product.isBuyable(new Amount(1400))).isFalse(); + }); + } + + @Test + void 가진_금액으로_제품을_구매할수_있으면_true를_반환한다() { + assertSimpleTest(() -> { + Product product = new Product( + new Name("콜라"), + new Price(1500), + new Quantity(10) + ); + assertThat(product.isBuyable(new Amount(1600))).isTrue(); + }); + } + + @Test + void 재고가_충분하지_않으면_false를_반환한다() { + assertSimpleTest(() -> { + Product product = new Product( + new Name("콜라"), + new Price(1500), + new Quantity(1) + ); + product.decreaseQuantity(); + assertThat(product.isEnoughQuantity()).isFalse(); + }); + } + + @Test + void 재고가_충분하면_true를_반환한다() { + assertSimpleTest(() -> { + Product product = new Product( + new Name("콜라"), + new Price(1500), + new Quantity(10) + ); + assertThat(product.isEnoughQuantity()).isTrue(); + }); + } +} diff --git a/src/test/java/vendingmachine/model/ProductsTest.java b/src/test/java/vendingmachine/model/ProductsTest.java new file mode 100644 index 000000000..d1e0c31de --- /dev/null +++ b/src/test/java/vendingmachine/model/ProductsTest.java @@ -0,0 +1,88 @@ +package vendingmachine.model; + +import static camp.nextstep.edu.missionutils.test.Assertions.assertSimpleTest; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class ProductsTest { + + private Products products; + + @BeforeEach + void setup() { + List productList = new ArrayList<>(); + productList.add(new Product( + new Name("콜라"), + new Price(1500), + new Quantity(10) + )); + productList.add(new Product( + new Name("사이다"), + new Price(1000), + new Quantity(10) + )); + products = new Products(productList); + } + + @Test + void 존재하는_상품을_조회한다() { + assertSimpleTest(() -> { + assertThat(products.find(new Name("콜라"))).isPresent(); + }); + } + + @Test + void 존재하지_않는_상품을_조회한다() { + assertSimpleTest(() -> { + assertThat(products.find(new Name("콜라1"))).isNotPresent(); + }); + } + + @Test + void 가장_저렴한_상품의_가격을_조회한다() { + assertSimpleTest(() -> assertThat(products.findMinPrice()).isEqualTo(1000)); + } + + @Test + void 구매_가능한_상품이_없을_때_개수를_계산한다() { + assertSimpleTest(() -> { + List productList = new ArrayList<>(); + productList.add(new Product( + new Name("콜라"), + new Price(1500), + new Quantity(10) + )); + productList.add(new Product( + new Name("사이다"), + new Price(1000), + new Quantity(10) + )); + products = new Products(productList); + assertThat(products.buyableProductCount(500)).isZero(); + }); + } + + @Test + void 구매_가능한_상품이_있을_때_개수를_계산한다() { + assertSimpleTest(() -> { + List productList = new ArrayList<>(); + productList.add(new Product( + new Name("콜라"), + new Price(1500), + new Quantity(10) + )); + productList.add(new Product( + new Name("사이다"), + new Price(1000), + new Quantity(10) + )); + products = new Products(productList); + assertThat(products.buyableProductCount(1000)).isEqualTo(1); + }); + } +} diff --git a/src/test/java/vendingmachine/model/QuantityTest.java b/src/test/java/vendingmachine/model/QuantityTest.java new file mode 100644 index 000000000..2dc8d8c4b --- /dev/null +++ b/src/test/java/vendingmachine/model/QuantityTest.java @@ -0,0 +1,19 @@ +package vendingmachine.model; + +import static camp.nextstep.edu.missionutils.test.Assertions.assertSimpleTest; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.Test; + +class QuantityTest { + + @Test + void 재고를_감소시킨다() { + assertSimpleTest(() -> { + Quantity quantity = new Quantity(1); + quantity.decrease(); + assertThat(quantity.getQuantity()).isZero(); + }); + } +} diff --git a/src/test/java/vendingmachine/model/VendingMachineTest.java b/src/test/java/vendingmachine/model/VendingMachineTest.java new file mode 100644 index 000000000..42f60dfab --- /dev/null +++ b/src/test/java/vendingmachine/model/VendingMachineTest.java @@ -0,0 +1,76 @@ +package vendingmachine.model; + +import static camp.nextstep.edu.missionutils.test.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import camp.nextstep.edu.missionutils.test.Assertions; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.IntStream; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class VendingMachineTest { + + private VendingMachine vendingMachine; + private Products products; + + @BeforeEach + void setup() { + List productList = new ArrayList<>(); + productList.add(new Product( + new Name("콜라"), + new Price(1500), + new Quantity(10) + )); + productList.add(new Product( + new Name("사이다"), + new Price(100), + new Quantity(30) + )); + products = new Products(productList); + Change change = new Change(new MachineAmount(450)); + Amount amount = new Amount(3000); + vendingMachine = new VendingMachine(change, products, amount); + } + + @Test + void 잔돈을_반환한다_잔돈이_부족한경우_모든_잔돈을_반환한다() { + assertSimpleTest(() -> { + assertThat(vendingMachine.getChange().getTotalChange()).isEqualTo(450); + }); + } + + @Test + void 잔돈이_부족하지_않은_경우_게산하여_반환한다() { + assertSimpleTest(() -> { + IntStream.range(0, 27).forEach(i -> vendingMachine.buy(new Order(new Name("사이다")))); + assertThat(vendingMachine.getChange().getTotalChange()).isEqualTo(300); + }); + } + + @Test + void 구매_가능한_상품이_있다면_true를_반환한다() { + assertSimpleTest(() -> { + assertThat(vendingMachine.isBuyableAnyProduct()).isTrue(); + }); + } + + @Test + void 구매_가능한_상품이_없다면_false를_반환한다() { + assertSimpleTest(() -> { + IntStream.range(0, 30).forEach(i -> vendingMachine.buy(new Order(new Name("사이다")))); + assertThat(vendingMachine.isBuyableAnyProduct()).isFalse(); + }); + } + + @Test + void 상품을_구매한다() { + assertSimpleTest(() -> { + vendingMachine.buy(new Order(new Name("사이다"))); + assertThat(vendingMachine.getAmount()).isEqualTo(2900); + assertThat(products.find(new Name("사이다")).get().getQuantity()).isEqualTo(29); + }); + } +} From 700cebb76ed9e24f98f54c69ed66ae48dab1cbaa Mon Sep 17 00:00:00 2001 From: HyeonsuLee Date: Sun, 1 Dec 2024 17:30:55 +0900 Subject: [PATCH 23/23] =?UTF-8?q?feat:=20=EC=83=81=EC=88=98=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vendingmachine/controller/Parser.java | 45 ++++++++++++++----- .../controller/VendingMachineController.java | 3 +- .../java/vendingmachine/model/Amount.java | 12 ++--- src/main/java/vendingmachine/model/Coin.java | 2 - .../vendingmachine/model/MachineAmount.java | 4 -- src/main/java/vendingmachine/model/Name.java | 12 ++--- src/main/java/vendingmachine/model/Order.java | 2 +- src/main/java/vendingmachine/model/Price.java | 8 ++-- .../java/vendingmachine/model/Product.java | 4 +- .../vendingmachine/model/VendingMachine.java | 6 +-- .../view/StringBuilderPrinter.java | 4 -- .../java/vendingmachine/model/AmountTest.java | 3 +- 12 files changed, 57 insertions(+), 48 deletions(-) diff --git a/src/main/java/vendingmachine/controller/Parser.java b/src/main/java/vendingmachine/controller/Parser.java index 645c4d84a..f354e22de 100644 --- a/src/main/java/vendingmachine/controller/Parser.java +++ b/src/main/java/vendingmachine/controller/Parser.java @@ -17,10 +17,23 @@ public class Parser { + private static final String PRODUCT_SEPARATOR = ";"; + private static final char PRODUCT_START_WITH_CHAR = '['; + private static final char PRODUCT_END_WITH_CHAR = ']'; + private static final String PRODUCT_START_WITH_STRING = "["; + private static final String PRODUCT_END_WITH_STRING = "]"; + private static final String PRODUCT_INFO_SEPARATOR = ","; + private static final int MIN_PRODUCT_INFO_STRING_SIZE = 2; + private static final int PRODUCT_INFO_SIZE = 3; + private static final int PRODUCT_NAME_INDEX = 0; + private static final int PRODUCT_PRICE_INDEX = 1; + private static final int PRODUCT_QUANTITY_INDEX = 2; + + private Parser() { } - public static MachineAmount parseAmountInt(String amount) { + public static MachineAmount parseMachineAmount(String amount) { try { return new MachineAmount(Integer.parseInt(amount)); }catch (NumberFormatException exception) { @@ -37,27 +50,35 @@ public static Amount parseAmount(String amount) { } public static Products parseProducts(String products) { - String[] product = products.split(";"); + String[] product = products.split(PRODUCT_SEPARATOR); return new Products(Arrays.stream(product).map(Parser::parseProduct).collect(Collectors.toList())); } private static Product parseProduct(String product) { - if (product.toCharArray().length <= 2) { + validateProductString(product); + String replaced = product.replace(PRODUCT_START_WITH_STRING, "").replace(PRODUCT_END_WITH_STRING, ""); + String[] productInfo = replaced.split(PRODUCT_INFO_SEPARATOR); + validateProductInfo(productInfo); + return new Product( + new Name(productInfo[PRODUCT_NAME_INDEX]), + parsePrice(productInfo[PRODUCT_PRICE_INDEX]), + parseQuantity(productInfo[PRODUCT_QUANTITY_INDEX]) + ); + } + + private static void validateProductInfo(String[] productInfo) { + if (productInfo.length != PRODUCT_INFO_SIZE) { throw new ProductFormatException(); } - if (!(product.charAt(0) == '[' && product.charAt(product.length() - 1) == ']')) { + } + + private static void validateProductString(String product) { + if (product.toCharArray().length <= MIN_PRODUCT_INFO_STRING_SIZE) { throw new ProductFormatException(); } - String replaced = product.replace("[", "").replace("]", ""); - String[] productInfo = replaced.split(","); - if (productInfo.length != 3) { + if (!(product.charAt(0) == PRODUCT_START_WITH_CHAR && product.charAt(product.length() - 1) == PRODUCT_END_WITH_CHAR)) { throw new ProductFormatException(); } - return new Product( - new Name(productInfo[0]), - parsePrice(productInfo[1]), - parseQuantity(productInfo[2]) - ); } private static Price parsePrice(String price) { diff --git a/src/main/java/vendingmachine/controller/VendingMachineController.java b/src/main/java/vendingmachine/controller/VendingMachineController.java index e9b8e71a5..b68ef1097 100644 --- a/src/main/java/vendingmachine/controller/VendingMachineController.java +++ b/src/main/java/vendingmachine/controller/VendingMachineController.java @@ -26,7 +26,6 @@ public void run() { Amount amount = handleInput(this::inputAmount); vendingMachineService = new VendingMachineService(new VendingMachine(change, products, amount)); buy(); - } private void buy() { @@ -55,7 +54,7 @@ private Amount inputAmount() { } private Change inputMachineAmount() { - MachineAmount amount = Parser.parseAmountInt(inputView.inputMachineAmount()); + MachineAmount amount = Parser.parseMachineAmount(inputView.inputMachineAmount()); return new Change(amount); } diff --git a/src/main/java/vendingmachine/model/Amount.java b/src/main/java/vendingmachine/model/Amount.java index e3b717cf2..f18a43dec 100644 --- a/src/main/java/vendingmachine/model/Amount.java +++ b/src/main/java/vendingmachine/model/Amount.java @@ -3,11 +3,11 @@ import vendingmachine.exception.AmountFormatException; public class Amount { - private int amount; + private int value; public Amount(int amount) { validateAmount(amount); - this.amount = amount; + this.value = amount; } private void validateAmount(int amount) { @@ -16,14 +16,14 @@ private void validateAmount(int amount) { } } - public int getAmount() { - return amount; + public int getValue() { + return value; } public void decrease(int amount) { - if (this.amount - amount < 0) { + if (this.value - amount < 0) { throw new AmountFormatException(); } - this.amount -= amount; + this.value -= amount; } } diff --git a/src/main/java/vendingmachine/model/Coin.java b/src/main/java/vendingmachine/model/Coin.java index e5d161ab5..6779e7d40 100644 --- a/src/main/java/vendingmachine/model/Coin.java +++ b/src/main/java/vendingmachine/model/Coin.java @@ -15,6 +15,4 @@ public enum Coin { public int getAmount() { return amount; } - - // 추가 기능 구현 } diff --git a/src/main/java/vendingmachine/model/MachineAmount.java b/src/main/java/vendingmachine/model/MachineAmount.java index a12b69ff8..3cd55baa4 100644 --- a/src/main/java/vendingmachine/model/MachineAmount.java +++ b/src/main/java/vendingmachine/model/MachineAmount.java @@ -19,8 +19,4 @@ private void validateAmount(int amount) { public int getAmount() { return amount; } - - public void decrease(int amount) { - this.amount -= amount; - } } diff --git a/src/main/java/vendingmachine/model/Name.java b/src/main/java/vendingmachine/model/Name.java index dc5d57ad9..89f3f7ba0 100644 --- a/src/main/java/vendingmachine/model/Name.java +++ b/src/main/java/vendingmachine/model/Name.java @@ -4,11 +4,11 @@ import vendingmachine.exception.ProductNameNotBlankException; public class Name { - private final String name; + private final String value; public Name(String name) { validateName(name); - this.name = name; + this.value = name; } private void validateName(String name) { @@ -17,8 +17,8 @@ private void validateName(String name) { } } - public String getName() { - return name; + public String getValue() { + return value; } @Override @@ -30,11 +30,11 @@ public boolean equals(Object other) { return false; } Name otherName = (Name) other; - return Objects.equals(getName(), otherName.getName()); + return Objects.equals(getValue(), otherName.getValue()); } @Override public int hashCode() { - return Objects.hashCode(getName()); + return Objects.hashCode(getValue()); } } diff --git a/src/main/java/vendingmachine/model/Order.java b/src/main/java/vendingmachine/model/Order.java index dddedc66d..c5cc32faf 100644 --- a/src/main/java/vendingmachine/model/Order.java +++ b/src/main/java/vendingmachine/model/Order.java @@ -8,6 +8,6 @@ public Order(Name productName) { } public Name name() { - return new Name(productName.getName()); + return new Name(productName.getValue()); } } diff --git a/src/main/java/vendingmachine/model/Price.java b/src/main/java/vendingmachine/model/Price.java index 3e3135abb..8039372b2 100644 --- a/src/main/java/vendingmachine/model/Price.java +++ b/src/main/java/vendingmachine/model/Price.java @@ -3,11 +3,11 @@ import vendingmachine.exception.ProductPriceFromatException; public class Price { - private final int price; + private final int value; public Price(int price) { validateAmount(price); - this.price = price; + this.value = price; } private void validateAmount(int price) { @@ -16,7 +16,7 @@ private void validateAmount(int price) { } } - public int getPrice() { - return price; + public int getValue() { + return value; } } diff --git a/src/main/java/vendingmachine/model/Product.java b/src/main/java/vendingmachine/model/Product.java index cbe79015d..95cf550ba 100644 --- a/src/main/java/vendingmachine/model/Product.java +++ b/src/main/java/vendingmachine/model/Product.java @@ -16,11 +16,11 @@ public Name getName() { } public boolean isBuyable(Amount amount) { - return price.getPrice() <= amount.getAmount(); + return price.getValue() <= amount.getValue(); } public int getPrice() { - return price.getPrice(); + return price.getValue(); } public void decreaseQuantity() { diff --git a/src/main/java/vendingmachine/model/VendingMachine.java b/src/main/java/vendingmachine/model/VendingMachine.java index ef1a09e2d..a55cb551d 100644 --- a/src/main/java/vendingmachine/model/VendingMachine.java +++ b/src/main/java/vendingmachine/model/VendingMachine.java @@ -16,10 +16,10 @@ public VendingMachine(Change change, Products products, Amount amount) { } public Change getChange() { - if (change.getTotalChange() <= amount.getAmount()) { + if (change.getTotalChange() <= amount.getValue()) { return change; } - return new Change(new MachineAmount(amount.getAmount())); + return new Change(new MachineAmount(amount.getValue())); } public boolean isBuyableAnyProduct() { @@ -44,6 +44,6 @@ private Product validateOrder(Order order) { } public int getAmount() { - return amount.getAmount(); + return amount.getValue(); } } diff --git a/src/main/java/vendingmachine/view/StringBuilderPrinter.java b/src/main/java/vendingmachine/view/StringBuilderPrinter.java index 4ab2ed9bd..f88619883 100644 --- a/src/main/java/vendingmachine/view/StringBuilderPrinter.java +++ b/src/main/java/vendingmachine/view/StringBuilderPrinter.java @@ -7,10 +7,6 @@ public void appendLine(String line) { stringBuilder.append(line).append('\n'); } - public void appendDivideLine() { - stringBuilder.append('\n'); - } - public void print() { System.out.println(stringBuilder); } diff --git a/src/test/java/vendingmachine/model/AmountTest.java b/src/test/java/vendingmachine/model/AmountTest.java index 853d9ee53..c72ca785e 100644 --- a/src/test/java/vendingmachine/model/AmountTest.java +++ b/src/test/java/vendingmachine/model/AmountTest.java @@ -4,7 +4,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import camp.nextstep.edu.missionutils.test.Assertions; import org.junit.jupiter.api.Test; class AmountTest { @@ -29,7 +28,7 @@ class AmountTest { assertSimpleTest(() -> { Amount amount = new Amount(10); amount.decrease(5); - assertThat(amount.getAmount()).isEqualTo(5); + assertThat(amount.getValue()).isEqualTo(5); }); } }