diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..b561fc821 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,10 @@ +## 기능 요구 사항 + +- [ ] 자판기의 보유 금액을 입력한다. `InputView` +- [ ] 자판기의 보유 금액을 기반으로 무작위로 동전의 갯수를 생성한다. `Machine` +- [ ] 생성한 자판기의 동전갯수를 출력한다. `OutputView` +- [ ] 상품명, 가격, 수량을 입력받는다. `InputView` +- [ ] 사용자에게 투입 금액을 입력받는다. `InputView` +- [ ] 구매할 상품 명을 입력받는다. `상품의 최소 금액 > 남은 금액일 때 까지 반복` - `InputView` +- [ ] 상품을 구매하고 남은 돈을 출력한다. `OutputView` +- [ ] 상품을 구매하고 나면 동전을 최소로 해서 거슬러 준다. `동전이 모자랄 땐 남은 금액은 반환하지 않음` `OutputView` \ No newline at end of file diff --git a/src/main/java/vendingmachine/Application.java b/src/main/java/vendingmachine/Application.java index 9d3be447b..2b5933769 100644 --- a/src/main/java/vendingmachine/Application.java +++ b/src/main/java/vendingmachine/Application.java @@ -1,7 +1,11 @@ package vendingmachine; +import vendingmachine.controller.MainController; + + public class Application { public static void main(String[] args) { - // TODO: 프로그램 구현 + MainController mainController = new MainController(); + mainController.run(); } } diff --git a/src/main/java/vendingmachine/Coin.java b/src/main/java/vendingmachine/Coin.java deleted file mode 100644 index c76293fbc..000000000 --- a/src/main/java/vendingmachine/Coin.java +++ /dev/null @@ -1,16 +0,0 @@ -package vendingmachine; - -public enum Coin { - COIN_500(500), - COIN_100(100), - COIN_50(50), - COIN_10(10); - - private final int amount; - - Coin(final int amount) { - this.amount = amount; - } - - // 추가 기능 구현 -} diff --git a/src/main/java/vendingmachine/Validator.java b/src/main/java/vendingmachine/Validator.java new file mode 100644 index 000000000..c68209618 --- /dev/null +++ b/src/main/java/vendingmachine/Validator.java @@ -0,0 +1,26 @@ +package vendingmachine; + +import vendingmachine.domain.Product; + +public class Validator { + + private Validator () {} + public static void validateMachineHasMoneyInput(String money) { + if (Integer.parseInt(money) <= 0) { + throw new IllegalArgumentException("[ERROR] 입력한 금액이 올바르지 않습니다."); + } + } + + public static void validateMachineHasMoneyInputType(String money) { + String regExp = "^[0-9]+$"; + if (!money.matches(regExp)) { + throw new IllegalArgumentException("[ERROR] 숫자가 아닌 값이 포함되어 있습니다."); + } + } + + public static void validateProduct(Product product) { + if (product == null) { + throw new IllegalArgumentException("[ERROR]"); + } + } +} diff --git a/src/main/java/vendingmachine/controller/MainController.java b/src/main/java/vendingmachine/controller/MainController.java new file mode 100644 index 000000000..8bdb5b608 --- /dev/null +++ b/src/main/java/vendingmachine/controller/MainController.java @@ -0,0 +1,151 @@ +package vendingmachine.controller; + +import vendingmachine.Validator; +import vendingmachine.util.Constant; +import vendingmachine.domain.Coin; +import vendingmachine.domain.Machine; +import vendingmachine.domain.Product; +import vendingmachine.view.InputView; +import vendingmachine.view.OutputView; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class MainController { + private static LinkedHashMap coinMap = generateRandomMachineCoin(); + private static LinkedHashMap changeCoins = setChangeMap(); + + public void run() { + List products = new ArrayList<>(); // 상품 객체 생성 + createProductInfo(products); + int minProductPrice = getMinProductPrice(products); + int userMoneyInput = InputView.inputMoney(); + while (userMoneyInput > minProductPrice) { + userMoneyInput = purchaseProduct(products, userMoneyInput); + } + printChangeResult(coinMap, userMoneyInput, changeCoins); + } + + private static void printChangeResult(LinkedHashMap coinMap, int userMoneyInput, LinkedHashMap changeCoins) { + OutputView.printCurrentInputMoney(userMoneyInput); + makeChange(userMoneyInput); + OutputView.printChange(changeCoins); + } + + private static LinkedHashMap generateRandomMachineCoin() { + Machine machine = new Machine(); + LinkedHashMap coinMap = machine.getCoins(); + generateMachineHasCoins(machine); + OutputView.printMachineHasCoins(coinMap); + return coinMap; + } + + private static void makeChange(int userMoneyInput) { + while (userMoneyInput != 0) { + if (hasCoin(userMoneyInput, 500)) { + calculateChange(500); + userMoneyInput -= 500; + continue; + } + if (hasCoin(userMoneyInput, 100)) { + calculateChange(100); + userMoneyInput -= 100; + continue; + } + if (hasCoin(userMoneyInput, 50)) { + calculateChange(50); + userMoneyInput -= 50; + continue; + } + if (hasCoin(userMoneyInput, 10)) { + calculateChange(10); + userMoneyInput -= 10; + continue; + } + if (checkRemainingCoins()) break; + } + } + + private static boolean hasCoin(int userMoneyInput, int coin) { + return userMoneyInput >= coin && coinMap.get(Coin.valueOf(coin)) > 0; + } + + private static void createProductInfo(List products) { + String[] productsInput = InputView.inputProductDetail().split(";"); + insertProduct(products, productsInput); + } + + private static int purchaseProduct(List products, int userMoneyInput) { + OutputView.printCurrentInputMoney(userMoneyInput); + String purchasingProduct = InputView.inputPurchaseProduct(); + Product inputProduct = getProduct(products, purchasingProduct); + int productPrice = inputProduct.getPrice(); + userMoneyInput -= productPrice; + return userMoneyInput; + } + + private static void generateMachineHasCoins(Machine machine) { + int machineInputMoney = InputView.inputMachineHoldMoney(); + machine.generateCoin(machineInputMoney); + } + + + private static int generateChange(Map coinMap, int userMoneyInput ,Map changeCoins) { + calculateChange(10); + userMoneyInput -= 10; + return userMoneyInput; + } + + private static boolean checkRemainingCoins() { + return coinMap.get(Coin.COIN_500) == 0 && coinMap.get(Coin.COIN_100) == 0 && coinMap.get(Coin.COIN_50) == 0 && coinMap.get(Coin.COIN_10) == 0; + } + + private static int getMinProductPrice(List products) { + int minProductPrice = products.get(0).getPrice(); + for (int i = 1; i < products.size(); i++) { + if (minProductPrice > products.get(i).getPrice()) { + minProductPrice = products.get(i).getPrice(); + } + } + return minProductPrice; + } + + private static void insertProduct(List products, String[] productsInput) { + for (String product : productsInput) { + String productDetail = product.replaceAll(Constant.REGEX, ""); + String[] productInfo = productDetail.split(","); + Product freshProduct = new Product(productInfo[0], Integer.parseInt(productInfo[1]), Integer.parseInt(productInfo[2])); + products.add(freshProduct); + } + } + + private static LinkedHashMap setChangeMap() { + LinkedHashMap changeCoins = new LinkedHashMap<>(); + setChangeMap(changeCoins); + return changeCoins; + } + + private static Product getProduct(List products, String purchasingProduct) { + Product inputProduct = products.stream() + .filter(x -> x.getName().equals(purchasingProduct)) + .findFirst() + .orElse(null); + Validator.validateProduct(inputProduct); + return inputProduct; + } + + private static void calculateChange(int inputCoin) { + Coin coin = Coin.valueOf(inputCoin); + coinMap.put(coin, coinMap.get(coin) - 1); + changeCoins.put(coin, changeCoins.get(coin) + 1); + } + + private static void setChangeMap(Map coinsMap) { + coinsMap.put(Coin.COIN_500, 0); + coinsMap.put(Coin.COIN_100, 0); + coinsMap.put(Coin.COIN_50, 0); + coinsMap.put(Coin.COIN_10, 0); + } +} diff --git a/src/main/java/vendingmachine/domain/Coin.java b/src/main/java/vendingmachine/domain/Coin.java new file mode 100644 index 000000000..f18bfad6f --- /dev/null +++ b/src/main/java/vendingmachine/domain/Coin.java @@ -0,0 +1,35 @@ +package vendingmachine.domain; + +import java.util.Arrays; +import java.util.function.Supplier; +import java.util.stream.Stream; + +public enum Coin { + COIN_500(500), + COIN_100(100), + COIN_50(50), + COIN_10(10); + + private final int amount; + + Coin(final int amount) { + this.amount = amount; + } + + /** + * 이 함수가 요구하는 것은 coin이라는 숫자를 받았을때 Coin Enum을 가져오는 것 + * @param coin + * @return + */ + public static Coin valueOf(int coin) { + return Arrays.stream(Coin.values()) + .filter(Coin -> Coin.amount == coin) + .findFirst() + .orElseThrow(null); + } + + @Override + public String toString() { + return this.amount + "원"; + } +} diff --git a/src/main/java/vendingmachine/domain/Machine.java b/src/main/java/vendingmachine/domain/Machine.java new file mode 100644 index 000000000..311969187 --- /dev/null +++ b/src/main/java/vendingmachine/domain/Machine.java @@ -0,0 +1,58 @@ +package vendingmachine.domain; + +import camp.nextstep.edu.missionutils.Randoms; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; + +public class Machine { + private LinkedHashMap coins; + + public Machine() { + this.coins = generateCoinMap(); + } + + public LinkedHashMap getCoins() { + return coins; + } + + private LinkedHashMap generateCoinMap() { + LinkedHashMap coinMap = new LinkedHashMap<>(); + coinMap.put(Coin.COIN_500, 0); + coinMap.put(Coin.COIN_100, 0); + coinMap.put(Coin.COIN_50, 0); + coinMap.put(Coin.COIN_10, 0); + return coinMap; + + } + + public void generateCoin(int moneyInput) { + List coinUnit = getCoinUnit(); + generateRandomCoin(moneyInput, coinUnit); + } + + private void generateRandomCoin(int moneyInput, List coinUnit) { + while(moneyInput > 0) { + int pickRandomNum = Randoms.pickNumberInList(coinUnit); + Coin randomCoin = Coin.valueOf(pickRandomNum); + if (isInputRemainingMoney(moneyInput, pickRandomNum)) continue; + moneyInput -= pickRandomNum; + coins.put(randomCoin, coins.get(randomCoin) + 1); + } + } + + private static boolean isInputRemainingMoney(int moneyInput, int pickRandomNum) { + return moneyInput - pickRandomNum < 0; + } + + private static List getCoinUnit() { + List coinUnit = new ArrayList<>(); + coinUnit.add(500); + coinUnit.add(100); + coinUnit.add(50); + coinUnit.add(10); + return coinUnit; + } + +} diff --git a/src/main/java/vendingmachine/domain/Product.java b/src/main/java/vendingmachine/domain/Product.java new file mode 100644 index 000000000..984f50eac --- /dev/null +++ b/src/main/java/vendingmachine/domain/Product.java @@ -0,0 +1,29 @@ +package vendingmachine.domain; + +import java.util.Arrays; +import java.util.stream.Stream; + +public class Product { + + private String name; + private int price; + private int quantity; + + public Product(String name, int price, int quantity) { + this.name = name; + this.price = price; + this.quantity = quantity; + } + + public String getName() { + return name; + } + + public int getPrice() { + return price; + } + + public int getQuantity() { + return quantity; + } +} diff --git a/src/main/java/vendingmachine/util/Constant.java b/src/main/java/vendingmachine/util/Constant.java new file mode 100644 index 000000000..820be4359 --- /dev/null +++ b/src/main/java/vendingmachine/util/Constant.java @@ -0,0 +1,5 @@ +package vendingmachine.util; + +public class Constant { + public static final String REGEX= "[^ㄱ-ㅎㅏ-ㅣ가-힣a-zA-Z0-9,]"; +} diff --git a/src/main/java/vendingmachine/util/MessageConstant.java b/src/main/java/vendingmachine/util/MessageConstant.java new file mode 100644 index 000000000..0298f410b --- /dev/null +++ b/src/main/java/vendingmachine/util/MessageConstant.java @@ -0,0 +1,7 @@ +package vendingmachine.util; + +public class MessageConstant { + public static final String INPUT_VENDING_MACHINE_HAS_MONEY_MSG = "자판기가 보유하고 있는 금액을 입력해 주세요."; + public static final String MACHINE_HAS_COIN_MSG = "자판기가 보유한 동전"; + +} diff --git a/src/main/java/vendingmachine/view/InputView.java b/src/main/java/vendingmachine/view/InputView.java new file mode 100644 index 000000000..88a0a6c9b --- /dev/null +++ b/src/main/java/vendingmachine/view/InputView.java @@ -0,0 +1,45 @@ +package vendingmachine.view; + +import camp.nextstep.edu.missionutils.Console; +import vendingmachine.Validator; + +public class InputView { + private InputView() {} + + /** + * 자판기가 초기에 갖고 있는 돈을 입력하는 함수 + * @return + */ + + public static int inputMachineHoldMoney() { + try { + OutputView.printMachineInputMoneyMsg(); + String inputMoney = Console.readLine(); + Validator.validateMachineHasMoneyInput(inputMoney); + Validator.validateMachineHasMoneyInputType(inputMoney); + return Integer.parseInt(inputMoney); + } catch (IllegalArgumentException error) { + System.out.println(error.getMessage()); + } + return -1; + } + + /** + * 상품명, 가격, 수량을 입력하는 함수 + * @return + */ + public static String inputProductDetail() { + OutputView.printProductDetailInputMsg(); + return Console.readLine(); + } + + public static int inputMoney() { + OutputView.printInputMoneyMsg(); + return Integer.parseInt(Console.readLine()); + } + + public static String inputPurchaseProduct() { + OutputView.printPurchaseProductInputMsg(); + return Console.readLine(); + } +} diff --git a/src/main/java/vendingmachine/view/OutputView.java b/src/main/java/vendingmachine/view/OutputView.java new file mode 100644 index 000000000..0c901d4e3 --- /dev/null +++ b/src/main/java/vendingmachine/view/OutputView.java @@ -0,0 +1,51 @@ +package vendingmachine.view; + +import vendingmachine.util.MessageConstant; +import vendingmachine.domain.Coin; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +public class OutputView { + private OutputView() { + } + public static void printMachineInputMoneyMsg() { + System.out.println(MessageConstant.INPUT_VENDING_MACHINE_HAS_MONEY_MSG); + } + public static void printMachineHasCoinMsg() { + System.out.println(MessageConstant.MACHINE_HAS_COIN_MSG); + } + + public static void printMachineHasCoins(HashMap coins) { + printMachineHasCoinMsg(); + for (Map.Entry entry : coins.entrySet()) { + System.out.println(entry.getKey() + " - " + entry.getValue() + "개"); + } + } + + public static void printProductDetailInputMsg() { + System.out.println("상품명과 가격, 수량을 입력해 주세요."); + } + + public static void printInputMoneyMsg() { + System.out.println("투입 금액을 입력해 주세요."); + } + + public static void printCurrentInputMoney(int money) { + System.out.println("투입 금액: " + money + "원"); + } + + public static void printPurchaseProductInputMsg() { + System.out.println("구매할 상품명을 입력해 주세요."); + } + + public static void printChange(LinkedHashMap coins) { + System.out.println("잔돈"); + for (Map.Entry entry : coins.entrySet()) { + if (entry.getValue() > 0) { + System.out.println(entry.getKey() + " - " + entry.getValue() + "개"); + } + } + } +}