From dbbff11f87420c93d9a916d6b54cf8897f3c3980 Mon Sep 17 00:00:00 2001 From: HaODay Date: Thu, 1 Dec 2022 23:19:31 +0900 Subject: [PATCH 01/33] =?UTF-8?q?docs(README):=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=EB=AA=A9=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 --- docs/README.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 docs/README.md diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..5b8c30b5a --- /dev/null +++ b/docs/README.md @@ -0,0 +1,41 @@ +# 자판기 + +## 기능 목록 + + +- 자판기가 보유하고 있는 금액 입력 받기 + - 입력값 검증 + - 숫자인지 + - 10의 배수인지 + +- 자판기가 보유하고 있는 금액으로 동전을 무작위로 생성 +- 자판기가 보유하고 있는 동전 출력 + +- 상품명, 가격, 수량 입력 받기 + - 입력값 검증 + - 숫자인지 + - 100원 이상인지 + - 10원으로 나누어 떨어지는지 + +- 투입 금액 입력 받기 + - 입력값 검증 + +- 구매할 상품 입력 받기 + - 입력값 검증 + +- 잔돈 출력 + - 남은 금액이 상품의 최저 가격보다 적은 경우 + - 모든 상품이 소진된 경우 + - 잔돈을 반환할 수 없는 경우 잔돈으로 반환할 수 있는 금액만 반환 + +- +투입 금액으로는 동전을 생성하지 않는다. +잔돈을 돌려줄 때 현재 보유한 최소 개수의 동전으로 잔돈을 돌려준다. +지폐를 잔돈으로 반환하는 경우는 없다고 가정한다. +사용자가 투입한 금액으로 상품을 구매할 수 있다. +남은 금액이 상품의 최저 가격보다 적거나, 모든 상품이 소진된 경우 바로 잔돈을 돌려준다. +잔돈을 반환할 수 없는 경우 잔돈으로 반환할 수 있는 금액만 반환한다. +반환되지 않은 금액은 자판기에 남는다. +사용자가 잘못된 값을 입력할 경우 IllegalArgumentException를 발생시키고, "[ERROR]"로 시작하는 에러 메시지를 출력 후 해당 부분부터 다시 입력을 받는다. + + From d7a93812a2f7aaa093afd76c6da713565f170571 Mon Sep 17 00:00:00 2001 From: HaODay Date: Sat, 3 Dec 2022 04:29:24 +0900 Subject: [PATCH 02/33] =?UTF-8?q?docs(README):=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/docs/README.md b/docs/README.md index 5b8c30b5a..1e8da1842 100644 --- a/docs/README.md +++ b/docs/README.md @@ -8,34 +8,50 @@ - 숫자인지 - 10의 배수인지 + - 자판기가 보유하고 있는 금액으로 동전을 무작위로 생성 + + - 자판기가 보유하고 있는 동전 출력 + - 상품명, 가격, 수량 입력 받기 - 입력값 검증 - 숫자인지 - 100원 이상인지 - 10원으로 나누어 떨어지는지 + - 투입 금액 입력 받기 - 입력값 검증 + - 숫자인지 + - 양수인지 + - 구매할 상품 입력 받기 - 입력값 검증 + - 형식에 맞는지 + + +- 입력 받은 상품 구매 가능 여부 확인 + - 상품의 품절 여부 + - 잔액이 충분한지 + - 판매하는 상품인지 + +- 상품 구매 + - 남은 투입금액 계산 + - 남은 상품 수량 계산 -- 잔돈 출력 - - 남은 금액이 상품의 최저 가격보다 적은 경우 - - 모든 상품이 소진된 경우 - - 잔돈을 반환할 수 없는 경우 잔돈으로 반환할 수 있는 금액만 반환 -- -투입 금액으로는 동전을 생성하지 않는다. -잔돈을 돌려줄 때 현재 보유한 최소 개수의 동전으로 잔돈을 돌려준다. -지폐를 잔돈으로 반환하는 경우는 없다고 가정한다. -사용자가 투입한 금액으로 상품을 구매할 수 있다. -남은 금액이 상품의 최저 가격보다 적거나, 모든 상품이 소진된 경우 바로 잔돈을 돌려준다. -잔돈을 반환할 수 없는 경우 잔돈으로 반환할 수 있는 금액만 반환한다. -반환되지 않은 금액은 자판기에 남는다. -사용자가 잘못된 값을 입력할 경우 IllegalArgumentException를 발생시키고, "[ERROR]"로 시작하는 에러 메시지를 출력 후 해당 부분부터 다시 입력을 받는다. +- 잔돈 반환 여부 확인 + - 잔돈 반환 + - 남은 금액이 남은 상품의 최저 가격보다 적은 경우 + - 모든 상품이 소진된 경우 + - 잔돈 반환 X + - 상품 추가 구매 +- 잔돈 계산 + - 잔돈 전체 반환 가능 여부 확인 + - 잔돈이 자판기 보유금액보다 금액이 작은 경우 > 전체 반환 + - 잔돈이 자판기 보유금액보다 금액이 큰 경우 > 잔돈으로 반환할 수 있는 금액(보유금액)만 반환 From eb32acbc2726a029a162d2065eadf4596a7e2468 Mon Sep 17 00:00:00 2001 From: HaODay Date: Sat, 3 Dec 2022 04:54:32 +0900 Subject: [PATCH 03/33] =?UTF-8?q?feat(InputView):=20=EC=9E=90=ED=8C=90?= =?UTF-8?q?=EA=B8=B0=EA=B0=80=20=EB=B3=B4=EC=9C=A0=ED=95=98=EA=B3=A0=20?= =?UTF-8?q?=EC=9E=88=EB=8A=94=20=EA=B8=88=EC=95=A1=20=EC=9E=85=EB=A0=A5=20?= =?UTF-8?q?=EB=B0=9B=EA=B3=A0=20=EA=B2=80=EC=A6=9D=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/vendingmachine/view/InputView.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/main/java/vendingmachine/view/InputView.java diff --git a/src/main/java/vendingmachine/view/InputView.java b/src/main/java/vendingmachine/view/InputView.java new file mode 100644 index 000000000..e4fcf3487 --- /dev/null +++ b/src/main/java/vendingmachine/view/InputView.java @@ -0,0 +1,30 @@ +package vendingmachine.view; + +import camp.nextstep.edu.missionutils.Console; + +public class InputView { + + public static int readBalance() { + System.out.println("자판기가 보유하고 있는 금액을 입력해 주세요."); + int balance = 0; + try { + balance = validateNum(); + if (balance % 10 != 0) { + throw new IllegalArgumentException("보유 금액은 10의 배수여야 합니다."); + } + } catch (IllegalArgumentException e) { + System.out.println("[ERROR] " + e.getMessage()); + balance = readBalance(); + } + return balance; + } + + + private static int validateNum() throws IllegalArgumentException { + try { + return Integer.parseInt(Console.readLine()); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("보유 금액은 숫자여야 합니다."); + } + } +} From 96c7f60e1ba7a423e0fc623eea6e8e098424e1fe Mon Sep 17 00:00:00 2001 From: HaODay Date: Sat, 3 Dec 2022 04:57:09 +0900 Subject: [PATCH 04/33] =?UTF-8?q?feat(Balance):=20=EC=9E=90=ED=8C=90?= =?UTF-8?q?=EA=B8=B0=EA=B0=80=20=EB=B3=B4=EC=9C=A0=ED=95=98=EA=B3=A0=20?= =?UTF-8?q?=EC=9E=88=EB=8A=94=20=EA=B8=88=EC=95=A1=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=8F=99=EC=A0=84=EC=9D=84=20=EB=AC=B4=EC=9E=91=EC=9C=84?= =?UTF-8?q?=EB=A1=9C=20=EC=83=9D=EC=84=B1=ED=95=98=EB=8A=94=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/vendingmachine/model/Balance.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/main/java/vendingmachine/model/Balance.java diff --git a/src/main/java/vendingmachine/model/Balance.java b/src/main/java/vendingmachine/model/Balance.java new file mode 100644 index 000000000..0c087383e --- /dev/null +++ b/src/main/java/vendingmachine/model/Balance.java @@ -0,0 +1,37 @@ +package vendingmachine.model; + +import vendingmachine.Coin; + +import java.util.EnumMap; +import java.util.Map; + +import static vendingmachine.Coin.COIN_10; + +public class Balance { + private int balance; + private int inputCoins; + + private Map coins = new EnumMap<>(Coin.class); + private Map changeCoins = new EnumMap<>(Coin.class); + + public Balance(int balance) { + this.balance = balance; + } + + public Map createCoin() { + int tmp = balance; + for (Coin coin : Coin.values()) { + int num = coin.pickNumberOfCoins(tmp); + if (coin.equals(COIN_10)) { + num = tmp / COIN_10.get(); + } + coins.put(coin, num); + tmp -= num * coin.get(); + if (tmp < 0) { + tmp = 0; + } + } + return coins; + } + +} From 59a970086dda2eb8dd83d7e30d6f37d9baf58da6 Mon Sep 17 00:00:00 2001 From: HaODay Date: Sat, 3 Dec 2022 04:58:22 +0900 Subject: [PATCH 05/33] =?UTF-8?q?feat(OutputVIew):=20=EC=9E=90=ED=8C=90?= =?UTF-8?q?=EA=B8=B0=EA=B0=80=20=EB=B3=B4=EC=9C=A0=ED=95=98=EA=B3=A0=20?= =?UTF-8?q?=EC=9E=88=EB=8A=94=20=EB=8F=99=EC=A0=84=20=EC=B6=9C=EB=A0=A5?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/vendingmachine/view/OutputView.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/main/java/vendingmachine/view/OutputView.java diff --git a/src/main/java/vendingmachine/view/OutputView.java b/src/main/java/vendingmachine/view/OutputView.java new file mode 100644 index 000000000..5db9806c7 --- /dev/null +++ b/src/main/java/vendingmachine/view/OutputView.java @@ -0,0 +1,17 @@ +package vendingmachine.view; + +import vendingmachine.Coin; + +import java.util.Map; + +public class OutputView { + + public static void printBalanceCoin(Map coins) { + System.out.println("\n자판기가 보유한 동전"); + + for (Map.Entry entry : coins.entrySet()) { + System.out.println(entry.getKey().get() + "원 - " + entry.getValue() + "개"); + } + } + +} From a055ae31a84ef16755548ef687304304f62c1c64 Mon Sep 17 00:00:00 2001 From: HaODay Date: Sat, 3 Dec 2022 05:00:28 +0900 Subject: [PATCH 06/33] =?UTF-8?q?feat(InputVIew):=20=EC=83=81=ED=92=88?= =?UTF-8?q?=EB=AA=85,=20=EA=B0=80=EA=B2=A9,=20=EC=88=98=EB=9F=89=20?= =?UTF-8?q?=EC=9E=85=EB=A0=A5=20=EB=B0=9B=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/vendingmachine/view/InputView.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/vendingmachine/view/InputView.java b/src/main/java/vendingmachine/view/InputView.java index e4fcf3487..5e3ed623f 100644 --- a/src/main/java/vendingmachine/view/InputView.java +++ b/src/main/java/vendingmachine/view/InputView.java @@ -6,7 +6,7 @@ public class InputView { public static int readBalance() { System.out.println("자판기가 보유하고 있는 금액을 입력해 주세요."); - int balance = 0; + int balance; try { balance = validateNum(); if (balance % 10 != 0) { @@ -19,6 +19,11 @@ public static int readBalance() { return balance; } + public static String readProductInfo() { + System.out.println("\n상품명과 가격, 수량을 입력해 주세요."); + return Console.readLine(); + } + private static int validateNum() throws IllegalArgumentException { try { From bacd922225f05bb566d076e56dcb6468d019eae7 Mon Sep 17 00:00:00 2001 From: HaODay Date: Sat, 3 Dec 2022 05:02:30 +0900 Subject: [PATCH 07/33] =?UTF-8?q?feat(VendingMachineController):=20?= =?UTF-8?q?=EC=9E=85=EB=A0=A5=20=EB=B0=9B=EC=9D=80=20=EC=83=81=ED=92=88=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EC=A0=80=EC=9E=A5=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/VendingMachineController.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/main/java/vendingmachine/controller/VendingMachineController.java diff --git a/src/main/java/vendingmachine/controller/VendingMachineController.java b/src/main/java/vendingmachine/controller/VendingMachineController.java new file mode 100644 index 000000000..496c62f29 --- /dev/null +++ b/src/main/java/vendingmachine/controller/VendingMachineController.java @@ -0,0 +1,37 @@ +package vendingmachine.controller; + +import vendingmachine.model.Balance; +import vendingmachine.model.Product; +import vendingmachine.model.Validator; +import vendingmachine.view.InputView; +import vendingmachine.view.OutputView; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.List; + +public class VendingMachineController { + + private static Map products = new HashMap<>(); + private static int amountOfInput; + + + public static void saveProducts() { + try { + String input = InputView.readProductInfo(); + String[] str = input.split(";"); + for (String s : str) { + String[] productInfo = s.substring(1, s.length() - 1).split(","); + Validator.validateProduct(productInfo, products); + products.put(productInfo[0], new Product(productInfo[1], productInfo[2])); + } + } catch (IllegalArgumentException e) { + System.out.println("[ERROR] " + e.getMessage()); + products = new HashMap<>(); + saveProducts(); + } + } +} + From aa1c9ae942f2eed198873cea84bcec9ce48acf14 Mon Sep 17 00:00:00 2001 From: HaODay Date: Sat, 3 Dec 2022 05:03:26 +0900 Subject: [PATCH 08/33] =?UTF-8?q?feat(Validator):=20=EC=9E=85=EB=A0=A5=20?= =?UTF-8?q?=EB=B0=9B=EC=9D=80=20=EC=83=81=ED=92=88=20=EC=A0=95=EB=B3=B4=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/vendingmachine/model/Validator.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/main/java/vendingmachine/model/Validator.java diff --git a/src/main/java/vendingmachine/model/Validator.java b/src/main/java/vendingmachine/model/Validator.java new file mode 100644 index 000000000..b3be38620 --- /dev/null +++ b/src/main/java/vendingmachine/model/Validator.java @@ -0,0 +1,18 @@ +package vendingmachine.model; + +import camp.nextstep.edu.missionutils.Console; + +import java.util.Map; + +public class Validator { + + public static void validateProduct(String[] productInfo, Map products) throws IllegalArgumentException { + if (productInfo.length != 3) { + throw new IllegalArgumentException("형식에 맞지 않습니다."); + } + if (products.containsKey(productInfo[0])) { + throw new IllegalArgumentException("같은 이름의 상품은 입력할 수 없습니다."); + } + } + +} From ec3b9f6eeff550be515b0ad37097ff129c27a281 Mon Sep 17 00:00:00 2001 From: HaODay Date: Sat, 3 Dec 2022 05:05:58 +0900 Subject: [PATCH 09/33] =?UTF-8?q?feat(InputView):=20=ED=88=AC=EC=9E=85=20?= =?UTF-8?q?=EA=B8=88=EC=95=A1=20=EC=9E=85=EB=A0=A5=20=EB=B0=9B=EA=B3=A0=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/vendingmachine/view/InputView.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main/java/vendingmachine/view/InputView.java b/src/main/java/vendingmachine/view/InputView.java index 5e3ed623f..2afe088e4 100644 --- a/src/main/java/vendingmachine/view/InputView.java +++ b/src/main/java/vendingmachine/view/InputView.java @@ -24,12 +24,26 @@ public static String readProductInfo() { return Console.readLine(); } + public static int readAmountOfInput() { + System.out.println("\n투입 금액을 입력해 주세요."); + int amountOfInput = 0; + try { + amountOfInput = validateNum(); + if (amountOfInput < 0) { + throw new IllegalArgumentException("투입 금액은 음수일 수 없습니다."); + } + } catch (IllegalArgumentException e) { + System.out.println("[ERROR] " + e.getMessage()); + amountOfInput = readAmountOfInput(); + } + return amountOfInput; + } private static int validateNum() throws IllegalArgumentException { try { return Integer.parseInt(Console.readLine()); } catch (NumberFormatException e) { - throw new IllegalArgumentException("보유 금액은 숫자여야 합니다."); + throw new IllegalArgumentException("입력값은 숫자여야 합니다."); } } } From 7974545aefcde6e85305c88ce972d1a9602cb9da Mon Sep 17 00:00:00 2001 From: HaODay Date: Sat, 3 Dec 2022 05:06:45 +0900 Subject: [PATCH 10/33] =?UTF-8?q?feat(InputView):=20=EA=B5=AC=EB=A7=A4=20?= =?UTF-8?q?=EC=83=81=ED=92=88=20=EC=9E=85=EB=A0=A5=20=EB=B0=9B=EB=8A=94=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/vendingmachine/view/InputView.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/vendingmachine/view/InputView.java b/src/main/java/vendingmachine/view/InputView.java index 2afe088e4..e1e76c309 100644 --- a/src/main/java/vendingmachine/view/InputView.java +++ b/src/main/java/vendingmachine/view/InputView.java @@ -39,6 +39,11 @@ public static int readAmountOfInput() { return amountOfInput; } + public static String readBuyingProduct() { + System.out.println("구매할 상품명을 입력해 주세요."); + return Console.readLine(); + } + private static int validateNum() throws IllegalArgumentException { try { return Integer.parseInt(Console.readLine()); From 93e8abdc0baf02ca6b8444f26c5cc3dcdd2d6b9a Mon Sep 17 00:00:00 2001 From: HaODay Date: Sat, 3 Dec 2022 05:08:05 +0900 Subject: [PATCH 11/33] =?UTF-8?q?feat(Validator):=20=EC=9E=85=EB=A0=A5=20?= =?UTF-8?q?=EB=B0=9B=EC=9D=80=20=EA=B5=AC=EB=A7=A4=20=EC=83=81=ED=92=88=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/vendingmachine/model/Validator.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/vendingmachine/model/Validator.java b/src/main/java/vendingmachine/model/Validator.java index b3be38620..9bf3fd80d 100644 --- a/src/main/java/vendingmachine/model/Validator.java +++ b/src/main/java/vendingmachine/model/Validator.java @@ -14,5 +14,15 @@ public static void validateProduct(String[] productInfo, Map pr throw new IllegalArgumentException("같은 이름의 상품은 입력할 수 없습니다."); } } - + public static void validateBuyingProduct(String buyingProduct, Map products, int amountOfInput) throws IllegalArgumentException { + if (!products.containsKey(buyingProduct)) { + throw new IllegalArgumentException("판매하지 않는 상품입니다."); + } + if (products.get(buyingProduct).price.get() > amountOfInput) { + throw new IllegalArgumentException("잔액이 부족합니다."); + } + if (products.get(buyingProduct).amount <= 0) { + throw new IllegalArgumentException("상품이 품절되었습니다."); + } + } } From bfe1a1037560b7ac07761effc03b7e6d6bf470a9 Mon Sep 17 00:00:00 2001 From: HaODay Date: Sat, 3 Dec 2022 05:11:08 +0900 Subject: [PATCH 12/33] =?UTF-8?q?feat(VendingMachineController):=20?= =?UTF-8?q?=EC=83=81=ED=92=88=20=EA=B5=AC=EB=A7=A4=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 남은 투입금액 계산 - 남은 상품 수량 계산 --- .../controller/VendingMachineController.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/vendingmachine/controller/VendingMachineController.java b/src/main/java/vendingmachine/controller/VendingMachineController.java index 496c62f29..e2d71ee38 100644 --- a/src/main/java/vendingmachine/controller/VendingMachineController.java +++ b/src/main/java/vendingmachine/controller/VendingMachineController.java @@ -16,7 +16,18 @@ public class VendingMachineController { private static Map products = new HashMap<>(); private static int amountOfInput; - + + private static void buy() { + try { + String buyingProduct = InputView.readBuyingProduct(); + Validator.validateBuyingProduct(buyingProduct, products, amountOfInput); + amountOfInput -= products.get(buyingProduct).price.get(); + products.get(buyingProduct).amount--; + } catch (IllegalArgumentException e) { + System.out.println("[ERROR] " + e.getMessage()); + buy(); + } + } public static void saveProducts() { try { From 4b3c3f37d4220c6514898c740ad27e4f299ed6f0 Mon Sep 17 00:00:00 2001 From: HaODay Date: Sat, 3 Dec 2022 05:13:06 +0900 Subject: [PATCH 13/33] =?UTF-8?q?feat(VendingMachineController):=20?= =?UTF-8?q?=EC=9E=94=EB=8F=88=20=EB=B0=98=ED=99=98=20=EC=97=AC=EB=B6=80=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 남은 금액이 남은 상품의 최저 가격보다 적은 경우 - 모든 상품이 소진된 경우 잔돈 반환 --- .../controller/VendingMachineController.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/java/vendingmachine/controller/VendingMachineController.java b/src/main/java/vendingmachine/controller/VendingMachineController.java index e2d71ee38..a6873c434 100644 --- a/src/main/java/vendingmachine/controller/VendingMachineController.java +++ b/src/main/java/vendingmachine/controller/VendingMachineController.java @@ -17,6 +17,20 @@ public class VendingMachineController { private static Map products = new HashMap<>(); private static int amountOfInput; + private static boolean canBuy() { + List leftProductsPrice = new ArrayList(); + for (Map.Entry entry : products.entrySet()) { + if (entry.getValue().amount != 0) { + leftProductsPrice.add(entry.getValue().price.get()); + } + } + Collections.sort(leftProductsPrice); + if (leftProductsPrice.isEmpty()) { + return false; + } + return amountOfInput >= leftProductsPrice.get(0); + } + private static void buy() { try { String buyingProduct = InputView.readBuyingProduct(); From d673a0105254b2bdbf0839f958ad52bd5e5d154e Mon Sep 17 00:00:00 2001 From: HaODay Date: Sat, 3 Dec 2022 05:14:06 +0900 Subject: [PATCH 14/33] =?UTF-8?q?feat(Balance):=20=EC=9E=94=EB=8F=88=20?= =?UTF-8?q?=EA=B3=84=EC=82=B0=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/vendingmachine/model/Balance.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/java/vendingmachine/model/Balance.java b/src/main/java/vendingmachine/model/Balance.java index 0c087383e..ec8664ac9 100644 --- a/src/main/java/vendingmachine/model/Balance.java +++ b/src/main/java/vendingmachine/model/Balance.java @@ -33,5 +33,17 @@ public Map createCoin() { } return coins; } - + public Map calculateChangeCoin(int change) { + if (change > balance) { + return coins; + } + for (Coin coin : Coin.values()) { + int numberOfBalanceCoin = coins.get(coin); + int neededCoins = change / coin.get(); + inputCoins = Math.min(neededCoins, numberOfBalanceCoin); + changeCoins.put(coin, inputCoins); + change -= coin.get() * inputCoins; + } + return changeCoins; + } } From 9de7ea49f4763af7f27c4695635205ae6616bc54 Mon Sep 17 00:00:00 2001 From: HaODay Date: Sat, 3 Dec 2022 05:14:58 +0900 Subject: [PATCH 15/33] =?UTF-8?q?feat(OutputView):=20=ED=88=AC=EC=9E=85?= =?UTF-8?q?=EA=B8=88=EC=95=A1,=20=EC=9E=94=EB=8F=88=20=EC=B6=9C=EB=A0=A5?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/vendingmachine/view/OutputView.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/vendingmachine/view/OutputView.java b/src/main/java/vendingmachine/view/OutputView.java index 5db9806c7..208560202 100644 --- a/src/main/java/vendingmachine/view/OutputView.java +++ b/src/main/java/vendingmachine/view/OutputView.java @@ -13,5 +13,16 @@ public static void printBalanceCoin(Map coins) { System.out.println(entry.getKey().get() + "원 - " + entry.getValue() + "개"); } } - + public static void printAmountOfInput(int amountOfInput) { + System.out.println("\n투입 금액: " + amountOfInput + "원"); + } + + public static void printChange(Map balanceCoins) { + System.out.println("잔돈"); + for (Map.Entry entry : balanceCoins.entrySet()) { + if (entry.getValue() != 0) { + System.out.println(entry.getKey().get() + "원 - " + entry.getValue() + "개"); + } + } + } } From 507b4c84c5dfca311ba74f1498091eac5b682cb8 Mon Sep 17 00:00:00 2001 From: HaODay Date: Sat, 3 Dec 2022 05:15:40 +0900 Subject: [PATCH 16/33] =?UTF-8?q?feat(VendingMachineController):=20?= =?UTF-8?q?=EC=9E=90=ED=8C=90=EA=B8=B0=20=EC=9E=91=EB=8F=99=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/VendingMachineController.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/vendingmachine/controller/VendingMachineController.java b/src/main/java/vendingmachine/controller/VendingMachineController.java index a6873c434..c4f299198 100644 --- a/src/main/java/vendingmachine/controller/VendingMachineController.java +++ b/src/main/java/vendingmachine/controller/VendingMachineController.java @@ -17,6 +17,19 @@ public class VendingMachineController { private static Map products = new HashMap<>(); private static int amountOfInput; + public static void run() { + Balance balance = new Balance(InputView.readBalance()); + OutputView.printBalanceCoin(balance.createCoin()); + saveProducts(); + amountOfInput = InputView.readAmountOfInput(); + OutputView.printAmountOfInput(amountOfInput); + while (canBuy()) { + buy(); + OutputView.printAmountOfInput(amountOfInput); + } + OutputView.printChange(balance.calculateChangeCoin(amountOfInput)); + } + private static boolean canBuy() { List leftProductsPrice = new ArrayList(); for (Map.Entry entry : products.entrySet()) { From 1951b18f4b70059c863ff52eb7f734a53139b188 Mon Sep 17 00:00:00 2001 From: HaODay Date: Sat, 3 Dec 2022 05:16:36 +0900 Subject: [PATCH 17/33] =?UTF-8?q?feat:=20=EB=8F=99=EC=A0=84,=20=EA=B0=80?= =?UTF-8?q?=EA=B2=A9,=20=EC=83=81=ED=92=88=20=ED=81=B4=EB=9E=98=EC=8A=A4?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/vendingmachine/Coin.java | 17 +++++++++++- src/main/java/vendingmachine/model/Price.java | 27 +++++++++++++++++++ .../java/vendingmachine/model/Product.java | 18 +++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 src/main/java/vendingmachine/model/Price.java create mode 100644 src/main/java/vendingmachine/model/Product.java diff --git a/src/main/java/vendingmachine/Coin.java b/src/main/java/vendingmachine/Coin.java index c76293fbc..13fb94588 100644 --- a/src/main/java/vendingmachine/Coin.java +++ b/src/main/java/vendingmachine/Coin.java @@ -1,5 +1,10 @@ package vendingmachine; +import camp.nextstep.edu.missionutils.Randoms; + +import java.util.ArrayList; +import java.util.List; + public enum Coin { COIN_500(500), COIN_100(100), @@ -12,5 +17,15 @@ public enum Coin { this.amount = amount; } - // 추가 기능 구현 + public int get() { + return amount; + } + + public int pickNumberOfCoins(int balance) { + List numbers = new ArrayList<>(); + for (int i = 0; i <= (balance / amount); i++) { + numbers.add(i); + } + return Randoms.pickNumberInList(numbers); + } } diff --git a/src/main/java/vendingmachine/model/Price.java b/src/main/java/vendingmachine/model/Price.java new file mode 100644 index 000000000..cdd992e38 --- /dev/null +++ b/src/main/java/vendingmachine/model/Price.java @@ -0,0 +1,27 @@ +package vendingmachine.model; + +public class Price { + private int price; + + Price(String price) { + try { + this.price = Integer.parseInt(price); + validate(this.price); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("가격은 숫자여야 합니다."); + } + } + + public int get() { + return price; + } + + private void validate(int price) { + if (price < 100) { + throw new IllegalArgumentException("가격은 100원 이상이어야 합니다."); + } + if (price % 10 != 0) { + throw new IllegalArgumentException("가격은 10원으로 나누어떨어져야 합니다."); + } + } +} diff --git a/src/main/java/vendingmachine/model/Product.java b/src/main/java/vendingmachine/model/Product.java new file mode 100644 index 000000000..1436f8558 --- /dev/null +++ b/src/main/java/vendingmachine/model/Product.java @@ -0,0 +1,18 @@ +package vendingmachine.model; + +public class Product { + public Price price; + public int amount; + + public Product(String price, String amount) throws IllegalArgumentException { + this.price = new Price(price); + try { + this.amount = Integer.parseInt(amount); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("수량은 숫자여야 합니다."); + } + if (this.amount < 0) { + throw new IllegalArgumentException("수량은 음수가 아닙니다."); + } + } +} From 93f76162106f4ecf2f85b90ae2cf6888a790bb59 Mon Sep 17 00:00:00 2001 From: HaODay Date: Sat, 3 Dec 2022 05:17:29 +0900 Subject: [PATCH 18/33] =?UTF-8?q?docs(README):=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/README.md b/docs/README.md index 1e8da1842..dc00db104 100644 --- a/docs/README.md +++ b/docs/README.md @@ -29,8 +29,6 @@ - 구매할 상품 입력 받기 - - 입력값 검증 - - 형식에 맞는지 - 입력 받은 상품 구매 가능 여부 확인 @@ -38,6 +36,7 @@ - 잔액이 충분한지 - 판매하는 상품인지 + - 상품 구매 - 남은 투입금액 계산 - 남은 상품 수량 계산 @@ -55,3 +54,5 @@ - 잔돈 전체 반환 가능 여부 확인 - 잔돈이 자판기 보유금액보다 금액이 작은 경우 > 전체 반환 - 잔돈이 자판기 보유금액보다 금액이 큰 경우 > 잔돈으로 반환할 수 있는 금액(보유금액)만 반환 + +- 잔돈 출력 From d4d7b1a40781fbfc80b292f993acbb0b6fc23098 Mon Sep 17 00:00:00 2001 From: HaODay Date: Sat, 3 Dec 2022 05:18:21 +0900 Subject: [PATCH 19/33] =?UTF-8?q?feat(Application):=20=EC=9E=90=ED=8C=90?= =?UTF-8?q?=EA=B8=B0=20=EC=9E=91=EB=8F=99=ED=95=98=EB=8A=94=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/vendingmachine/Application.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/vendingmachine/Application.java b/src/main/java/vendingmachine/Application.java index 9d3be447b..cdef6303f 100644 --- a/src/main/java/vendingmachine/Application.java +++ b/src/main/java/vendingmachine/Application.java @@ -1,7 +1,9 @@ package vendingmachine; +import vendingmachine.controller.VendingMachineController; + public class Application { public static void main(String[] args) { - // TODO: 프로그램 구현 + VendingMachineController.run(); } } From 2c530a5c3a4668446f608c785291bf2d1d206526 Mon Sep 17 00:00:00 2001 From: HaODay Date: Sat, 3 Dec 2022 05:33:03 +0900 Subject: [PATCH 20/33] =?UTF-8?q?test(ApplicationTest):=20=EB=B3=B4?= =?UTF-8?q?=EC=9C=A0=20=EA=B8=88=EC=95=A1=20=EC=98=88=EC=99=B8=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/vendingmachine/ApplicationTest.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/test/java/vendingmachine/ApplicationTest.java b/src/test/java/vendingmachine/ApplicationTest.java index 4bec0f51e..4c19a580b 100644 --- a/src/test/java/vendingmachine/ApplicationTest.java +++ b/src/test/java/vendingmachine/ApplicationTest.java @@ -1,7 +1,11 @@ package vendingmachine; import camp.nextstep.edu.missionutils.test.NsTest; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import static camp.nextstep.edu.missionutils.test.Assertions.assertRandomNumberInListTest; import static camp.nextstep.edu.missionutils.test.Assertions.assertSimpleTest; @@ -24,11 +28,13 @@ class ApplicationTest extends NsTest { ); } - @Test - void 예외_테스트() { + @ParameterizedTest + @ValueSource(strings={"-1","25","s"}) + @DisplayName("보유 금액 예외 테스트") + void 예외_테스트(String balance) { assertSimpleTest( () -> { - runException("-1"); + runException(balance); assertThat(output()).contains(ERROR_MESSAGE); } ); From 4cbd3b8aac6ef18054e468a1371c8eb3568861eb Mon Sep 17 00:00:00 2001 From: HaODay Date: Sat, 3 Dec 2022 05:43:05 +0900 Subject: [PATCH 21/33] =?UTF-8?q?test(CoinTest):=20=EB=8F=99=EC=A0=84=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=EC=9D=B4=20=EC=A0=95=EC=83=81=EC=A0=81?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=90=98=EB=8A=94=EC=A7=80=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/vendingmachine/CoinTest.java | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/test/java/vendingmachine/CoinTest.java diff --git a/src/test/java/vendingmachine/CoinTest.java b/src/test/java/vendingmachine/CoinTest.java new file mode 100644 index 000000000..87f8d51cc --- /dev/null +++ b/src/test/java/vendingmachine/CoinTest.java @@ -0,0 +1,26 @@ +package vendingmachine; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import vendingmachine.model.Balance; + +import java.util.Map; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +public class CoinTest { + + @ParameterizedTest + @ValueSource(ints = {500, 30410, 391740}) + @DisplayName("동전 생성 테스트") + void createCoinTest(int input) { + Balance balance = new Balance(input); + Map coins = balance.createCoin(); + int sum = 0; + for (Coin coin : Coin.values()) { + sum += coin.get() * coins.get(coin); + } + assertThat(sum).isEqualTo(input); + } +} From dee099a43ee0c6edc02dec49996edb40751e1306 Mon Sep 17 00:00:00 2001 From: HaODay Date: Mon, 12 Dec 2022 19:26:44 +0900 Subject: [PATCH 22/33] =?UTF-8?q?fix:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=ED=86=B5=EA=B3=BC=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20Coin,=20Balance=20=EC=BD=94=EB=93=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/vendingmachine/Coin.java | 17 ++++----- .../java/vendingmachine/model/Balance.java | 37 ++++++++++--------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/main/java/vendingmachine/Coin.java b/src/main/java/vendingmachine/Coin.java index 13fb94588..d654f754c 100644 --- a/src/main/java/vendingmachine/Coin.java +++ b/src/main/java/vendingmachine/Coin.java @@ -1,10 +1,5 @@ package vendingmachine; -import camp.nextstep.edu.missionutils.Randoms; - -import java.util.ArrayList; -import java.util.List; - public enum Coin { COIN_500(500), COIN_100(100), @@ -21,11 +16,13 @@ public int get() { return amount; } - public int pickNumberOfCoins(int balance) { - List numbers = new ArrayList<>(); - for (int i = 0; i <= (balance / amount); i++) { - numbers.add(i); + public static Coin getEqualCoin(int amount) { + for (Coin coin : Coin.values()) { + if (coin.amount == amount) { + return coin; + } } - return Randoms.pickNumberInList(numbers); + return null; } + } diff --git a/src/main/java/vendingmachine/model/Balance.java b/src/main/java/vendingmachine/model/Balance.java index ec8664ac9..5157f8f59 100644 --- a/src/main/java/vendingmachine/model/Balance.java +++ b/src/main/java/vendingmachine/model/Balance.java @@ -1,38 +1,41 @@ package vendingmachine.model; +import camp.nextstep.edu.missionutils.Randoms; import vendingmachine.Coin; +import java.util.ArrayList; import java.util.EnumMap; +import java.util.List; import java.util.Map; -import static vendingmachine.Coin.COIN_10; - public class Balance { - private int balance; - private int inputCoins; - - private Map coins = new EnumMap<>(Coin.class); - private Map changeCoins = new EnumMap<>(Coin.class); + private final int balance; + List numbers = new ArrayList<>(); + private final Map coins = new EnumMap<>(Coin.class); + private final Map changeCoins = new EnumMap<>(Coin.class); public Balance(int balance) { this.balance = balance; + for (Coin coin : Coin.values()) { + numbers.add(coin.get()); + coins.put(coin, 0); + } } public Map createCoin() { int tmp = balance; - for (Coin coin : Coin.values()) { - int num = coin.pickNumberOfCoins(tmp); - if (coin.equals(COIN_10)) { - num = tmp / COIN_10.get(); - } - coins.put(coin, num); - tmp -= num * coin.get(); - if (tmp < 0) { - tmp = 0; + + while (tmp > 0) { + int randomCoinAmount = Randoms.pickNumberInList(numbers); + if (tmp >= randomCoinAmount) { + Coin key = Coin.getEqualCoin(randomCoinAmount); + coins.put(key, coins.get(key) + 1); + tmp = tmp - randomCoinAmount; } } return coins; } + public Map calculateChangeCoin(int change) { if (change > balance) { return coins; @@ -40,7 +43,7 @@ public Map calculateChangeCoin(int change) { for (Coin coin : Coin.values()) { int numberOfBalanceCoin = coins.get(coin); int neededCoins = change / coin.get(); - inputCoins = Math.min(neededCoins, numberOfBalanceCoin); + int inputCoins = Math.min(neededCoins, numberOfBalanceCoin); changeCoins.put(coin, inputCoins); change -= coin.get() * inputCoins; } From ec2a16396e2ab3d2237b3f06462a80a29123ff23 Mon Sep 17 00:00:00 2001 From: HaODay Date: Mon, 12 Dec 2022 19:27:18 +0900 Subject: [PATCH 23/33] =?UTF-8?q?refactor(Price):=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EC=BD=94=EB=93=9C=20Validator=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/vendingmachine/model/Price.java | 23 +++++-------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/src/main/java/vendingmachine/model/Price.java b/src/main/java/vendingmachine/model/Price.java index cdd992e38..43fd330a2 100644 --- a/src/main/java/vendingmachine/model/Price.java +++ b/src/main/java/vendingmachine/model/Price.java @@ -1,27 +1,16 @@ package vendingmachine.model; -public class Price { - private int price; +import static vendingmachine.model.Validator.validateNum; +import static vendingmachine.model.Validator.validatePrice; +public class Price { + private final int price; Price(String price) { - try { - this.price = Integer.parseInt(price); - validate(this.price); - } catch (NumberFormatException e) { - throw new IllegalArgumentException("가격은 숫자여야 합니다."); - } + this.price = validateNum(price); + validatePrice(this.price); } public int get() { return price; } - - private void validate(int price) { - if (price < 100) { - throw new IllegalArgumentException("가격은 100원 이상이어야 합니다."); - } - if (price % 10 != 0) { - throw new IllegalArgumentException("가격은 10원으로 나누어떨어져야 합니다."); - } - } } From 9345282c66eb68a2070613ab0658c825db5814c1 Mon Sep 17 00:00:00 2001 From: HaODay Date: Mon, 12 Dec 2022 19:27:43 +0900 Subject: [PATCH 24/33] =?UTF-8?q?refactor(Product):=20=EA=B2=80=EC=A6=9D?= =?UTF-8?q?=20=EA=B4=80=EB=A0=A8=20=EC=BD=94=EB=93=9C=20Validator=EB=A1=9C?= =?UTF-8?q?=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/vendingmachine/model/Product.java | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/main/java/vendingmachine/model/Product.java b/src/main/java/vendingmachine/model/Product.java index 1436f8558..7c127cf69 100644 --- a/src/main/java/vendingmachine/model/Product.java +++ b/src/main/java/vendingmachine/model/Product.java @@ -1,18 +1,27 @@ package vendingmachine.model; +import static vendingmachine.model.Validator.validateNegative; +import static vendingmachine.model.Validator.validateNum; + public class Product { - public Price price; - public int amount; + private final Price price; + private int amount; public Product(String price, String amount) throws IllegalArgumentException { this.price = new Price(price); - try { - this.amount = Integer.parseInt(amount); - } catch (NumberFormatException e) { - throw new IllegalArgumentException("수량은 숫자여야 합니다."); - } - if (this.amount < 0) { - throw new IllegalArgumentException("수량은 음수가 아닙니다."); - } + this.amount = validateNum(amount); + validateNegative(this.amount); + } + + public boolean stockIsLeft() { + return amount > 0; + } + + public void reduceAmount() { + amount--; + } + + public int getPrice() { + return price.get(); } } From 4ef0101e158a6b424e2fa7bad84b32dcd8410a4f Mon Sep 17 00:00:00 2001 From: HaODay Date: Mon, 12 Dec 2022 19:28:35 +0900 Subject: [PATCH 25/33] =?UTF-8?q?refactor(ErrorMessage):=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=20=EC=97=90=EB=9F=AC=20=EB=A9=94=EC=8B=9C=EC=A7=80=20?= =?UTF-8?q?=EC=97=B4=EA=B1=B0=ED=98=95=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../util/message/ErrorMessage.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/main/java/vendingmachine/util/message/ErrorMessage.java diff --git a/src/main/java/vendingmachine/util/message/ErrorMessage.java b/src/main/java/vendingmachine/util/message/ErrorMessage.java new file mode 100644 index 000000000..51ceec9e1 --- /dev/null +++ b/src/main/java/vendingmachine/util/message/ErrorMessage.java @@ -0,0 +1,23 @@ +package vendingmachine.util.message; + +import static vendingmachine.util.NumberConsts.MIN_PRICE; +import static vendingmachine.util.NumberConsts.MIN_UNIT; + +public enum ErrorMessage { + ERROR_MESSAGE("[ERROR] "), + INVALID_PRICE(String.format("%d원 이상이어야 합니다.", MIN_PRICE)), + INVALID_UNIT(String.format("%d원으로 나누어떨어져야 합니다.", MIN_UNIT)), + INVALID_FORMAT("형식에 맞지 않습니다."), + NOT_NUMBER("형식에 맞지 않습니다."), + IS_NEGATIVE("음수를 입력할 수 없습니다."); + + private final String message; + + ErrorMessage(String message) { + this.message = message; + } + + public String fullMessage() { + return ERROR_MESSAGE.message + message; + } +} From 356d745339b19cb7f87cf0967515d580561d7d9f Mon Sep 17 00:00:00 2001 From: HaODay Date: Mon, 12 Dec 2022 19:29:03 +0900 Subject: [PATCH 26/33] =?UTF-8?q?refactor(ProductErrorMessage):=20?= =?UTF-8?q?=EC=83=81=ED=92=88=20=EA=B4=80=EB=A0=A8=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EB=A9=94=EC=8B=9C=EC=A7=80=20=EC=97=B4=EA=B1=B0=ED=98=95=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../util/message/ProductErrorMessage.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/main/java/vendingmachine/util/message/ProductErrorMessage.java diff --git a/src/main/java/vendingmachine/util/message/ProductErrorMessage.java b/src/main/java/vendingmachine/util/message/ProductErrorMessage.java new file mode 100644 index 000000000..822def938 --- /dev/null +++ b/src/main/java/vendingmachine/util/message/ProductErrorMessage.java @@ -0,0 +1,19 @@ +package vendingmachine.util.message; + +public enum ProductErrorMessage { + ERROR_MESSAGE("[ERROR] "), + INVALID_NAME("같은 이름의 상품은 입력할 수 없습니다."), + NOT_FOR_SALE("판매하지 않는 상품입니다."), + INSUFFICIENT_BALANCE("잔액이 부족합니다."), + OUT_OF_STOCK("상품이 품절되었습니다."); + + private final String message; + + ProductErrorMessage(String message) { + this.message = message; + } + + public String fullMessage() { + return ERROR_MESSAGE.message + message; + } +} From f568e50546e52876cabcb2c7cd5818e4a5b21dba Mon Sep 17 00:00:00 2001 From: HaODay Date: Mon, 12 Dec 2022 19:29:52 +0900 Subject: [PATCH 27/33] =?UTF-8?q?refactor(InputMessage):=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=20=EC=95=88=EB=82=B4=20=EB=A9=94=EC=8B=9C=EC=A7=80=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EC=97=B4=EA=B1=B0=ED=98=95=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../util/message/InputMessage.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/main/java/vendingmachine/util/message/InputMessage.java diff --git a/src/main/java/vendingmachine/util/message/InputMessage.java b/src/main/java/vendingmachine/util/message/InputMessage.java new file mode 100644 index 000000000..914bfbdc7 --- /dev/null +++ b/src/main/java/vendingmachine/util/message/InputMessage.java @@ -0,0 +1,21 @@ +package vendingmachine.util.message; + +public enum InputMessage { + + BALANCE("자판기가 보유하고 있는 금액을 입력해 주세요."), + PRODUCT_INFO("\n상품명과 가격, 수량을 입력해 주세요."), + AMOUNT_OF_INPUT("\n투입 금액을 입력해 주세요."), + BUYING_PRODUCT("구매할 상품명을 입력해 주세요."); + + public final String message; + + InputMessage(String message) { + this.message = message; + } + + public String fullMessage() { + return message; + } + +} + From 0e3ba711540c304104c94bba39050b38afec6bef Mon Sep 17 00:00:00 2001 From: HaODay Date: Mon, 12 Dec 2022 19:30:52 +0900 Subject: [PATCH 28/33] =?UTF-8?q?refactor(NumberConsts):=20=EC=83=81?= =?UTF-8?q?=EC=88=98=20=EB=AA=A8=EC=9D=8C=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/vendingmachine/util/NumberConsts.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/main/java/vendingmachine/util/NumberConsts.java diff --git a/src/main/java/vendingmachine/util/NumberConsts.java b/src/main/java/vendingmachine/util/NumberConsts.java new file mode 100644 index 000000000..35951c73d --- /dev/null +++ b/src/main/java/vendingmachine/util/NumberConsts.java @@ -0,0 +1,7 @@ +package vendingmachine.util; + +public class NumberConsts { + public static final int MIN_PRICE = 100; + public static final int MIN_UNIT = 10; + public static final int NUMBER_OF_ELEMENTS = 3; +} From 0885bd8ed5ee1b20d54f8b2f053d1c1c424b1cb2 Mon Sep 17 00:00:00 2001 From: HaODay Date: Mon, 12 Dec 2022 19:31:26 +0900 Subject: [PATCH 29/33] =?UTF-8?q?chore:=20=EC=9E=91=EC=9D=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=EC=82=AC=ED=95=AD=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/vendingmachine/view/OutputView.java | 1 + src/test/java/vendingmachine/ApplicationTest.java | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/vendingmachine/view/OutputView.java b/src/main/java/vendingmachine/view/OutputView.java index 208560202..5ad2a88ac 100644 --- a/src/main/java/vendingmachine/view/OutputView.java +++ b/src/main/java/vendingmachine/view/OutputView.java @@ -13,6 +13,7 @@ public static void printBalanceCoin(Map coins) { System.out.println(entry.getKey().get() + "원 - " + entry.getValue() + "개"); } } + public static void printAmountOfInput(int amountOfInput) { System.out.println("\n투입 금액: " + amountOfInput + "원"); } diff --git a/src/test/java/vendingmachine/ApplicationTest.java b/src/test/java/vendingmachine/ApplicationTest.java index 4c19a580b..46b6d6a87 100644 --- a/src/test/java/vendingmachine/ApplicationTest.java +++ b/src/test/java/vendingmachine/ApplicationTest.java @@ -1,15 +1,16 @@ package vendingmachine; import camp.nextstep.edu.missionutils.test.NsTest; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import vendingmachine.controller.VendingMachineController; import static camp.nextstep.edu.missionutils.test.Assertions.assertRandomNumberInListTest; import static camp.nextstep.edu.missionutils.test.Assertions.assertSimpleTest; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; class ApplicationTest extends NsTest { private static final String ERROR_MESSAGE = "[ERROR]"; @@ -31,7 +32,7 @@ class ApplicationTest extends NsTest { @ParameterizedTest @ValueSource(strings={"-1","25","s"}) @DisplayName("보유 금액 예외 테스트") - void 예외_테스트(String balance) { + void balanceExceptionTest(String balance) { assertSimpleTest( () -> { runException(balance); From d8d3fc9a51115eacbbdc520f47d33a5e472d96f4 Mon Sep 17 00:00:00 2001 From: HaODay Date: Mon, 12 Dec 2022 19:32:04 +0900 Subject: [PATCH 30/33] =?UTF-8?q?refactor(Validator):=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=ED=95=9C=20=EC=97=B4=EA=B1=B0=20=EC=83=81=EC=88=98=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/vendingmachine/model/Validator.java | 75 ++++++++++++++++--- 1 file changed, 64 insertions(+), 11 deletions(-) diff --git a/src/main/java/vendingmachine/model/Validator.java b/src/main/java/vendingmachine/model/Validator.java index 9bf3fd80d..8977a8617 100644 --- a/src/main/java/vendingmachine/model/Validator.java +++ b/src/main/java/vendingmachine/model/Validator.java @@ -1,28 +1,81 @@ package vendingmachine.model; -import camp.nextstep.edu.missionutils.Console; - import java.util.Map; +import static vendingmachine.util.NumberConsts.MIN_PRICE; +import static vendingmachine.util.NumberConsts.MIN_UNIT; +import static vendingmachine.util.NumberConsts.NUMBER_OF_ELEMENTS; +import static vendingmachine.util.message.ErrorMessage.INVALID_FORMAT; +import static vendingmachine.util.message.ErrorMessage.INVALID_PRICE; +import static vendingmachine.util.message.ErrorMessage.INVALID_UNIT; +import static vendingmachine.util.message.ErrorMessage.IS_NEGATIVE; +import static vendingmachine.util.message.ErrorMessage.NOT_NUMBER; +import static vendingmachine.util.message.ProductErrorMessage.INSUFFICIENT_BALANCE; +import static vendingmachine.util.message.ProductErrorMessage.INVALID_NAME; +import static vendingmachine.util.message.ProductErrorMessage.NOT_FOR_SALE; +import static vendingmachine.util.message.ProductErrorMessage.OUT_OF_STOCK; + public class Validator { + public static int validateBalance(String input) throws IllegalArgumentException { + int balance = validateNum(input); + validateNegative(balance); + validateInvalidUnit(balance); + return balance; + } + + public static int validateAmountOfInput(String input) throws IllegalArgumentException { + int amountOfInput = validateNum(input); + validateNegative(amountOfInput); + return amountOfInput; + } + + public static void validatePrice(int price) { + if (price < MIN_PRICE) { + throw new IllegalArgumentException(INVALID_PRICE.fullMessage()); + } + validateInvalidUnit(price); + } + public static void validateProduct(String[] productInfo, Map products) throws IllegalArgumentException { - if (productInfo.length != 3) { - throw new IllegalArgumentException("형식에 맞지 않습니다."); + if (productInfo.length != NUMBER_OF_ELEMENTS) { + throw new IllegalArgumentException(INVALID_FORMAT.fullMessage()); } if (products.containsKey(productInfo[0])) { - throw new IllegalArgumentException("같은 이름의 상품은 입력할 수 없습니다."); + throw new IllegalArgumentException(INVALID_NAME.fullMessage()); } } - public static void validateBuyingProduct(String buyingProduct, Map products, int amountOfInput) throws IllegalArgumentException { + + public static void validateBuyingProduct(String buyingProduct, Map products, + int amountOfInput) throws IllegalArgumentException { if (!products.containsKey(buyingProduct)) { - throw new IllegalArgumentException("판매하지 않는 상품입니다."); + throw new IllegalArgumentException(NOT_FOR_SALE.fullMessage()); } - if (products.get(buyingProduct).price.get() > amountOfInput) { - throw new IllegalArgumentException("잔액이 부족합니다."); + if (products.get(buyingProduct).getPrice() > amountOfInput) { + throw new IllegalArgumentException(INSUFFICIENT_BALANCE.fullMessage()); } - if (products.get(buyingProduct).amount <= 0) { - throw new IllegalArgumentException("상품이 품절되었습니다."); + if (!products.get(buyingProduct).stockIsLeft()) { + throw new IllegalArgumentException(OUT_OF_STOCK.fullMessage()); + } + } + + public static int validateNum(String input) throws IllegalArgumentException { + try { + return Integer.parseInt(input); + } catch (NumberFormatException e) { + throw new IllegalArgumentException(NOT_NUMBER.fullMessage()); + } + } + + public static void validateNegative(int num) throws IllegalArgumentException { + if (num < 0) { + throw new IllegalArgumentException(IS_NEGATIVE.fullMessage()); + } + } + + public static void validateInvalidUnit(int num) throws IllegalArgumentException { + if (num % MIN_UNIT != 0) { + throw new IllegalArgumentException(INVALID_UNIT.fullMessage()); } } } From 5c84a60ca32a51acad8dccc27fda3e3e165606be Mon Sep 17 00:00:00 2001 From: HaODay Date: Mon, 12 Dec 2022 19:32:31 +0900 Subject: [PATCH 31/33] =?UTF-8?q?refactor(InputView):=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=ED=95=9C=20=EC=97=B4=EA=B1=B0=20=EC=83=81=EC=88=98=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/vendingmachine/view/InputView.java | 42 +++++++------------ 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/src/main/java/vendingmachine/view/InputView.java b/src/main/java/vendingmachine/view/InputView.java index e1e76c309..2bd74aa5d 100644 --- a/src/main/java/vendingmachine/view/InputView.java +++ b/src/main/java/vendingmachine/view/InputView.java @@ -2,53 +2,39 @@ import camp.nextstep.edu.missionutils.Console; +import static vendingmachine.model.Validator.*; +import static vendingmachine.util.message.InputMessage.*; + public class InputView { public static int readBalance() { - System.out.println("자판기가 보유하고 있는 금액을 입력해 주세요."); - int balance; + System.out.println(BALANCE.fullMessage()); try { - balance = validateNum(); - if (balance % 10 != 0) { - throw new IllegalArgumentException("보유 금액은 10의 배수여야 합니다."); - } + return validateBalance(Console.readLine()); } catch (IllegalArgumentException e) { - System.out.println("[ERROR] " + e.getMessage()); - balance = readBalance(); + System.out.println(e.getMessage()); + return readBalance(); } - return balance; } public static String readProductInfo() { - System.out.println("\n상품명과 가격, 수량을 입력해 주세요."); + System.out.println(PRODUCT_INFO.fullMessage()); return Console.readLine(); } + public static int readAmountOfInput() { - System.out.println("\n투입 금액을 입력해 주세요."); - int amountOfInput = 0; + System.out.println(AMOUNT_OF_INPUT.fullMessage()); try { - amountOfInput = validateNum(); - if (amountOfInput < 0) { - throw new IllegalArgumentException("투입 금액은 음수일 수 없습니다."); - } + return validateAmountOfInput(Console.readLine()); } catch (IllegalArgumentException e) { - System.out.println("[ERROR] " + e.getMessage()); - amountOfInput = readAmountOfInput(); + System.out.println(e.getMessage()); + return readAmountOfInput(); } - return amountOfInput; } public static String readBuyingProduct() { - System.out.println("구매할 상품명을 입력해 주세요."); + System.out.println(BUYING_PRODUCT.fullMessage()); return Console.readLine(); } - - private static int validateNum() throws IllegalArgumentException { - try { - return Integer.parseInt(Console.readLine()); - } catch (NumberFormatException e) { - throw new IllegalArgumentException("입력값은 숫자여야 합니다."); - } - } } From 762c9092383e8ea9c23f73d585f192869ffd6780 Mon Sep 17 00:00:00 2001 From: HaODay Date: Mon, 12 Dec 2022 19:33:26 +0900 Subject: [PATCH 32/33] =?UTF-8?q?refactor(Application):=20VendingMachineCo?= =?UTF-8?q?ntroller=20=EA=B0=9D=EC=B2=B4=EB=A1=9C=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=ED=95=98=EC=97=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/vendingmachine/Application.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/vendingmachine/Application.java b/src/main/java/vendingmachine/Application.java index cdef6303f..c41cd8251 100644 --- a/src/main/java/vendingmachine/Application.java +++ b/src/main/java/vendingmachine/Application.java @@ -2,8 +2,11 @@ import vendingmachine.controller.VendingMachineController; + public class Application { public static void main(String[] args) { - VendingMachineController.run(); + VendingMachineController vendingMachineController=new VendingMachineController(); + vendingMachineController.setVendingMachine(); + vendingMachineController.runVendingMachine(); } } From 1a24493213f72095b46e8985e1343405e08c7f36 Mon Sep 17 00:00:00 2001 From: HaODay Date: Mon, 12 Dec 2022 19:34:01 +0900 Subject: [PATCH 33/33] =?UTF-8?q?refactor(VendingMachieController):=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/VendingMachineController.java | 59 ++++++++++--------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/src/main/java/vendingmachine/controller/VendingMachineController.java b/src/main/java/vendingmachine/controller/VendingMachineController.java index c4f299198..067f7f948 100644 --- a/src/main/java/vendingmachine/controller/VendingMachineController.java +++ b/src/main/java/vendingmachine/controller/VendingMachineController.java @@ -7,22 +7,27 @@ import vendingmachine.view.OutputView; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; -import java.util.Map; import java.util.List; +import java.util.Map; + +import static vendingmachine.model.Validator.validateProduct; public class VendingMachineController { - private static Map products = new HashMap<>(); + private static Balance balance; + private static Map products; private static int amountOfInput; - public static void run() { - Balance balance = new Balance(InputView.readBalance()); + public void setVendingMachine() { + balance = new Balance(InputView.readBalance()); OutputView.printBalanceCoin(balance.createCoin()); saveProducts(); amountOfInput = InputView.readAmountOfInput(); OutputView.printAmountOfInput(amountOfInput); + } + + public void runVendingMachine() { while (canBuy()) { buy(); OutputView.printAmountOfInput(amountOfInput); @@ -30,46 +35,46 @@ public static void run() { OutputView.printChange(balance.calculateChangeCoin(amountOfInput)); } - private static boolean canBuy() { + private boolean canBuy() { List leftProductsPrice = new ArrayList(); for (Map.Entry entry : products.entrySet()) { - if (entry.getValue().amount != 0) { - leftProductsPrice.add(entry.getValue().price.get()); + if (entry.getValue().stockIsLeft()) { + leftProductsPrice.add(entry.getValue().getPrice()); } } - Collections.sort(leftProductsPrice); if (leftProductsPrice.isEmpty()) { return false; } - return amountOfInput >= leftProductsPrice.get(0); + return amountOfInput >= leftProductsPrice.stream().mapToInt(Integer::intValue).min().getAsInt(); } - private static void buy() { - try { - String buyingProduct = InputView.readBuyingProduct(); - Validator.validateBuyingProduct(buyingProduct, products, amountOfInput); - amountOfInput -= products.get(buyingProduct).price.get(); - products.get(buyingProduct).amount--; - } catch (IllegalArgumentException e) { - System.out.println("[ERROR] " + e.getMessage()); - buy(); - } - } - public static void saveProducts() { + public void saveProducts() throws IllegalArgumentException { + products = new HashMap<>(); try { - String input = InputView.readProductInfo(); - String[] str = input.split(";"); + String[] str = InputView.readProductInfo().split(";"); for (String s : str) { String[] productInfo = s.substring(1, s.length() - 1).split(","); - Validator.validateProduct(productInfo, products); + validateProduct(productInfo, products); products.put(productInfo[0], new Product(productInfo[1], productInfo[2])); } } catch (IllegalArgumentException e) { - System.out.println("[ERROR] " + e.getMessage()); - products = new HashMap<>(); + System.out.println(e.getMessage()); saveProducts(); } } + + + private void buy() { + try { + String buyingProduct = InputView.readBuyingProduct(); + Validator.validateBuyingProduct(buyingProduct, products, amountOfInput); + amountOfInput -= products.get(buyingProduct).getPrice(); + products.get(buyingProduct).reduceAmount(); + } catch (IllegalArgumentException e) { + System.out.println(e.getMessage()); + buy(); + } + } }