diff --git a/src/main/java/vendingmachine/Application.java b/src/main/java/vendingmachine/Application.java index 9d3be447b..c734f9640 100644 --- a/src/main/java/vendingmachine/Application.java +++ b/src/main/java/vendingmachine/Application.java @@ -1,7 +1,18 @@ package vendingmachine; +import vendingmachine.controller.MachineController; +import vendingmachine.model.Coins; +import vendingmachine.view.MachineView; + +import java.util.HashMap; + public class Application { public static void main(String[] args) { // TODO: 프로그램 구현 + MachineView machineView = new MachineView(); + CoinGenerator coinGenerator = new CoinGenerator(); + Coins coins = new Coins(new HashMap<>()); + MachineController machineController = new MachineController(machineView, coinGenerator); + machineController.run(); } } diff --git a/src/main/java/vendingmachine/CoinGenerator.java b/src/main/java/vendingmachine/CoinGenerator.java new file mode 100644 index 000000000..09ab97bf6 --- /dev/null +++ b/src/main/java/vendingmachine/CoinGenerator.java @@ -0,0 +1,39 @@ +package vendingmachine; + +import camp.nextstep.edu.missionutils.Randoms; +import vendingmachine.model.Coin; +import vendingmachine.model.Coins; + +import java.util.*; + +public class CoinGenerator { + private Map emptyCoins(){ + Map map = new HashMap<>(); + map.put(Coin.COIN_500.getAmount(), 0); + map.put(Coin.COIN_100.getAmount(), 0); + map.put(Coin.COIN_50.getAmount(), 0); + map.put(Coin.COIN_10.getAmount(), 0); + return map; + } + + private List coinAmountNames(){ + List coinAmountNames = new ArrayList<>(); + coinAmountNames.add(Coin.COIN_500.getAmount()); + coinAmountNames.add(Coin.COIN_100.getAmount()); + coinAmountNames.add(Coin.COIN_50.getAmount()); + coinAmountNames.add(Coin.COIN_10.getAmount()); + return coinAmountNames; + } + + public Coins generateCoins(int money){ + Map map = emptyCoins(); + while(money>0){ + int pickNum = Randoms.pickNumberInList(coinAmountNames()); + if(pickNum<=money){ + map.put(pickNum, map.get(pickNum)+1); + money -= pickNum; + } + } + return new Coins(map); + } +} diff --git a/src/main/java/vendingmachine/controller/MachineController.java b/src/main/java/vendingmachine/controller/MachineController.java new file mode 100644 index 000000000..4856c95ac --- /dev/null +++ b/src/main/java/vendingmachine/controller/MachineController.java @@ -0,0 +1,34 @@ +package vendingmachine.controller; + +import camp.nextstep.edu.missionutils.Console; +import vendingmachine.CoinGenerator; +import vendingmachine.model.Coins; +import vendingmachine.model.Products; +import vendingmachine.view.MachineView; + +import java.util.List; + +public class MachineController { + private final MachineView machineView; + private final CoinGenerator coinGenerator; + + public MachineController(MachineView machineView, CoinGenerator coinGenerator) { + this.machineView = machineView; + this.coinGenerator = coinGenerator; + } + + public void run() { + Coins coins = makeCoins(); + Products products = machineView.productNameAndAmount(); + int money = machineView.insertProducts(products, coins); + } + + public Coins makeCoins(){ + machineView.inputChange(); + int change = machineView.change(); + Coins coins = coinGenerator.generateCoins(change); + List quantitys = coins.getQuantitys(); + machineView.outputCoins(quantitys); + return coins; + } +} diff --git a/src/main/java/vendingmachine/Coin.java b/src/main/java/vendingmachine/model/Coin.java similarity index 70% rename from src/main/java/vendingmachine/Coin.java rename to src/main/java/vendingmachine/model/Coin.java index c76293fbc..769f20605 100644 --- a/src/main/java/vendingmachine/Coin.java +++ b/src/main/java/vendingmachine/model/Coin.java @@ -1,4 +1,4 @@ -package vendingmachine; +package vendingmachine.model; public enum Coin { COIN_500(500), @@ -13,4 +13,7 @@ public enum Coin { } // 추가 기능 구현 + public int getAmount(){ + return this.amount; + } } diff --git a/src/main/java/vendingmachine/model/Coins.java b/src/main/java/vendingmachine/model/Coins.java new file mode 100644 index 000000000..20277e224 --- /dev/null +++ b/src/main/java/vendingmachine/model/Coins.java @@ -0,0 +1,26 @@ +package vendingmachine.model; + +import java.util.*; + +public class Coins { + private final Map coins; + + public Coins(Map coins) { + Map map = new HashMap<>(); + for(Integer key: coins.keySet()){ + if(key==500) map.put(Coin.COIN_500, coins.get(key)); + if(key==100) map.put(Coin.COIN_100, coins.get(key)); + if(key==50) map.put(Coin.COIN_50, coins.get(key)); + if(key==10) map.put(Coin.COIN_10, coins.get(key)); + } + this.coins = map; + } + + public Map getCoins(){ + return new HashMap<>(coins); + } + + public List getQuantitys(){ + return Arrays.asList(coins.get(Coin.COIN_500), coins.get(Coin.COIN_100), coins.get(Coin.COIN_50), coins.get(Coin.COIN_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..484a7dd82 --- /dev/null +++ b/src/main/java/vendingmachine/model/Product.java @@ -0,0 +1,25 @@ +package vendingmachine.model; + +public class Product { + private final String name; + private final int price; + private final int amount; + + public Product(String name, int price, int amount) { + this.name = name; + this.price = price; + this.amount = amount; + } + + public int getPrice(){ + return this.price; + } + + public String getName() { + return this.name; + } + + public int getAmount(){ + return this.amount; + } +} diff --git a/src/main/java/vendingmachine/model/Products.java b/src/main/java/vendingmachine/model/Products.java new file mode 100644 index 000000000..b5fe04ccb --- /dev/null +++ b/src/main/java/vendingmachine/model/Products.java @@ -0,0 +1,38 @@ +package vendingmachine.model; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +public class Products { + private final List products; + + public Products(List products) { + this.products = new ArrayList<>(products); + } + + public List getProducts(){ + return Collections.unmodifiableList(this.products); + } + + public int getMinPrice(){ + int minPrice = (int) 1e8; + for(Product product:products){ + if(product.getPrice() p.getName().equals(name)) + .findFirst().orElseThrow( + IllegalArgumentException::new + ); + } +} diff --git a/src/main/java/vendingmachine/view/MachineView.java b/src/main/java/vendingmachine/view/MachineView.java new file mode 100644 index 000000000..7258dca16 --- /dev/null +++ b/src/main/java/vendingmachine/view/MachineView.java @@ -0,0 +1,239 @@ +package vendingmachine.view; + +import camp.nextstep.edu.missionutils.Console; +import vendingmachine.model.Coin; +import vendingmachine.model.Coins; +import vendingmachine.model.Product; +import vendingmachine.model.Products; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class MachineView { + private static final String INPUT_CHANGE = "자판기가 보유하고 있는 금액을 입력해 주세요."; + private static final String NOW_CHANGE = System.lineSeparator() + "자판기가 보유한 동전" + System.lineSeparator(); + private static final String NOW_500 = "500원 - %d개" + System.lineSeparator(); + private static final String NOW_100 = "100원 - %d개" + System.lineSeparator(); + private static final String NOW_50 = "50원 - %d개" + System.lineSeparator(); + private static final String NOW_10 = "10원 - %d개" + System.lineSeparator(); + private static final String OUTPUT_CHANGE = NOW_CHANGE + NOW_500 + NOW_100 + NOW_50 + NOW_10 + System.lineSeparator(); + private static final String PRODUCT_NAME_AND_AMOUNT = "상품명과 가격, 수량을 입력해 주세요."; + private static final String INSERT_MONEY = System.lineSeparator() + "투입 금액을 입력해 주세요."; + private static final String NOW_MONEY = System.lineSeparator() + "투입 금액: %d원" + System.lineSeparator(); + private static final String INSERT_PRODUCT_NAME = "구매할 상품명을 입력해 주세요."; + private static final String FINAL_CHANGE = "잔돈"; + private static final String ERROR_PREFIX = "[ERROR] "; + private static final String IS_NOT_NUMBER = "금액은 숫자여야 합니다."; + private static final String WRONG_PATTERN = "입력형식이 맞지 않습니다."; + private static final String WRONG_PRODUCT = "존재하지 않는 상품입니다."; + private static final String MUST_BE_OVER_TEN = "10원 단위로만 입력이 가능합니다."; + private static final String PATTERN = "\\[[ㄱ-힣]+,\\d+,\\d+\\](;\\[[ㄱ-힣]+,\\d+,\\d+\\])*"; + + public void inputChange() { + System.out.println(INPUT_CHANGE); + } + + public int change() { + try { + return validateInt(Integer.parseInt(Console.readLine())); + } catch (NumberFormatException e) { + System.out.println(ERROR_PREFIX + IS_NOT_NUMBER); + return change(); + } + } + + private int validateInt(int parseInt) { + if(parseInt<0){ + throw new NumberFormatException(); + } + return parseInt; + } + + public void nowChange() { + System.out.println(NOW_CHANGE); + } + + public Products productNameAndAmount() { + System.out.println(PRODUCT_NAME_AND_AMOUNT); + return validateProductNameAndAmount(); + } + + public Products validateProductNameAndAmount() { + String input = Console.readLine(); + Pattern pattern = Pattern.compile(PATTERN); + Matcher matcher = pattern.matcher(input); + try { + if (!matcher.find()) { + throw new IllegalArgumentException(); + } + } catch (IllegalArgumentException e) { + System.out.println(ERROR_PREFIX + WRONG_PATTERN); + return productNameAndAmount(); + } + return createProducts(input, matcher); + } + + public Products createProducts(String input, Matcher matcher) { + List orders = Arrays.asList(input.split(";")); + List products = new ArrayList<>(); + for (String s : orders) { + List order = Arrays.asList(s.substring(1, s.length() - 1).split(",")); + Product product = new Product(order.get(0), Integer.parseInt(order.get(1)), Integer.parseInt(order.get(2))); + products.add(product); + } + return new Products(products); + } + + public int insertMoney() { + System.out.println(INSERT_MONEY); + int money = 0; + try { + money = validateMoney(Integer.parseInt(Console.readLine())); + } catch (NumberFormatException e) { + System.out.println(ERROR_PREFIX + IS_NOT_NUMBER); + } + return money; + } + + private int validateMoney(int input) { + int result = 0; + try { + if (input % 10 == 0) { + result = input; + } + if (input % 10 != 0) { + throw new IllegalArgumentException(); + } + } catch (IllegalArgumentException e) { + System.out.println(ERROR_PREFIX + MUST_BE_OVER_TEN); + return insertMoney(); + } + return result; + } + + public int nowMoneyAndInsertProductName(int money, Products products, Coins coins) { + int result = 0; + try { + if (money >= products.getMinPrice()) { + System.out.printf(NOW_MONEY, money); + System.out.println(INSERT_PRODUCT_NAME); + result = validateProduct(Console.readLine(), products, money, coins); + } + if (money < products.getMinPrice()) { + throw new IllegalArgumentException(); + } + } catch (IllegalArgumentException e) { + returnChange(money, coins); + } + return result; + } + + private int validateProduct(String input, Products products, int money, Coins coins) { + try { + validateAmount(money, products, input, coins); + validatePrice(money, products, input, coins); + return nowMoneyAndInsertProductName(money - products.getPrice(input), products, coins); + } catch (IllegalArgumentException e) { + System.out.println(ERROR_PREFIX + WRONG_PRODUCT); + return nowMoneyAndInsertProductName(money, products, coins); + } + } + + private void validatePrice(int money, Products products, String input, Coins coins) { + try { + if (products.findProduct(input).getPrice() > money) { + throw new IllegalArgumentException(); + } + } catch (IllegalArgumentException e) { + returnChange(money, coins); + } + } + + private void validateAmount(int money, Products products, String input, Coins coins) { + try { + if (products.findProduct(input).getAmount() == 0) { + throw new IllegalArgumentException(); + } + } catch (IllegalArgumentException e) { + returnChange(money, coins); + } + } + + private void returnChange(int money, Coins coins) { + System.out.printf(NOW_MONEY, money); + System.out.println(FINAL_CHANGE); + List coin = coins.getQuantitys(); + money = check500(money, coin.get(0)); + money = check100(money, coin.get(1)); + money = check50(money, coin.get(2)); + money = check10(money, coin.get(3)); + System.exit(0); + } + + private int check10(int money, Integer i) { + int cnt = 0; + while(money/Coin.COIN_10.getAmount()>0 && i>0){ + money-=Coin.COIN_10.getAmount(); + cnt++; + i--; + } + System.out.printf(NOW_10, cnt); + return money; + } + + private int check50(int money, Integer i) { + int cnt = 0; + while(money/Coin.COIN_50.getAmount()>0 && i>0){ + money-=Coin.COIN_50.getAmount(); + cnt++; + i--; + } + System.out.printf(NOW_50, cnt); + return money; + } + + private int check100(int money, Integer i) { + int cnt = 0; + while(money/Coin.COIN_100.getAmount()>0 && i>0){ + money-=Coin.COIN_100.getAmount(); + cnt++; + i--; + } + System.out.printf(NOW_100, cnt); + return money; + } + + private int check500(int money, int i) { + int cnt = 0; + while(money/Coin.COIN_500.getAmount()>0 && i>0){ + money-=Coin.COIN_500.getAmount(); + cnt++; + i--; + } + System.out.printf(NOW_500, cnt); + return money; + } + + public void insertProductName() { + System.out.println(INSERT_PRODUCT_NAME); + } + + public void finalChange() { + System.out.println(FINAL_CHANGE); + } + + public void outputCoins(List coins) { + System.out.printf(OUTPUT_CHANGE, coins.toArray()); + } + + public int insertProducts(Products products, Coins coins) { + int money = insertMoney(); + do { + money = nowMoneyAndInsertProductName(money, products, coins); + } while (money < products.getMinPrice()); + return money; + } +} diff --git a/src/test/java/vendingmachine/ApplicationTest.java b/src/test/java/vendingmachine/ApplicationTest.java index 4bec0f51e..e5fd1c802 100644 --- a/src/test/java/vendingmachine/ApplicationTest.java +++ b/src/test/java/vendingmachine/ApplicationTest.java @@ -11,7 +11,7 @@ class ApplicationTest extends NsTest { private static final String ERROR_MESSAGE = "[ERROR]"; @Test - void 기능_테스트() { + void functionTest() { assertRandomNumberInListTest( () -> { run("450", "[콜라,1500,20];[사이다,1000,10]", "3000", "콜라", "사이다");