From 31df7ff5cd307ae1789cdaac7f5f666de3a3fe49 Mon Sep 17 00:00:00 2001 From: jisu-om Date: Wed, 22 Nov 2023 15:44:56 +0900 Subject: [PATCH 1/9] =?UTF-8?q?Docs:=20=EA=B5=AC=ED=98=84=ED=95=A0=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=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 --- build.gradle | 10 +-- docs/README.md | 65 +++++++++++++++++++ .../vendingmachine/{ => domain}/Coin.java | 2 +- 3 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 docs/README.md rename src/main/java/vendingmachine/{ => domain}/Coin.java (87%) diff --git a/build.gradle b/build.gradle index 67e032179..d10d94f65 100644 --- a/build.gradle +++ b/build.gradle @@ -14,11 +14,11 @@ dependencies { implementation 'com.github.woowacourse-projects:mission-utils:1.0.0' } -java { - toolchain { - languageVersion = JavaLanguageVersion.of(8) - } -} +//java { +// toolchain { +// languageVersion = JavaLanguageVersion.of(8) +// } +//} test { useJUnitPlatform() diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..311f535bd --- /dev/null +++ b/docs/README.md @@ -0,0 +1,65 @@ +# 구현 기능 목록 + + +- [ ] 반환되는 동전이 최소한이 되는 자판기를 구현 + + +- [ ] 자판기가 보유한 금액으로 동전을 무작위 생성 + - [ ] 입력받은 자판기 보유 금액으로 동전 생성 (500원, 100원, 50원, 10원) + - [ ] 투입 금액으로는 동전을 생성하지 않는다. + + +- [ ] 상품명, 가격, 수량을 입력하여 상품을 추가할 수 있다. + - [ ] 상품 가격은 100원부터 시작하며, 10원으로 나누어떨어져야 한다. + - 입력 형식 예: "[콜라,1500,20];[사이다,1000,10]" + + +- [ ] 사용자가 투입한 금액으로 상품을 구매할 수 있다. + - [ ] 투입 금액을 입력받고 투입 금액을 출력하고 구매할 상품명을 입력받는다. + + +- [ ] 잔돈 반환 조건 + - [ ] 남은 금액이 상품의 최저 가격보다 적은 경우 바로 잔돈을 돌려준다. + - [ ] 모든 상품이 소진된 경우 바로 잔돈을 돌려준다. + - [ ] 잔돈을 반환할 수 없는 경우 잔돈으로 반환할 수 있는 금액만 반환. 반환되지 않은 금액은 자판기에 남는다. + - [ ] 지폐를 잔돈으로 반환하는 경우는 없다. + + +- [ ] 사용자가 잘못된 값을 입력할 경우 IllegalArgumentException 발생, "[ERROR]"로 시작하는 에러 메시지를 출력 후, 해당 부분부터 다시 입력을 받는다. + + +- [ ] 코드 컨벤션 적용 ; 메서드 10라인 이하, else X, indent 2까지 + + + +--- +- 전체 흐름 입출력 예시 +``` +자판기가 보유하고 있는 금액을 입력해 주세요. +450 + +자판기가 보유한 동전 +500원 - 0개 +100원 - 4개 +50원 - 1개 +10원 - 0개 + +상품명과 가격, 수량을 입력해 주세요. +[콜라,1500,20];[사이다,1000,10] + +투입 금액을 입력해 주세요. +3000 + +투입 금액: 3000원 +구매할 상품명을 입력해 주세요. +콜라 + +투입 금액: 1500원 +구매할 상품명을 입력해 주세요. +사이다 + +투입 금액: 500원 +잔돈 +100원 - 4개 +50원 - 1개 +``` \ No newline at end of file diff --git a/src/main/java/vendingmachine/Coin.java b/src/main/java/vendingmachine/domain/Coin.java similarity index 87% rename from src/main/java/vendingmachine/Coin.java rename to src/main/java/vendingmachine/domain/Coin.java index c76293fbc..7bb89c146 100644 --- a/src/main/java/vendingmachine/Coin.java +++ b/src/main/java/vendingmachine/domain/Coin.java @@ -1,4 +1,4 @@ -package vendingmachine; +package vendingmachine.domain; public enum Coin { COIN_500(500), From c137f1a25607132d6edd18a51147778f0b8230e3 Mon Sep 17 00:00:00 2001 From: jisu-om Date: Wed, 22 Nov 2023 16:00:36 +0900 Subject: [PATCH 2/9] =?UTF-8?q?Feat:=20=EC=9E=90=ED=8C=90=EA=B8=B0=20?= =?UTF-8?q?=EB=B3=B4=EC=9C=A0=20=EA=B8=88=EC=95=A1=20=EC=9E=85=EB=A0=A5?= =?UTF-8?q?=EB=B0=9B=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 5 +++-- src/main/java/vendingmachine/Application.java | 7 ++++++- .../controller/MainController.java | 19 +++++++++++++++++++ src/main/java/vendingmachine/domain/Coin.java | 2 +- .../vendingmachine/domain/CoinGenerator.java | 4 ++++ .../exception/ErrorMessage.java | 16 ++++++++++++++++ .../utils/VendingMachineCoinValidator.java | 15 +++++++++++++++ .../java/vendingmachine/view/InputView.java | 16 ++++++++++++++++ .../java/vendingmachine/view/OutputView.java | 4 ++++ 9 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 src/main/java/vendingmachine/controller/MainController.java create mode 100644 src/main/java/vendingmachine/domain/CoinGenerator.java create mode 100644 src/main/java/vendingmachine/exception/ErrorMessage.java create mode 100644 src/main/java/vendingmachine/utils/VendingMachineCoinValidator.java create mode 100644 src/main/java/vendingmachine/view/InputView.java create mode 100644 src/main/java/vendingmachine/view/OutputView.java diff --git a/docs/README.md b/docs/README.md index 311f535bd..bb78d4837 100644 --- a/docs/README.md +++ b/docs/README.md @@ -5,8 +5,9 @@ - [ ] 자판기가 보유한 금액으로 동전을 무작위 생성 - - [ ] 입력받은 자판기 보유 금액으로 동전 생성 (500원, 100원, 50원, 10원) - - [ ] 투입 금액으로는 동전을 생성하지 않는다. + - [X] 자판기 보유 금액 입력받기 + - [ ] 입력받은 자판기 보유 금액으로 동전 생성 (500원, 100원, 50원, 10원) + - [ ] 투입 금액으로는 동전을 생성하지 않는다. - [ ] 상품명, 가격, 수량을 입력하여 상품을 추가할 수 있다. diff --git a/src/main/java/vendingmachine/Application.java b/src/main/java/vendingmachine/Application.java index 9d3be447b..3a536bc7f 100644 --- a/src/main/java/vendingmachine/Application.java +++ b/src/main/java/vendingmachine/Application.java @@ -1,7 +1,12 @@ package vendingmachine; +import vendingmachine.controller.MainController; +import vendingmachine.view.InputView; +import vendingmachine.view.OutputView; + public class Application { public static void main(String[] args) { - // TODO: 프로그램 구현 + MainController mainController = new MainController(new InputView(), new OutputView()); + mainController.run(); } } diff --git a/src/main/java/vendingmachine/controller/MainController.java b/src/main/java/vendingmachine/controller/MainController.java new file mode 100644 index 000000000..d59919ab1 --- /dev/null +++ b/src/main/java/vendingmachine/controller/MainController.java @@ -0,0 +1,19 @@ +package vendingmachine.controller; + +import vendingmachine.view.InputView; +import vendingmachine.view.OutputView; + +public class MainController { + private final InputView inputView; + private final OutputView outputView; + + public MainController(InputView inputView, OutputView outputView) { + this.inputView = inputView; + this.outputView = outputView; + } + + public void run() { + long amount = inputView.readVendingMachineAmount(); + //금액으로 동전 무작위 생성 + } +} diff --git a/src/main/java/vendingmachine/domain/Coin.java b/src/main/java/vendingmachine/domain/Coin.java index 7bb89c146..57a44ebd7 100644 --- a/src/main/java/vendingmachine/domain/Coin.java +++ b/src/main/java/vendingmachine/domain/Coin.java @@ -12,5 +12,5 @@ public enum Coin { this.amount = amount; } - // 추가 기능 구현 + } diff --git a/src/main/java/vendingmachine/domain/CoinGenerator.java b/src/main/java/vendingmachine/domain/CoinGenerator.java new file mode 100644 index 000000000..75721bdb1 --- /dev/null +++ b/src/main/java/vendingmachine/domain/CoinGenerator.java @@ -0,0 +1,4 @@ +package vendingmachine.domain; + +public class CoinGenerator { +} diff --git a/src/main/java/vendingmachine/exception/ErrorMessage.java b/src/main/java/vendingmachine/exception/ErrorMessage.java new file mode 100644 index 000000000..95f9130b4 --- /dev/null +++ b/src/main/java/vendingmachine/exception/ErrorMessage.java @@ -0,0 +1,16 @@ +package vendingmachine.exception; + +public enum ErrorMessage { + CAPTION("[ERROR] "), + INVALID_VENDING_MACHINE_COINS("금액은 숫자여야 합니다."); + + private final String message; + + ErrorMessage(String message) { + this.message = message; + } + + public String getMessage() { + return CAPTION.message + message; + } +} diff --git a/src/main/java/vendingmachine/utils/VendingMachineCoinValidator.java b/src/main/java/vendingmachine/utils/VendingMachineCoinValidator.java new file mode 100644 index 000000000..54077e7d7 --- /dev/null +++ b/src/main/java/vendingmachine/utils/VendingMachineCoinValidator.java @@ -0,0 +1,15 @@ +package vendingmachine.utils; + +import static vendingmachine.exception.ErrorMessage.INVALID_VENDING_MACHINE_COINS; + +public class VendingMachineCoinValidator { + public static long safeParseLong(String input) { + try { + return Long.parseLong(input); + } catch (NumberFormatException e) { + throw new IllegalArgumentException(INVALID_VENDING_MACHINE_COINS.getMessage()); + } + } + + +} diff --git a/src/main/java/vendingmachine/view/InputView.java b/src/main/java/vendingmachine/view/InputView.java new file mode 100644 index 000000000..1b202820a --- /dev/null +++ b/src/main/java/vendingmachine/view/InputView.java @@ -0,0 +1,16 @@ +package vendingmachine.view; + +import camp.nextstep.edu.missionutils.Console; +import vendingmachine.utils.VendingMachineCoinValidator; + +public class InputView { + private static final String ASK_VENDING_MACHINE_AMOUNT = "자판기가 보유하고 있는 금액을 입력해 주세요."; + + public long readVendingMachineAmount() { + System.out.println(ASK_VENDING_MACHINE_AMOUNT); + String input = Console.readLine(); + return VendingMachineCoinValidator.safeParseLong(input); + } + + +} diff --git a/src/main/java/vendingmachine/view/OutputView.java b/src/main/java/vendingmachine/view/OutputView.java new file mode 100644 index 000000000..2a53e8a58 --- /dev/null +++ b/src/main/java/vendingmachine/view/OutputView.java @@ -0,0 +1,4 @@ +package vendingmachine.view; + +public class OutputView { +} From db1c3b2e0bd3a22b51ca4b4318d37765d3a8a1db Mon Sep 17 00:00:00 2001 From: jisu-om Date: Wed, 22 Nov 2023 17:16:17 +0900 Subject: [PATCH 3/9] =?UTF-8?q?Feat:=20=EC=83=81=ED=92=88=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=EB=B0=9B=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 15 +++-- .../controller/MainController.java | 39 +++++++++++- src/main/java/vendingmachine/domain/Item.java | 26 ++++++++ .../java/vendingmachine/domain/Items.java | 28 +++++++++ .../domain/VendingMachineAmount.java | 16 +++++ src/main/java/vendingmachine/dto/ItemDto.java | 23 +++++++ .../exception/ErrorMessage.java | 7 ++- .../vendingmachine/utils/ItemValidator.java | 62 +++++++++++++++++++ .../vendingmachine/utils/ItemsValidator.java | 16 +++++ .../utils/VendingMachineAmountValidator.java | 22 +++++++ .../utils/VendingMachineCoinValidator.java | 15 ----- .../java/vendingmachine/view/InputView.java | 48 +++++++++++++- .../java/vendingmachine/view/OutputView.java | 5 ++ 13 files changed, 297 insertions(+), 25 deletions(-) create mode 100644 src/main/java/vendingmachine/domain/Item.java create mode 100644 src/main/java/vendingmachine/domain/Items.java create mode 100644 src/main/java/vendingmachine/domain/VendingMachineAmount.java create mode 100644 src/main/java/vendingmachine/dto/ItemDto.java create mode 100644 src/main/java/vendingmachine/utils/ItemValidator.java create mode 100644 src/main/java/vendingmachine/utils/ItemsValidator.java create mode 100644 src/main/java/vendingmachine/utils/VendingMachineAmountValidator.java delete mode 100644 src/main/java/vendingmachine/utils/VendingMachineCoinValidator.java diff --git a/docs/README.md b/docs/README.md index bb78d4837..555ebc490 100644 --- a/docs/README.md +++ b/docs/README.md @@ -6,13 +6,15 @@ - [ ] 자판기가 보유한 금액으로 동전을 무작위 생성 - [X] 자판기 보유 금액 입력받기 + - [X] 10 으로 나눠떨어져야 함 - [ ] 입력받은 자판기 보유 금액으로 동전 생성 (500원, 100원, 50원, 10원) - [ ] 투입 금액으로는 동전을 생성하지 않는다. -- [ ] 상품명, 가격, 수량을 입력하여 상품을 추가할 수 있다. - - [ ] 상품 가격은 100원부터 시작하며, 10원으로 나누어떨어져야 한다. - - 입력 형식 예: "[콜라,1500,20];[사이다,1000,10]" +- [X] 상품명, 가격, 수량을 입력하여 상품을 추가할 수 있다. + - [X] 상품 가격은 100원부터 시작하며, 10원으로 나누어떨어져야 한다. + - [X] 상품이 여러개인 경우, 상품명이 중복되지 않아야 한다. + - [X] 입력 형식 예: "[콜라,1500,20];[사이다,1000,10]" - [ ] 사용자가 투입한 금액으로 상품을 구매할 수 있다. @@ -23,13 +25,18 @@ - [ ] 남은 금액이 상품의 최저 가격보다 적은 경우 바로 잔돈을 돌려준다. - [ ] 모든 상품이 소진된 경우 바로 잔돈을 돌려준다. - [ ] 잔돈을 반환할 수 없는 경우 잔돈으로 반환할 수 있는 금액만 반환. 반환되지 않은 금액은 자판기에 남는다. + - [ ] 잔돈을 돌려줄 때 현재 보유한 최소 개수의 동전으로 잔돈을 돌려준다. - [ ] 지폐를 잔돈으로 반환하는 경우는 없다. - [ ] 사용자가 잘못된 값을 입력할 경우 IllegalArgumentException 발생, "[ERROR]"로 시작하는 에러 메시지를 출력 후, 해당 부분부터 다시 입력을 받는다. -- [ ] 코드 컨벤션 적용 ; 메서드 10라인 이하, else X, indent 2까지 +- [ ] 코드 컨벤션 적용 + - [ ] 메서드 10라인 이하 + - [ ] else X + - [ ] indent 2까지 + - [ ] 하드코딩 X (상수 처리) diff --git a/src/main/java/vendingmachine/controller/MainController.java b/src/main/java/vendingmachine/controller/MainController.java index d59919ab1..8e826d29d 100644 --- a/src/main/java/vendingmachine/controller/MainController.java +++ b/src/main/java/vendingmachine/controller/MainController.java @@ -1,8 +1,16 @@ package vendingmachine.controller; +import vendingmachine.domain.Item; +import vendingmachine.domain.Items; +import vendingmachine.domain.VendingMachineAmount; +import vendingmachine.dto.ItemDto; import vendingmachine.view.InputView; import vendingmachine.view.OutputView; +import java.util.List; +import java.util.function.Supplier; +import java.util.stream.Collectors; + public class MainController { private final InputView inputView; private final OutputView outputView; @@ -13,7 +21,34 @@ public MainController(InputView inputView, OutputView outputView) { } public void run() { - long amount = inputView.readVendingMachineAmount(); - //금액으로 동전 무작위 생성 + VendingMachineAmount amount = createVendingMachineAmount(); + //TODO amount 로 동전 무작위 생성 + Items items = createItems(); + } + + private VendingMachineAmount createVendingMachineAmount() { + return readUserInput(() -> { + long amount = inputView.readVendingMachineAmount(); + return VendingMachineAmount.from(amount); + }); + } + + private Items createItems() { + return readUserInput(() -> { + List items = inputView.readItems().stream() + .map(ItemDto::toItem) + .collect(Collectors.toList()); + return Items.from(items); + }); + } + + private T readUserInput(Supplier supplier) { + while (true) { + try { + return supplier.get(); + } catch (IllegalArgumentException e) { + outputView.printError(e.getMessage()); + } + } } } diff --git a/src/main/java/vendingmachine/domain/Item.java b/src/main/java/vendingmachine/domain/Item.java new file mode 100644 index 000000000..deb6fc326 --- /dev/null +++ b/src/main/java/vendingmachine/domain/Item.java @@ -0,0 +1,26 @@ +package vendingmachine.domain; + +import vendingmachine.utils.ItemValidator; + +public class Item { + private final String name; + private final long price; + private final long quantity; + + private Item(String name, long price, long quantity) { + this.name = name; + this.price = price; + this.quantity = quantity; + } + + public static Item of(String name, long price, long quantity) { + ItemValidator.validatePrice(price); + + return new Item(name, price, quantity); + } + + + public String provideName() { + return name; + } +} diff --git a/src/main/java/vendingmachine/domain/Items.java b/src/main/java/vendingmachine/domain/Items.java new file mode 100644 index 000000000..dd5ce00c1 --- /dev/null +++ b/src/main/java/vendingmachine/domain/Items.java @@ -0,0 +1,28 @@ +package vendingmachine.domain; + +import vendingmachine.utils.ItemsValidator; + +import java.util.List; +import java.util.stream.Collectors; + +public class Items { + private final List items; + + private Items(List items) { + this.items = items; + } + + public static Items from(List items) { + validateUniqueName(items); + return new Items(items); + } + + private static void validateUniqueName(List items) { + List names = items.stream() + .map(Item::provideName) + .collect(Collectors.toList()); + ItemsValidator.validateUniqueValue(names); + } + + +} diff --git a/src/main/java/vendingmachine/domain/VendingMachineAmount.java b/src/main/java/vendingmachine/domain/VendingMachineAmount.java new file mode 100644 index 000000000..00c7983d0 --- /dev/null +++ b/src/main/java/vendingmachine/domain/VendingMachineAmount.java @@ -0,0 +1,16 @@ +package vendingmachine.domain; + +import vendingmachine.utils.VendingMachineAmountValidator; + +public class VendingMachineAmount { + private final long amount; + + private VendingMachineAmount(long amount) { + this.amount = amount; + } + + public static VendingMachineAmount from(long amount) { + VendingMachineAmountValidator.validateDividedByMinimumAmount(amount); + return new VendingMachineAmount(amount); + } +} diff --git a/src/main/java/vendingmachine/dto/ItemDto.java b/src/main/java/vendingmachine/dto/ItemDto.java new file mode 100644 index 000000000..bd01001c5 --- /dev/null +++ b/src/main/java/vendingmachine/dto/ItemDto.java @@ -0,0 +1,23 @@ +package vendingmachine.dto; + +import vendingmachine.domain.Item; + +public class ItemDto { + private final String name; + private final long price; + private final long quantity; + + private ItemDto(String name, long price, long quantity) { + this.name = name; + this.price = price; + this.quantity = quantity; + } + + public static ItemDto of(String name, long price, long quantity) { + return new ItemDto(name, price, quantity); + } + + public Item toItem() { + return Item.of(name, price, quantity); + } +} diff --git a/src/main/java/vendingmachine/exception/ErrorMessage.java b/src/main/java/vendingmachine/exception/ErrorMessage.java index 95f9130b4..5f233a9c8 100644 --- a/src/main/java/vendingmachine/exception/ErrorMessage.java +++ b/src/main/java/vendingmachine/exception/ErrorMessage.java @@ -2,7 +2,12 @@ public enum ErrorMessage { CAPTION("[ERROR] "), - INVALID_VENDING_MACHINE_COINS("금액은 숫자여야 합니다."); + NOT_NUMERIC_VENDING_MACHINE_AMOUNT("금액은 숫자여야 합니다."), + INVALID_VENDING_MACHINE_AMOUNT("금액은 10으로 나눠떨어져야 합니다."), + INVALID_ITEMS_FORMAT("유효하지 않은 상품 입력 형식 입니다."), + INVALID_ITEM_DETAIL("유효하지 않은 상품 입력 값 입니다."), + INVALID_ITEM_PRICE("상품 가격은 100 이상 이어야 하고, 10 으로 나눠떨어지는 값이어야 합니다."), + DUPLICATE_ITEM_NAMES("중복된 상품 이름 입니다."); private final String message; diff --git a/src/main/java/vendingmachine/utils/ItemValidator.java b/src/main/java/vendingmachine/utils/ItemValidator.java new file mode 100644 index 000000000..14bb42425 --- /dev/null +++ b/src/main/java/vendingmachine/utils/ItemValidator.java @@ -0,0 +1,62 @@ +package vendingmachine.utils; + +import org.junit.platform.commons.util.StringUtils; + +import java.util.List; + +import static vendingmachine.exception.ErrorMessage.*; + +public class ItemValidator { + private static final int MINIMUM_PRICE = 100; + private static final int PRICE_UNIT = 10; + + public static List safeSplit(String input, String delimiter) { + validateEmpty(input); + validateStartsOrEndsWithDelimiter(input, delimiter); + return List.of(input.split(delimiter)); + } + + private static void validateEmpty(String input) { + if (StringUtils.isBlank(input)) { + throw new IllegalArgumentException(INVALID_ITEMS_FORMAT.getMessage()); + } + } + + private static void validateStartsOrEndsWithDelimiter(String input, String delimiter) { + if (input.startsWith(delimiter) || input.endsWith(delimiter)) { + throw new IllegalArgumentException(INVALID_ITEMS_FORMAT.getMessage()); + } + } + + public static String validateAndCleanPair(String input, String startDelimiter, String endDelimiter) { + if (!input.startsWith(startDelimiter) || !input.endsWith(endDelimiter)) { + throw new IllegalArgumentException(INVALID_ITEMS_FORMAT.getMessage()); + } + return input.substring(1, input.length() - 1); + } + + public static long safeParsePositiveLong(String input) { + try { + long value = Long.parseLong(input); + validatePositive(value); + return value; + } catch (NumberFormatException e) { + throw new IllegalArgumentException(INVALID_ITEMS_FORMAT.getMessage()); + } + } + + private static void validatePositive(long value) { + if (value <= 0) { + throw new IllegalArgumentException(INVALID_ITEM_DETAIL.getMessage()); + } + } + + public static void validatePrice(long price) { + if (price < MINIMUM_PRICE) { + throw new IllegalArgumentException(INVALID_ITEM_PRICE.getMessage()); + } + if (price % PRICE_UNIT != 0) { + throw new IllegalArgumentException(INVALID_ITEM_PRICE.getMessage()); + } + } +} diff --git a/src/main/java/vendingmachine/utils/ItemsValidator.java b/src/main/java/vendingmachine/utils/ItemsValidator.java new file mode 100644 index 000000000..9d15ffaaa --- /dev/null +++ b/src/main/java/vendingmachine/utils/ItemsValidator.java @@ -0,0 +1,16 @@ +package vendingmachine.utils; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static vendingmachine.exception.ErrorMessage.DUPLICATE_ITEM_NAMES; + +public class ItemsValidator { + public static void validateUniqueValue(List elements) { + Set uniqueElements = new HashSet<>(elements); + if (elements.size() != uniqueElements.size()) { + throw new IllegalArgumentException(DUPLICATE_ITEM_NAMES.getMessage()); + } + } +} diff --git a/src/main/java/vendingmachine/utils/VendingMachineAmountValidator.java b/src/main/java/vendingmachine/utils/VendingMachineAmountValidator.java new file mode 100644 index 000000000..5a4fe9547 --- /dev/null +++ b/src/main/java/vendingmachine/utils/VendingMachineAmountValidator.java @@ -0,0 +1,22 @@ +package vendingmachine.utils; + +import static vendingmachine.exception.ErrorMessage.INVALID_VENDING_MACHINE_AMOUNT; +import static vendingmachine.exception.ErrorMessage.NOT_NUMERIC_VENDING_MACHINE_AMOUNT; + +public class VendingMachineAmountValidator { + private static final int MINIMUM_AMOUNT = 10; + public static long safeParseLong(String input) { + try { + return Long.parseLong(input); + } catch (NumberFormatException e) { + throw new IllegalArgumentException(NOT_NUMERIC_VENDING_MACHINE_AMOUNT.getMessage()); + } + } + + public static void validateDividedByMinimumAmount(long amount) { + if (amount % MINIMUM_AMOUNT != 0) { + throw new IllegalArgumentException(INVALID_VENDING_MACHINE_AMOUNT.getMessage()); + } + } + +} diff --git a/src/main/java/vendingmachine/utils/VendingMachineCoinValidator.java b/src/main/java/vendingmachine/utils/VendingMachineCoinValidator.java deleted file mode 100644 index 54077e7d7..000000000 --- a/src/main/java/vendingmachine/utils/VendingMachineCoinValidator.java +++ /dev/null @@ -1,15 +0,0 @@ -package vendingmachine.utils; - -import static vendingmachine.exception.ErrorMessage.INVALID_VENDING_MACHINE_COINS; - -public class VendingMachineCoinValidator { - public static long safeParseLong(String input) { - try { - return Long.parseLong(input); - } catch (NumberFormatException e) { - throw new IllegalArgumentException(INVALID_VENDING_MACHINE_COINS.getMessage()); - } - } - - -} diff --git a/src/main/java/vendingmachine/view/InputView.java b/src/main/java/vendingmachine/view/InputView.java index 1b202820a..cd435c2fb 100644 --- a/src/main/java/vendingmachine/view/InputView.java +++ b/src/main/java/vendingmachine/view/InputView.java @@ -1,16 +1,58 @@ package vendingmachine.view; import camp.nextstep.edu.missionutils.Console; -import vendingmachine.utils.VendingMachineCoinValidator; +import vendingmachine.dto.ItemDto; +import vendingmachine.utils.ItemValidator; +import vendingmachine.utils.VendingMachineAmountValidator; + +import java.util.List; +import java.util.stream.Collectors; + +import static vendingmachine.exception.ErrorMessage.INVALID_ITEMS_FORMAT; public class InputView { private static final String ASK_VENDING_MACHINE_AMOUNT = "자판기가 보유하고 있는 금액을 입력해 주세요."; + private static final String ASK_ITEMS = "상품명과 가격, 수량을 입력해 주세요."; + private static final String ITEM_START_SYMBOL = "["; + private static final String ITEM_END_SYMBOL = "]"; + private static final String ITEM_DELIMITER = ";"; + private static final String ITEM_DETAIL_DELIMITER = ","; + private static final int REQUIRED_ITEM_COMPONENTS = 3; + private static final int ITEM_NAME_INDEX = 0; + private static final int ITEM_PRICE_INDEX = 1; + private static final int ITEM_QUANTITY_INDEX = 2; public long readVendingMachineAmount() { System.out.println(ASK_VENDING_MACHINE_AMOUNT); String input = Console.readLine(); - return VendingMachineCoinValidator.safeParseLong(input); + return VendingMachineAmountValidator.safeParseLong(input); + } + + public List readItems() { + System.out.println(ASK_ITEMS); + String input = Console.readLine(); + List pairs = ItemValidator.safeSplit(input, ITEM_DELIMITER); + return toItemDto(pairs); } + private List toItemDto(List pairs) { + return pairs.stream() + .map(pair -> ItemValidator.validateAndCleanPair(pair, ITEM_START_SYMBOL, ITEM_END_SYMBOL)) + .map(this::createItemDto) + .collect(Collectors.toList()); + } + + private ItemDto createItemDto(String pair) { + List pairs = ItemValidator.safeSplit(pair, ITEM_DETAIL_DELIMITER); + + if (pairs.size() != REQUIRED_ITEM_COMPONENTS) { + throw new IllegalArgumentException(INVALID_ITEMS_FORMAT.getMessage()); + } + String name = pairs.get(ITEM_NAME_INDEX); + long price = ItemValidator.safeParsePositiveLong(pairs.get(ITEM_PRICE_INDEX)); + long quantity = ItemValidator.safeParsePositiveLong(pairs.get(ITEM_QUANTITY_INDEX)); + + return ItemDto.of(name, price, quantity); + } -} +} \ No newline at end of file diff --git a/src/main/java/vendingmachine/view/OutputView.java b/src/main/java/vendingmachine/view/OutputView.java index 2a53e8a58..271f47782 100644 --- a/src/main/java/vendingmachine/view/OutputView.java +++ b/src/main/java/vendingmachine/view/OutputView.java @@ -1,4 +1,9 @@ package vendingmachine.view; public class OutputView { + + + public void printError(String message) { + System.out.println(message); + } } From 7de6ed2f0752d86f292f14323f4b1fc8e222d71b Mon Sep 17 00:00:00 2001 From: jisu-om Date: Wed, 22 Nov 2023 17:25:03 +0900 Subject: [PATCH 4/9] =?UTF-8?q?Feat:=20=ED=88=AC=EC=9E=85=EA=B8=88?= =?UTF-8?q?=EC=95=A1=20=EC=9E=85=EB=A0=A5=EB=B0=9B=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 --- docs/README.md | 2 +- .../vendingmachine/controller/MainController.java | 9 +++++++-- .../vendingmachine/exception/ErrorMessage.java | 3 ++- .../vendingmachine/utils/InputAmountValidator.java | 13 +++++++++++++ src/main/java/vendingmachine/view/InputView.java | 14 ++++++++++++++ 5 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 src/main/java/vendingmachine/utils/InputAmountValidator.java diff --git a/docs/README.md b/docs/README.md index 555ebc490..d49c38950 100644 --- a/docs/README.md +++ b/docs/README.md @@ -55,7 +55,7 @@ 상품명과 가격, 수량을 입력해 주세요. [콜라,1500,20];[사이다,1000,10] -투입 금액을 입력해 주세요. +투입 금액을 입력해 주세요. 3000 투입 금액: 3000원 diff --git a/src/main/java/vendingmachine/controller/MainController.java b/src/main/java/vendingmachine/controller/MainController.java index 8e826d29d..be082567f 100644 --- a/src/main/java/vendingmachine/controller/MainController.java +++ b/src/main/java/vendingmachine/controller/MainController.java @@ -21,9 +21,10 @@ public MainController(InputView inputView, OutputView outputView) { } public void run() { - VendingMachineAmount amount = createVendingMachineAmount(); + VendingMachineAmount amount = createVendingMachineAmount(); //자판기 보유금액 //TODO amount 로 동전 무작위 생성 - Items items = createItems(); + Items items = createItems(); //상품 + long inputAmount = createInputAmount(); //투입금액 } private VendingMachineAmount createVendingMachineAmount() { @@ -42,6 +43,10 @@ private Items createItems() { }); } + private long createInputAmount() { + return readUserInput(inputView::readInputAmount); + } + private T readUserInput(Supplier supplier) { while (true) { try { diff --git a/src/main/java/vendingmachine/exception/ErrorMessage.java b/src/main/java/vendingmachine/exception/ErrorMessage.java index 5f233a9c8..97061db70 100644 --- a/src/main/java/vendingmachine/exception/ErrorMessage.java +++ b/src/main/java/vendingmachine/exception/ErrorMessage.java @@ -7,7 +7,8 @@ public enum ErrorMessage { INVALID_ITEMS_FORMAT("유효하지 않은 상품 입력 형식 입니다."), INVALID_ITEM_DETAIL("유효하지 않은 상품 입력 값 입니다."), INVALID_ITEM_PRICE("상품 가격은 100 이상 이어야 하고, 10 으로 나눠떨어지는 값이어야 합니다."), - DUPLICATE_ITEM_NAMES("중복된 상품 이름 입니다."); + DUPLICATE_ITEM_NAMES("중복된 상품 이름 입니다."), + NOT_NUMERIC_INPUT_AMOUNT("투입 금액은 숫자여야 합니다."); private final String message; diff --git a/src/main/java/vendingmachine/utils/InputAmountValidator.java b/src/main/java/vendingmachine/utils/InputAmountValidator.java new file mode 100644 index 000000000..068c68f50 --- /dev/null +++ b/src/main/java/vendingmachine/utils/InputAmountValidator.java @@ -0,0 +1,13 @@ +package vendingmachine.utils; + +import static vendingmachine.exception.ErrorMessage.NOT_NUMERIC_INPUT_AMOUNT; + +public class InputAmountValidator { + public static long safeParseLong(String input) { + try { + return Long.parseLong(input); + } catch (NumberFormatException e) { + throw new IllegalArgumentException(NOT_NUMERIC_INPUT_AMOUNT.getMessage()); + } + } +} diff --git a/src/main/java/vendingmachine/view/InputView.java b/src/main/java/vendingmachine/view/InputView.java index cd435c2fb..ce2695914 100644 --- a/src/main/java/vendingmachine/view/InputView.java +++ b/src/main/java/vendingmachine/view/InputView.java @@ -2,6 +2,7 @@ import camp.nextstep.edu.missionutils.Console; import vendingmachine.dto.ItemDto; +import vendingmachine.utils.InputAmountValidator; import vendingmachine.utils.ItemValidator; import vendingmachine.utils.VendingMachineAmountValidator; @@ -13,6 +14,7 @@ public class InputView { private static final String ASK_VENDING_MACHINE_AMOUNT = "자판기가 보유하고 있는 금액을 입력해 주세요."; private static final String ASK_ITEMS = "상품명과 가격, 수량을 입력해 주세요."; + private static final String ASK_INPUT_AMOUNT = "투입 금액을 입력해 주세요."; private static final String ITEM_START_SYMBOL = "["; private static final String ITEM_END_SYMBOL = "]"; private static final String ITEM_DELIMITER = ";"; @@ -29,12 +31,17 @@ public long readVendingMachineAmount() { } public List readItems() { + printNewLine(); System.out.println(ASK_ITEMS); String input = Console.readLine(); List pairs = ItemValidator.safeSplit(input, ITEM_DELIMITER); return toItemDto(pairs); } + private void printNewLine() { + System.out.println(); + } + private List toItemDto(List pairs) { return pairs.stream() .map(pair -> ItemValidator.validateAndCleanPair(pair, ITEM_START_SYMBOL, ITEM_END_SYMBOL)) @@ -55,4 +62,11 @@ private ItemDto createItemDto(String pair) { return ItemDto.of(name, price, quantity); } + public long readInputAmount() { + printNewLine(); + System.out.println(ASK_INPUT_AMOUNT); + String input = Console.readLine(); + return InputAmountValidator.safeParseLong(input); + } + } \ No newline at end of file From 5e3457b002983ac0761efb287ec4ca90dcfe4fe8 Mon Sep 17 00:00:00 2001 From: jisu-om Date: Wed, 22 Nov 2023 18:33:23 +0900 Subject: [PATCH 5/9] =?UTF-8?q?Feat:=20=EC=83=81=ED=92=88=20=EA=B5=AC?= =?UTF-8?q?=EC=9E=85=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 --- docs/README.md | 4 +-- .../controller/MainController.java | 34 +++++++++++++++---- src/main/java/vendingmachine/domain/Item.java | 31 ++++++++++++++++- .../java/vendingmachine/domain/Items.java | 14 ++++++++ .../vendingmachine/dto/OrderDetailDto.java | 20 +++++++++++ .../exception/ErrorMessage.java | 4 ++- .../utils/InputAmountValidator.java | 16 ++++++--- .../java/vendingmachine/view/InputView.java | 7 +++- .../java/vendingmachine/view/OutputView.java | 12 +++++-- 9 files changed, 125 insertions(+), 17 deletions(-) create mode 100644 src/main/java/vendingmachine/dto/OrderDetailDto.java diff --git a/docs/README.md b/docs/README.md index d49c38950..13b1e32d1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -17,8 +17,8 @@ - [X] 입력 형식 예: "[콜라,1500,20];[사이다,1000,10]" -- [ ] 사용자가 투입한 금액으로 상품을 구매할 수 있다. - - [ ] 투입 금액을 입력받고 투입 금액을 출력하고 구매할 상품명을 입력받는다. +- [X] 사용자가 투입한 금액으로 상품을 구매할 수 있다. + - [X] 투입 금액을 입력받고 투입 금액을 출력하고 구매할 상품명을 입력받는다. - [ ] 잔돈 반환 조건 diff --git a/src/main/java/vendingmachine/controller/MainController.java b/src/main/java/vendingmachine/controller/MainController.java index be082567f..8de461cc7 100644 --- a/src/main/java/vendingmachine/controller/MainController.java +++ b/src/main/java/vendingmachine/controller/MainController.java @@ -1,9 +1,8 @@ package vendingmachine.controller; -import vendingmachine.domain.Item; -import vendingmachine.domain.Items; -import vendingmachine.domain.VendingMachineAmount; +import vendingmachine.domain.*; import vendingmachine.dto.ItemDto; +import vendingmachine.dto.OrderDetailDto; import vendingmachine.view.InputView; import vendingmachine.view.OutputView; @@ -21,10 +20,24 @@ public MainController(InputView inputView, OutputView outputView) { } public void run() { - VendingMachineAmount amount = createVendingMachineAmount(); //자판기 보유금액 + VendingMachineAmount amount = createVendingMachineAmount(); //TODO amount 로 동전 무작위 생성 - Items items = createItems(); //상품 - long inputAmount = createInputAmount(); //투입금액 + + Items items = createItems(); + long inputAmount = createInputAmount(); + outputView.printInputAmount(inputAmount); + boolean canBuyMore = true; + while (canBuyMore) { + OrderDetailDto orderDetailDto = createOrderDetailDto(inputAmount, items); + long updatedInputAmount = orderDetailDto.getUpdatedInputAmount(); + outputView.printInputAmount(updatedInputAmount); + canBuyMore = canBuyMore(); + } + //TODO 잔돈 반환 + } + + private boolean canBuyMore() { + //TODO 잔돈 반환 조건 체크 } private VendingMachineAmount createVendingMachineAmount() { @@ -47,6 +60,15 @@ private long createInputAmount() { return readUserInput(inputView::readInputAmount); } + private OrderDetailDto createOrderDetailDto(long inputAmount, Items items) { + return readUserInput(() -> { + String itemName = inputView.readOrderItemName(); + Item orderItem = items.buyItem(itemName, inputAmount); + long updatedInputAmount = inputAmount - orderItem.providePrice(); + return OrderDetailDto.of(itemName, updatedInputAmount); + }); + } + private T readUserInput(Supplier supplier) { while (true) { try { diff --git a/src/main/java/vendingmachine/domain/Item.java b/src/main/java/vendingmachine/domain/Item.java index deb6fc326..cf4d80586 100644 --- a/src/main/java/vendingmachine/domain/Item.java +++ b/src/main/java/vendingmachine/domain/Item.java @@ -2,10 +2,12 @@ import vendingmachine.utils.ItemValidator; +import static vendingmachine.exception.ErrorMessage.CANNOT_BUY_ORDER_ITEM; + public class Item { private final String name; private final long price; - private final long quantity; + private long quantity; private Item(String name, long price, long quantity) { this.name = name; @@ -19,8 +21,35 @@ public static Item of(String name, long price, long quantity) { return new Item(name, price, quantity); } + public void buyItem(long priceAmount) { + if (canBuy(priceAmount)) { + updateQuantity(); + return; + } + throw new IllegalArgumentException(CANNOT_BUY_ORDER_ITEM.getMessage()); + } + + private void updateQuantity() { + quantity--; + } + + public boolean canBuy(long priceAmount) { + return hasQuantity() && priceAmount >= price; + } + + private boolean hasQuantity() { + return quantity > 0; + } public String provideName() { return name; } + + public long providePrice() { + return price; + } + + public long provideQuantity() { + return quantity; + } } diff --git a/src/main/java/vendingmachine/domain/Items.java b/src/main/java/vendingmachine/domain/Items.java index dd5ce00c1..73739b566 100644 --- a/src/main/java/vendingmachine/domain/Items.java +++ b/src/main/java/vendingmachine/domain/Items.java @@ -5,6 +5,9 @@ import java.util.List; import java.util.stream.Collectors; +import static vendingmachine.exception.ErrorMessage.CANNOT_BUY_ORDER_ITEM; +import static vendingmachine.exception.ErrorMessage.INVALID_ORDER_ITEM_NAME; + public class Items { private final List items; @@ -24,5 +27,16 @@ private static void validateUniqueName(List items) { ItemsValidator.validateUniqueValue(names); } + public Item buyItem(String itemName, long priceAmount) { + Item item = findItemByName(itemName); + item.buyItem(priceAmount); + return item; + } + private Item findItemByName(String name) { + return items.stream() + .filter(item -> item.provideName().equals(name)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException(INVALID_ORDER_ITEM_NAME.getMessage())); + } } diff --git a/src/main/java/vendingmachine/dto/OrderDetailDto.java b/src/main/java/vendingmachine/dto/OrderDetailDto.java new file mode 100644 index 000000000..aee4eb568 --- /dev/null +++ b/src/main/java/vendingmachine/dto/OrderDetailDto.java @@ -0,0 +1,20 @@ +package vendingmachine.dto; + + +public class OrderDetailDto { + private final String itemName; + private final long updatedInputAmount; + + private OrderDetailDto(String itemName, long updatedInputAmount) { + this.itemName = itemName; + this.updatedInputAmount = updatedInputAmount; + } + + public static OrderDetailDto of(String itemName, long updatedInputAmount) { + return new OrderDetailDto(itemName, updatedInputAmount); + } + + public long getUpdatedInputAmount() { + return updatedInputAmount; + } +} diff --git a/src/main/java/vendingmachine/exception/ErrorMessage.java b/src/main/java/vendingmachine/exception/ErrorMessage.java index 97061db70..295f7e41c 100644 --- a/src/main/java/vendingmachine/exception/ErrorMessage.java +++ b/src/main/java/vendingmachine/exception/ErrorMessage.java @@ -8,7 +8,9 @@ public enum ErrorMessage { INVALID_ITEM_DETAIL("유효하지 않은 상품 입력 값 입니다."), INVALID_ITEM_PRICE("상품 가격은 100 이상 이어야 하고, 10 으로 나눠떨어지는 값이어야 합니다."), DUPLICATE_ITEM_NAMES("중복된 상품 이름 입니다."), - NOT_NUMERIC_INPUT_AMOUNT("투입 금액은 숫자여야 합니다."); + NOT_NUMERIC_INPUT_AMOUNT("투입 금액은 숫자여야 합니다."), + INVALID_ORDER_ITEM_NAME("유효하지 않은 상품명 입니다."), + CANNOT_BUY_ORDER_ITEM("재고가 없거나 금액이 부족하여 해당 상품은 구매 불가합니다."); private final String message; diff --git a/src/main/java/vendingmachine/utils/InputAmountValidator.java b/src/main/java/vendingmachine/utils/InputAmountValidator.java index 068c68f50..5862fff63 100644 --- a/src/main/java/vendingmachine/utils/InputAmountValidator.java +++ b/src/main/java/vendingmachine/utils/InputAmountValidator.java @@ -1,13 +1,21 @@ package vendingmachine.utils; -import static vendingmachine.exception.ErrorMessage.NOT_NUMERIC_INPUT_AMOUNT; +import static vendingmachine.exception.ErrorMessage.*; public class InputAmountValidator { - public static long safeParseLong(String input) { + public static long safeParsePositiveLong(String input) { try { - return Long.parseLong(input); + long value = Long.parseLong(input); + validatePositive(value); + return value; } catch (NumberFormatException e) { - throw new IllegalArgumentException(NOT_NUMERIC_INPUT_AMOUNT.getMessage()); + throw new IllegalArgumentException(INVALID_ITEMS_FORMAT.getMessage()); + } + } + + private static void validatePositive(long value) { + if (value <= 0) { + throw new IllegalArgumentException(INVALID_ITEM_DETAIL.getMessage()); } } } diff --git a/src/main/java/vendingmachine/view/InputView.java b/src/main/java/vendingmachine/view/InputView.java index ce2695914..8fe159d1d 100644 --- a/src/main/java/vendingmachine/view/InputView.java +++ b/src/main/java/vendingmachine/view/InputView.java @@ -23,6 +23,7 @@ public class InputView { private static final int ITEM_NAME_INDEX = 0; private static final int ITEM_PRICE_INDEX = 1; private static final int ITEM_QUANTITY_INDEX = 2; + private static final String ASK_ORDER_ITEM = "구매할 상품명을 입력해 주세요."; public long readVendingMachineAmount() { System.out.println(ASK_VENDING_MACHINE_AMOUNT); @@ -66,7 +67,11 @@ public long readInputAmount() { printNewLine(); System.out.println(ASK_INPUT_AMOUNT); String input = Console.readLine(); - return InputAmountValidator.safeParseLong(input); + return InputAmountValidator.safeParsePositiveLong(input); } + public String readOrderItemName() { + System.out.println(ASK_ORDER_ITEM); + return Console.readLine(); + } } \ No newline at end of file diff --git a/src/main/java/vendingmachine/view/OutputView.java b/src/main/java/vendingmachine/view/OutputView.java index 271f47782..26e73eefd 100644 --- a/src/main/java/vendingmachine/view/OutputView.java +++ b/src/main/java/vendingmachine/view/OutputView.java @@ -1,9 +1,17 @@ package vendingmachine.view; public class OutputView { - - + private static final String INPUT_AMOUNT_FORMAT = "투입 금액: %d원"; public void printError(String message) { System.out.println(message); } + + public void printInputAmount(long amount) { + printNewLine(); + System.out.printf((INPUT_AMOUNT_FORMAT) + "%n", amount); + } + + private void printNewLine() { + System.out.println(); + } } From bf436686354ea38e4e9c83828e478e90ad8dc3b5 Mon Sep 17 00:00:00 2001 From: jisu-om Date: Wed, 22 Nov 2023 18:59:56 +0900 Subject: [PATCH 6/9] =?UTF-8?q?Feat:=20=EC=9E=94=EB=8F=88=20=EB=B0=98?= =?UTF-8?q?=ED=99=98=20=EC=A1=B0=EA=B1=B4=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 9 ++++--- .../controller/MainController.java | 26 ++++++++++++------- .../vendingmachine/domain/InsertedAmount.java | 26 +++++++++++++++++++ src/main/java/vendingmachine/domain/Item.java | 6 ++--- .../java/vendingmachine/domain/Items.java | 19 ++++++++++++-- .../exception/ErrorMessage.java | 6 ++--- .../utils/VendingMachineAmountValidator.java | 4 +-- 7 files changed, 73 insertions(+), 23 deletions(-) create mode 100644 src/main/java/vendingmachine/domain/InsertedAmount.java diff --git a/docs/README.md b/docs/README.md index 13b1e32d1..369b7eacf 100644 --- a/docs/README.md +++ b/docs/README.md @@ -21,9 +21,12 @@ - [X] 투입 금액을 입력받고 투입 금액을 출력하고 구매할 상품명을 입력받는다. -- [ ] 잔돈 반환 조건 - - [ ] 남은 금액이 상품의 최저 가격보다 적은 경우 바로 잔돈을 돌려준다. - - [ ] 모든 상품이 소진된 경우 바로 잔돈을 돌려준다. +- [X] 잔돈 반환 조건 + - [X] 남은 금액이 상품의 최저 가격보다 적은 경우 바로 잔돈을 돌려준다. + - [X] 모든 상품이 소진된 경우 바로 잔돈을 돌려준다. + + +- [ ] 잔돈 반환 - [ ] 잔돈을 반환할 수 없는 경우 잔돈으로 반환할 수 있는 금액만 반환. 반환되지 않은 금액은 자판기에 남는다. - [ ] 잔돈을 돌려줄 때 현재 보유한 최소 개수의 동전으로 잔돈을 돌려준다. - [ ] 지폐를 잔돈으로 반환하는 경우는 없다. diff --git a/src/main/java/vendingmachine/controller/MainController.java b/src/main/java/vendingmachine/controller/MainController.java index 8de461cc7..d5be81415 100644 --- a/src/main/java/vendingmachine/controller/MainController.java +++ b/src/main/java/vendingmachine/controller/MainController.java @@ -24,20 +24,23 @@ public void run() { //TODO amount 로 동전 무작위 생성 Items items = createItems(); - long inputAmount = createInputAmount(); - outputView.printInputAmount(inputAmount); + InsertedAmount insertedAmount = createInsertedAmount(); + outputView.printInputAmount(insertedAmount.provideAmount()); boolean canBuyMore = true; while (canBuyMore) { - OrderDetailDto orderDetailDto = createOrderDetailDto(inputAmount, items); - long updatedInputAmount = orderDetailDto.getUpdatedInputAmount(); - outputView.printInputAmount(updatedInputAmount); - canBuyMore = canBuyMore(); + OrderDetailDto orderDetailDto = createOrderDetailDto(insertedAmount.provideAmount(), items); + insertedAmount.updateAmount(orderDetailDto.getUpdatedInputAmount()); + outputView.printInputAmount(insertedAmount.provideAmount()); + canBuyMore = canBuyMore(items, insertedAmount); } //TODO 잔돈 반환 + // - 잔돈을 반환할 수 없는 경우 잔돈으로 반환할 수 있는 금액만 반환. 반환되지 않은 금액은 자판기에 남는다. + // - 잔돈을 돌려줄 때 현재 보유한 최소 개수의 동전으로 잔돈을 돌려준다. + // - 지폐를 잔돈으로 반환하는 경우는 없다. } - private boolean canBuyMore() { - //TODO 잔돈 반환 조건 체크 + private boolean canBuyMore(Items items, InsertedAmount amount) { + return amount.isEqualOrLargerThan(items.findPurchasableMinimumPrice()) && !items.hasNoQuantity(); } private VendingMachineAmount createVendingMachineAmount() { @@ -56,8 +59,11 @@ private Items createItems() { }); } - private long createInputAmount() { - return readUserInput(inputView::readInputAmount); + private InsertedAmount createInsertedAmount() { + readUserInput(() -> { + long amount = inputView.readInputAmount(); + return InsertedAmount.from(amount); + }); } private OrderDetailDto createOrderDetailDto(long inputAmount, Items items) { diff --git a/src/main/java/vendingmachine/domain/InsertedAmount.java b/src/main/java/vendingmachine/domain/InsertedAmount.java new file mode 100644 index 000000000..6a0071ed2 --- /dev/null +++ b/src/main/java/vendingmachine/domain/InsertedAmount.java @@ -0,0 +1,26 @@ +package vendingmachine.domain; + +public class InsertedAmount { + private long amount; + + private InsertedAmount(long amount) { + this.amount = amount; + } + + public static InsertedAmount from(long amount) { + return new InsertedAmount(amount); + } + + public long provideAmount() { + return amount; + } + + public void updateAmount(long amount) { + this.amount = amount; + } + + + public boolean isEqualOrLargerThan(long amount) { + return this.amount >= amount; + } +} diff --git a/src/main/java/vendingmachine/domain/Item.java b/src/main/java/vendingmachine/domain/Item.java index cf4d80586..73476cd43 100644 --- a/src/main/java/vendingmachine/domain/Item.java +++ b/src/main/java/vendingmachine/domain/Item.java @@ -37,7 +37,7 @@ public boolean canBuy(long priceAmount) { return hasQuantity() && priceAmount >= price; } - private boolean hasQuantity() { + public boolean hasQuantity() { return quantity > 0; } @@ -49,7 +49,7 @@ public long providePrice() { return price; } - public long provideQuantity() { - return quantity; + public boolean hasNoQuantity() { + return quantity == 0; } } diff --git a/src/main/java/vendingmachine/domain/Items.java b/src/main/java/vendingmachine/domain/Items.java index 73739b566..f4f347ad6 100644 --- a/src/main/java/vendingmachine/domain/Items.java +++ b/src/main/java/vendingmachine/domain/Items.java @@ -3,10 +3,11 @@ import vendingmachine.utils.ItemsValidator; import java.util.List; +import java.util.NoSuchElementException; +import java.util.OptionalLong; import java.util.stream.Collectors; -import static vendingmachine.exception.ErrorMessage.CANNOT_BUY_ORDER_ITEM; -import static vendingmachine.exception.ErrorMessage.INVALID_ORDER_ITEM_NAME; +import static vendingmachine.exception.ErrorMessage.*; public class Items { private final List items; @@ -39,4 +40,18 @@ private Item findItemByName(String name) { .findFirst() .orElseThrow(() -> new IllegalArgumentException(INVALID_ORDER_ITEM_NAME.getMessage())); } + + public long findPurchasableMinimumPrice() { + return items.stream() + .filter(Item::hasQuantity) + .mapToLong(Item::providePrice) + .min() + .orElseThrow(() -> new IllegalArgumentException(INVALID_ORDER_ITEM.getMessage())); + } + + public boolean hasNoQuantity() { + return items.stream() + .allMatch(Item::hasNoQuantity); + } + } diff --git a/src/main/java/vendingmachine/exception/ErrorMessage.java b/src/main/java/vendingmachine/exception/ErrorMessage.java index 295f7e41c..2e87c67d5 100644 --- a/src/main/java/vendingmachine/exception/ErrorMessage.java +++ b/src/main/java/vendingmachine/exception/ErrorMessage.java @@ -2,15 +2,15 @@ public enum ErrorMessage { CAPTION("[ERROR] "), - NOT_NUMERIC_VENDING_MACHINE_AMOUNT("금액은 숫자여야 합니다."), + NOT_NUMERIC_AMOUNT("금액은 숫자여야 합니다."), INVALID_VENDING_MACHINE_AMOUNT("금액은 10으로 나눠떨어져야 합니다."), INVALID_ITEMS_FORMAT("유효하지 않은 상품 입력 형식 입니다."), INVALID_ITEM_DETAIL("유효하지 않은 상품 입력 값 입니다."), INVALID_ITEM_PRICE("상품 가격은 100 이상 이어야 하고, 10 으로 나눠떨어지는 값이어야 합니다."), DUPLICATE_ITEM_NAMES("중복된 상품 이름 입니다."), - NOT_NUMERIC_INPUT_AMOUNT("투입 금액은 숫자여야 합니다."), INVALID_ORDER_ITEM_NAME("유효하지 않은 상품명 입니다."), - CANNOT_BUY_ORDER_ITEM("재고가 없거나 금액이 부족하여 해당 상품은 구매 불가합니다."); + CANNOT_BUY_ORDER_ITEM("재고가 없거나 금액이 부족하여 해당 상품은 구매 불가합니다."), + INVALID_ORDER_ITEM("구매 가능한 아이템이 없습니다."); private final String message; diff --git a/src/main/java/vendingmachine/utils/VendingMachineAmountValidator.java b/src/main/java/vendingmachine/utils/VendingMachineAmountValidator.java index 5a4fe9547..6706d2b5b 100644 --- a/src/main/java/vendingmachine/utils/VendingMachineAmountValidator.java +++ b/src/main/java/vendingmachine/utils/VendingMachineAmountValidator.java @@ -1,7 +1,7 @@ package vendingmachine.utils; import static vendingmachine.exception.ErrorMessage.INVALID_VENDING_MACHINE_AMOUNT; -import static vendingmachine.exception.ErrorMessage.NOT_NUMERIC_VENDING_MACHINE_AMOUNT; +import static vendingmachine.exception.ErrorMessage.NOT_NUMERIC_AMOUNT; public class VendingMachineAmountValidator { private static final int MINIMUM_AMOUNT = 10; @@ -9,7 +9,7 @@ public static long safeParseLong(String input) { try { return Long.parseLong(input); } catch (NumberFormatException e) { - throw new IllegalArgumentException(NOT_NUMERIC_VENDING_MACHINE_AMOUNT.getMessage()); + throw new IllegalArgumentException(NOT_NUMERIC_AMOUNT.getMessage()); } } From 8da9ea0c14885da73d91b4ab62f7105c55f3a612 Mon Sep 17 00:00:00 2001 From: jisu-om Date: Wed, 22 Nov 2023 19:30:31 +0900 Subject: [PATCH 7/9] =?UTF-8?q?Feat:=20=EC=9E=90=ED=8C=90=EA=B8=B0=20?= =?UTF-8?q?=EB=B3=B4=EC=9C=A0=20=EA=B8=88=EC=95=A1=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=8F=99=EC=A0=84=20=EC=83=9D=EC=84=B1=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 --- docs/README.md | 8 +++---- .../controller/MainController.java | 9 ++++---- src/main/java/vendingmachine/domain/Coin.java | 21 ++++++++++++++++++- .../vendingmachine/domain/CoinGenerator.java | 4 ---- .../domain/VendingMachineAmount.java | 16 -------------- .../domain/VendingMachineCoins.java | 20 ++++++++++++++++++ 6 files changed, 48 insertions(+), 30 deletions(-) delete mode 100644 src/main/java/vendingmachine/domain/CoinGenerator.java delete mode 100644 src/main/java/vendingmachine/domain/VendingMachineAmount.java create mode 100644 src/main/java/vendingmachine/domain/VendingMachineCoins.java diff --git a/docs/README.md b/docs/README.md index 369b7eacf..19cef1e6f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,14 +1,14 @@ # 구현 기능 목록 -- [ ] 반환되는 동전이 최소한이 되는 자판기를 구현 +- [X] 반환되는 동전이 최소한이 되는 자판기를 구현 -> 가능한 한 적은 수의 동전 반환 = 큰 단위의 동전 최대한 많이 만들기 -- [ ] 자판기가 보유한 금액으로 동전을 무작위 생성 +- [X] 자판기가 보유한 금액으로 동전을 무작위 생성 - [X] 자판기 보유 금액 입력받기 - [X] 10 으로 나눠떨어져야 함 - - [ ] 입력받은 자판기 보유 금액으로 동전 생성 (500원, 100원, 50원, 10원) - - [ ] 투입 금액으로는 동전을 생성하지 않는다. + - [X] 입력받은 자판기 보유 금액으로 동전 생성 (500원, 100원, 50원, 10원) + - [X] 투입 금액으로는 동전을 생성하지 않는다. - [X] 상품명, 가격, 수량을 입력하여 상품을 추가할 수 있다. diff --git a/src/main/java/vendingmachine/controller/MainController.java b/src/main/java/vendingmachine/controller/MainController.java index d5be81415..95ffda99e 100644 --- a/src/main/java/vendingmachine/controller/MainController.java +++ b/src/main/java/vendingmachine/controller/MainController.java @@ -20,9 +20,7 @@ public MainController(InputView inputView, OutputView outputView) { } public void run() { - VendingMachineAmount amount = createVendingMachineAmount(); - //TODO amount 로 동전 무작위 생성 - + VendingMachineCoins coins = createVendingMachineAmount(); Items items = createItems(); InsertedAmount insertedAmount = createInsertedAmount(); outputView.printInputAmount(insertedAmount.provideAmount()); @@ -37,16 +35,17 @@ public void run() { // - 잔돈을 반환할 수 없는 경우 잔돈으로 반환할 수 있는 금액만 반환. 반환되지 않은 금액은 자판기에 남는다. // - 잔돈을 돌려줄 때 현재 보유한 최소 개수의 동전으로 잔돈을 돌려준다. // - 지폐를 잔돈으로 반환하는 경우는 없다. + } private boolean canBuyMore(Items items, InsertedAmount amount) { return amount.isEqualOrLargerThan(items.findPurchasableMinimumPrice()) && !items.hasNoQuantity(); } - private VendingMachineAmount createVendingMachineAmount() { + private VendingMachineCoins createVendingMachineAmount() { return readUserInput(() -> { long amount = inputView.readVendingMachineAmount(); - return VendingMachineAmount.from(amount); + return VendingMachineCoins.from(amount); }); } diff --git a/src/main/java/vendingmachine/domain/Coin.java b/src/main/java/vendingmachine/domain/Coin.java index 57a44ebd7..43745973d 100644 --- a/src/main/java/vendingmachine/domain/Coin.java +++ b/src/main/java/vendingmachine/domain/Coin.java @@ -1,5 +1,9 @@ package vendingmachine.domain; +import java.util.Arrays; +import java.util.EnumMap; +import java.util.concurrent.atomic.AtomicLong; + public enum Coin { COIN_500(500), COIN_100(100), @@ -12,5 +16,20 @@ public enum Coin { this.amount = amount; } + public static EnumMap generateMinimumCoins(long totalAmount) { + EnumMap coins = new EnumMap<>(Coin.class); + AtomicLong remainingAmount = new AtomicLong(totalAmount); + + Arrays.stream(Coin.values()).forEach(coin -> { + long coinCount = remainingAmount.get() / coin.getAmount(); + remainingAmount.addAndGet(-coinCount * coin.getAmount()); + coins.put(coin, coinCount); + }); -} + return coins; + } + + public int getAmount() { + return amount; + } +} \ No newline at end of file diff --git a/src/main/java/vendingmachine/domain/CoinGenerator.java b/src/main/java/vendingmachine/domain/CoinGenerator.java deleted file mode 100644 index 75721bdb1..000000000 --- a/src/main/java/vendingmachine/domain/CoinGenerator.java +++ /dev/null @@ -1,4 +0,0 @@ -package vendingmachine.domain; - -public class CoinGenerator { -} diff --git a/src/main/java/vendingmachine/domain/VendingMachineAmount.java b/src/main/java/vendingmachine/domain/VendingMachineAmount.java deleted file mode 100644 index 00c7983d0..000000000 --- a/src/main/java/vendingmachine/domain/VendingMachineAmount.java +++ /dev/null @@ -1,16 +0,0 @@ -package vendingmachine.domain; - -import vendingmachine.utils.VendingMachineAmountValidator; - -public class VendingMachineAmount { - private final long amount; - - private VendingMachineAmount(long amount) { - this.amount = amount; - } - - public static VendingMachineAmount from(long amount) { - VendingMachineAmountValidator.validateDividedByMinimumAmount(amount); - return new VendingMachineAmount(amount); - } -} diff --git a/src/main/java/vendingmachine/domain/VendingMachineCoins.java b/src/main/java/vendingmachine/domain/VendingMachineCoins.java new file mode 100644 index 000000000..ac0da674f --- /dev/null +++ b/src/main/java/vendingmachine/domain/VendingMachineCoins.java @@ -0,0 +1,20 @@ +package vendingmachine.domain; + +import vendingmachine.utils.VendingMachineAmountValidator; + +import java.util.EnumMap; + +public class VendingMachineCoins { + private final EnumMap coins; + + private VendingMachineCoins(EnumMap coins) { + this.coins = coins; + } + + public static VendingMachineCoins from(long amount) { + VendingMachineAmountValidator.validateDividedByMinimumAmount(amount); + //TODO enumMap 생성 + EnumMap coins = Coin.generateMinimumCoins(amount); + return new VendingMachineCoins(coins); + } +} From f6525ffdb0f693b0995673baa68f8be9bcedafbb Mon Sep 17 00:00:00 2001 From: jisu-om Date: Wed, 22 Nov 2023 20:10:09 +0900 Subject: [PATCH 8/9] =?UTF-8?q?Feat:=20=EC=9E=94=EB=8F=88=20=EA=B3=84?= =?UTF-8?q?=EC=82=B0=20=EB=B0=8F=20=EC=B6=9C=EB=A0=A5=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/MainController.java | 16 +++++++++-- .../domain/VendingMachineCoins.java | 23 +++++++++++++-- src/main/java/vendingmachine/dto/CoinDto.java | 4 +++ .../vendingmachine/dto/ExchangeCoinsDto.java | 28 +++++++++++++++++++ .../java/vendingmachine/view/OutputView.java | 12 ++++++++ 5 files changed, 77 insertions(+), 6 deletions(-) create mode 100644 src/main/java/vendingmachine/dto/CoinDto.java create mode 100644 src/main/java/vendingmachine/dto/ExchangeCoinsDto.java diff --git a/src/main/java/vendingmachine/controller/MainController.java b/src/main/java/vendingmachine/controller/MainController.java index 95ffda99e..5e619c2cf 100644 --- a/src/main/java/vendingmachine/controller/MainController.java +++ b/src/main/java/vendingmachine/controller/MainController.java @@ -1,11 +1,13 @@ package vendingmachine.controller; import vendingmachine.domain.*; +import vendingmachine.dto.ExchangeCoinsDto; import vendingmachine.dto.ItemDto; import vendingmachine.dto.OrderDetailDto; import vendingmachine.view.InputView; import vendingmachine.view.OutputView; +import java.util.EnumMap; import java.util.List; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -31,11 +33,19 @@ public void run() { outputView.printInputAmount(insertedAmount.provideAmount()); canBuyMore = canBuyMore(items, insertedAmount); } + generateExchangeCoins(coins, insertedAmount); + } + + private void generateExchangeCoins(VendingMachineCoins coins, InsertedAmount insertedAmount) { //TODO 잔돈 반환 // - 잔돈을 반환할 수 없는 경우 잔돈으로 반환할 수 있는 금액만 반환. 반환되지 않은 금액은 자판기에 남는다. // - 잔돈을 돌려줄 때 현재 보유한 최소 개수의 동전으로 잔돈을 돌려준다. // - 지폐를 잔돈으로 반환하는 경우는 없다. - + long amount = insertedAmount.provideAmount(); + EnumMap exchangeCoins = coins.generateExchangeCoins(amount); + ExchangeCoinsDto exchangeCoinsDto = ExchangeCoinsDto.from(exchangeCoins); +// ExchangeCoinsDto 생성 + outputView.printExchangeCoins(exchangeCoinsDto); } private boolean canBuyMore(Items items, InsertedAmount amount) { @@ -59,7 +69,7 @@ private Items createItems() { } private InsertedAmount createInsertedAmount() { - readUserInput(() -> { + return readUserInput(() -> { long amount = inputView.readInputAmount(); return InsertedAmount.from(amount); }); @@ -83,4 +93,4 @@ private T readUserInput(Supplier supplier) { } } } -} +} \ No newline at end of file diff --git a/src/main/java/vendingmachine/domain/VendingMachineCoins.java b/src/main/java/vendingmachine/domain/VendingMachineCoins.java index ac0da674f..b3c46eb06 100644 --- a/src/main/java/vendingmachine/domain/VendingMachineCoins.java +++ b/src/main/java/vendingmachine/domain/VendingMachineCoins.java @@ -5,16 +5,33 @@ import java.util.EnumMap; public class VendingMachineCoins { + private final long totalAmount; private final EnumMap coins; - private VendingMachineCoins(EnumMap coins) { + private VendingMachineCoins(long totalAmount, EnumMap coins) { + this.totalAmount = totalAmount; this.coins = coins; } public static VendingMachineCoins from(long amount) { VendingMachineAmountValidator.validateDividedByMinimumAmount(amount); - //TODO enumMap 생성 EnumMap coins = Coin.generateMinimumCoins(amount); - return new VendingMachineCoins(coins); + return new VendingMachineCoins(amount, coins); + } + + public EnumMap generateExchangeCoins(long amount) { + if (amount > totalAmount) { + return new EnumMap<>(coins); + } + + EnumMap exchangeCoins = new EnumMap<>(Coin.class); + + for (Coin coin : Coin.values()) { + long coinCount = Math.min(coins.get(coin), amount / coin.getAmount()); + amount -= coinCount * coin.getAmount(); + coins.put(coin, coins.get(coin) - coinCount); + exchangeCoins.put(coin, coinCount); + } + return exchangeCoins; } } diff --git a/src/main/java/vendingmachine/dto/CoinDto.java b/src/main/java/vendingmachine/dto/CoinDto.java new file mode 100644 index 000000000..acb62c6d6 --- /dev/null +++ b/src/main/java/vendingmachine/dto/CoinDto.java @@ -0,0 +1,4 @@ +package vendingmachine.dto; + +public class CoinDto { +} diff --git a/src/main/java/vendingmachine/dto/ExchangeCoinsDto.java b/src/main/java/vendingmachine/dto/ExchangeCoinsDto.java new file mode 100644 index 000000000..510b5271f --- /dev/null +++ b/src/main/java/vendingmachine/dto/ExchangeCoinsDto.java @@ -0,0 +1,28 @@ +package vendingmachine.dto; + +import vendingmachine.domain.Coin; + +import java.util.EnumMap; +import java.util.Map; +import java.util.stream.Collectors; + +public class ExchangeCoinsDto { + private final Map coins; + + private ExchangeCoinsDto(Map coins) { + this.coins = coins; + } + + public static ExchangeCoinsDto from(EnumMap exchangeCoins) { + Map coins = exchangeCoins.entrySet().stream() + .collect(Collectors.toMap( + entry -> entry.getKey().getAmount(), + Map.Entry::getValue + )); + return new ExchangeCoinsDto(coins); + } + + public Map getCoins() { + return coins; + } +} \ No newline at end of file diff --git a/src/main/java/vendingmachine/view/OutputView.java b/src/main/java/vendingmachine/view/OutputView.java index 26e73eefd..ef6dbc41e 100644 --- a/src/main/java/vendingmachine/view/OutputView.java +++ b/src/main/java/vendingmachine/view/OutputView.java @@ -1,7 +1,12 @@ package vendingmachine.view; +import vendingmachine.dto.ExchangeCoinsDto; + public class OutputView { private static final String INPUT_AMOUNT_FORMAT = "투입 금액: %d원"; + private static final String START_EXCHANGE = "잔돈"; + private static final String EXCHANGE_FORMAT = "%d원 - %d개"; + public void printError(String message) { System.out.println(message); } @@ -14,4 +19,11 @@ public void printInputAmount(long amount) { private void printNewLine() { System.out.println(); } + + public + public void printExchangeCoins(ExchangeCoinsDto dto) { + System.out.println(START_EXCHANGE); + dto.getCoins().forEach((coinValue, quantity) -> + System.out.println(String.format(EXCHANGE_FORMAT, coinValue, quantity))); + } } From 5c7ebe5fe5716f5d6f1a970c1cd87c82dd98b9d5 Mon Sep 17 00:00:00 2001 From: jisu-om Date: Wed, 22 Nov 2023 20:19:52 +0900 Subject: [PATCH 9/9] =?UTF-8?q?Feat:=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/MainController.java | 7 ++---- .../domain/VendingMachineCoins.java | 4 ++++ src/main/java/vendingmachine/dto/CoinDto.java | 4 ---- .../dto/VendingMachineCoinsDto.java | 23 +++++++++++++++++++ .../java/vendingmachine/view/OutputView.java | 10 +++++++- 5 files changed, 38 insertions(+), 10 deletions(-) delete mode 100644 src/main/java/vendingmachine/dto/CoinDto.java create mode 100644 src/main/java/vendingmachine/dto/VendingMachineCoinsDto.java diff --git a/src/main/java/vendingmachine/controller/MainController.java b/src/main/java/vendingmachine/controller/MainController.java index 5e619c2cf..0c6dc8ebb 100644 --- a/src/main/java/vendingmachine/controller/MainController.java +++ b/src/main/java/vendingmachine/controller/MainController.java @@ -4,6 +4,7 @@ import vendingmachine.dto.ExchangeCoinsDto; import vendingmachine.dto.ItemDto; import vendingmachine.dto.OrderDetailDto; +import vendingmachine.dto.VendingMachineCoinsDto; import vendingmachine.view.InputView; import vendingmachine.view.OutputView; @@ -23,6 +24,7 @@ public MainController(InputView inputView, OutputView outputView) { public void run() { VendingMachineCoins coins = createVendingMachineAmount(); + outputView.printVendingMachineAmount(VendingMachineCoinsDto.from(coins)); Items items = createItems(); InsertedAmount insertedAmount = createInsertedAmount(); outputView.printInputAmount(insertedAmount.provideAmount()); @@ -37,14 +39,9 @@ public void run() { } private void generateExchangeCoins(VendingMachineCoins coins, InsertedAmount insertedAmount) { - //TODO 잔돈 반환 - // - 잔돈을 반환할 수 없는 경우 잔돈으로 반환할 수 있는 금액만 반환. 반환되지 않은 금액은 자판기에 남는다. - // - 잔돈을 돌려줄 때 현재 보유한 최소 개수의 동전으로 잔돈을 돌려준다. - // - 지폐를 잔돈으로 반환하는 경우는 없다. long amount = insertedAmount.provideAmount(); EnumMap exchangeCoins = coins.generateExchangeCoins(amount); ExchangeCoinsDto exchangeCoinsDto = ExchangeCoinsDto.from(exchangeCoins); -// ExchangeCoinsDto 생성 outputView.printExchangeCoins(exchangeCoinsDto); } diff --git a/src/main/java/vendingmachine/domain/VendingMachineCoins.java b/src/main/java/vendingmachine/domain/VendingMachineCoins.java index b3c46eb06..aeda83faf 100644 --- a/src/main/java/vendingmachine/domain/VendingMachineCoins.java +++ b/src/main/java/vendingmachine/domain/VendingMachineCoins.java @@ -34,4 +34,8 @@ public EnumMap generateExchangeCoins(long amount) { } return exchangeCoins; } + + public EnumMap provideCoins() { + return coins; + } } diff --git a/src/main/java/vendingmachine/dto/CoinDto.java b/src/main/java/vendingmachine/dto/CoinDto.java deleted file mode 100644 index acb62c6d6..000000000 --- a/src/main/java/vendingmachine/dto/CoinDto.java +++ /dev/null @@ -1,4 +0,0 @@ -package vendingmachine.dto; - -public class CoinDto { -} diff --git a/src/main/java/vendingmachine/dto/VendingMachineCoinsDto.java b/src/main/java/vendingmachine/dto/VendingMachineCoinsDto.java new file mode 100644 index 000000000..bfadbbb84 --- /dev/null +++ b/src/main/java/vendingmachine/dto/VendingMachineCoinsDto.java @@ -0,0 +1,23 @@ +package vendingmachine.dto; + +import vendingmachine.domain.Coin; +import vendingmachine.domain.VendingMachineCoins; + +import java.util.EnumMap; + +public class VendingMachineCoinsDto { + private final EnumMap coins; + + private VendingMachineCoinsDto(EnumMap coins) { + this.coins = coins; + } + + public static VendingMachineCoinsDto from(VendingMachineCoins coins) { + EnumMap providedCoins = coins.provideCoins(); + return new VendingMachineCoinsDto(providedCoins); + } + + public EnumMap getCoins() { + return coins; + } +} diff --git a/src/main/java/vendingmachine/view/OutputView.java b/src/main/java/vendingmachine/view/OutputView.java index ef6dbc41e..7e89d4ef8 100644 --- a/src/main/java/vendingmachine/view/OutputView.java +++ b/src/main/java/vendingmachine/view/OutputView.java @@ -1,9 +1,11 @@ package vendingmachine.view; import vendingmachine.dto.ExchangeCoinsDto; +import vendingmachine.dto.VendingMachineCoinsDto; public class OutputView { private static final String INPUT_AMOUNT_FORMAT = "투입 금액: %d원"; + private static final String VENDING_MACHINE_AMOUNT_MESSAGE = "자판기가 보유한 동전"; private static final String START_EXCHANGE = "잔돈"; private static final String EXCHANGE_FORMAT = "%d원 - %d개"; @@ -20,7 +22,13 @@ private void printNewLine() { System.out.println(); } - public + public void printVendingMachineAmount(VendingMachineCoinsDto dto) { + printNewLine(); + System.out.println(VENDING_MACHINE_AMOUNT_MESSAGE); + dto.getCoins().forEach((coin, quantity) -> + System.out.println(String.format(EXCHANGE_FORMAT, coin.getAmount(), quantity))); + } + public void printExchangeCoins(ExchangeCoinsDto dto) { System.out.println(START_EXCHANGE); dto.getCoins().forEach((coinValue, quantity) ->