diff --git a/README.md b/README.md index 86699576c..6dfeaf59b 100644 --- a/README.md +++ b/README.md @@ -1,46 +1,22 @@ # 미션 - 자판기 -## 🔍 진행방식 - -- 미션은 **기능 요구사항, 프로그래밍 요구사항, 과제 진행 요구사항** 세 가지로 구성되어 있다. -- 세 개의 요구사항을 만족하기 위해 노력한다. 특히 기능을 구현하기 전에 기능 목록을 만들고, 기능 단위로 커밋 하는 방식으로 진행한다. -- 기능 요구사항에 기재되지 않은 내용은 스스로 판단하여 구현한다. - -## ✉️ 미션 제출 방법 - -- 미션 구현을 완료한 후 GitHub을 통해 제출해야 한다. - - GitHub을 활용한 제출 방법은 [프리코스 과제 제출 문서](https://github.com/woowacourse/woowacourse-docs/tree/master/precourse) 를 참고해 제출한다. -- GitHub에 미션을 제출한 후 [우아한테크코스 지원 플랫폼](https://apply.techcourse.co.kr) 에 접속하여 프리코스 과제를 제출한다. - - 자세한 방법은 [링크](https://github.com/woowacourse/woowacourse-docs/tree/master/precourse#제출-가이드) 를 참고한다. - - **Pull Request만 보내고, 지원 플랫폼에서 과제를 제출하지 않으면 최종 제출하지 않은 것으로 처리되니 주의한다.** - -## ✔️ 과제 제출 전 체크리스트 - 0점 방지 - -- 터미널에서 `java -version`을 실행해 자바 8인지 확인한다. 또는 Eclipse, IntelliJ IDEA와 같은 IDE의 자바 8로 실행하는지 확인한다. -- 터미널에서 맥 또는 리눅스 사용자의 경우 `./gradlew clean test`, 윈도우 사용자의 경우 `gradlew.bat clean test` 명령을 실행했을 때 모든 테스트가 아래와 같이 통과하는지 확인한다. - -``` -BUILD SUCCESSFUL in 0s -``` - ---- +반환되는 동전이 최소한이 되는 자판기를 구현한다. ## 🚀 기능 요구사항 -반환되는 동전이 최소한이 되는 자판기를 구현한다. - - 자판기가 보유하고 있는 금액으로 동전을 무작위로 생성한다. - - 투입 금액으로는 동전을 생성하지 않는다. + - 투입 금액으로는 동전을 생성하지 않는다. - 잔돈을 돌려줄 때 현재 보유한 최소 개수의 동전으로 잔돈을 돌려준다. - 지폐를 잔돈으로 반환하는 경우는 없다고 가정한다. - 상품명, 가격, 수량을 입력하여 상품을 추가할 수 있다. - - 상품 가격은 100원부터 시작하며, 10원으로 나누어떨어져야 한다. + - 상품 가격은 100원부터 시작한다. + - 상품 가격은 10원으로 나누어떨어져야 한다. - 사용자가 투입한 금액으로 상품을 구매할 수 있다. - 남은 금액이 상품의 최저 가격보다 적거나, 모든 상품이 소진된 경우 바로 잔돈을 돌려준다. - 잔돈을 반환할 수 없는 경우 잔돈으로 반환할 수 있는 금액만 반환한다. - - 반환되지 않은 금액은 자판기에 남는다. -- 사용자가 잘못된 값을 입력할 경우 `IllegalArgumentException`를 발생시키고, "[ERROR]"로 시작하는 에러 메시지를 출력 후 해당 부분부터 다시 입력을 받는다. -- 아래의 프로그래밍 실행 결과 예시와 동일하게 입력과 출력이 이루어져야 한다. + - 반환되지 않은 금액은 자판기에 남는다. +- 사용자가 잘못된 값을 입력할 경우 `IllegalArgumentException`를 발생시키고, "[ERROR]"로 시작하는 에러 메시지를 출력 후 해당 부분부터 다시 입력을 + 받는다. ### ✍🏻 입출력 요구사항 @@ -106,62 +82,4 @@ BUILD SUCCESSFUL in 0s 잔돈 100원 - 4개 50원 - 1개 -``` - ---- - -## 🎱 프로그래밍 요구사항 - -- 프로그램을 실행하는 시작점은 `Application`의 `main()`이다. -- JDK 8 버전에서 실행 가능해야 한다. **JDK 8에서 정상 동작하지 않을 경우 0점 처리**한다. -- 자바 코드 컨벤션을 지키면서 프로그래밍한다. - - https://naver.github.io/hackday-conventions-java -- indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다. - - 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다. - - 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메소드)를 분리하면 된다. -- 3항 연산자를 쓰지 않는다. -- 함수(또는 메소드)의 길이가 15라인을 넘어가지 않도록 구현한다. - - 함수(또는 메소드)가 한 가지 일만 잘 하도록 구현한다. -- else 예약어를 쓰지 않는다. - - 힌트: if 조건절에서 값을 return하는 방식으로 구현하면 else를 사용하지 않아도 된다. - - else를 쓰지 말라고 하니 switch/case로 구현하는 경우가 있는데 switch/case도 허용하지 않는다. -- 프로그래밍 요구사항에서 별도로 변경 불가 안내가 없는 경우 파일 수정과 패키지 이동을 자유롭게 할 수 있다. - -### 프로그래밍 요구사항 - Coin - -- Coin 클래스를 활용해 구현해야 한다. -- 필드(인스턴스 변수)인 `amount`의 접근 제어자 private을 변경할 수 없다. - -```java -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; - } - - // 추가 기능 구현 -} -``` - -### 프로그래밍 요구사항 - Randoms, Console - -- JDK에서 기본 제공하는 Random, Scanner API 대신 `camp.nextstep.edu.missionutils`에서 제공하는 `Randoms`, `Console` API를 활용해 구현해야 한다. - - Random 값 추출은 `camp.nextstep.edu.missionutils.Randoms`의 `pickNumberInList()`를 활용한다. - - 사용자가 입력하는 값은 `camp.nextstep.edu.missionutils.Console`의 `readLine()`을 활용한다. -- 프로그램 구현을 완료했을 때 `src/test/java` 디렉터리의 `ApplicationTest`에 있는 모든 테스트 케이스가 성공해야 한다. **테스트가 실패할 경우 0점 처리한다.** - ---- - -## 📈 과제 진행 요구사항 - -- 미션은 [java-vendingmachine-precourse](https://github.com/woowacourse/java-vendingmachine-precourse) 저장소를 Fork/Clone해 시작한다. -- **기능을 구현하기 전에 java-vendingmachine-precourse/docs/README.md 파일에 구현할 기능 목록을 정리**해 추가한다. -- **Git의 커밋 단위는 앞 단계에서 README.md 파일에 정리한 기능 목록 단위**로 추가한다. - - [AngularJS Commit Message Conventions](https://gist.github.com/stephenparish/9941e89d80e2bc58a153) 참고해 commit log를 남긴다. -- 과제 진행 및 제출 방법은 [프리코스 과제 제출 문서](https://github.com/woowacourse/woowacourse-docs/tree/master/precourse) 를 참고한다. +``` \ No newline at end of file diff --git a/src/main/java/vendingmachine/Application.java b/src/main/java/vendingmachine/Application.java index 9d3be447b..f635ec366 100644 --- a/src/main/java/vendingmachine/Application.java +++ b/src/main/java/vendingmachine/Application.java @@ -1,7 +1,21 @@ package vendingmachine; +import vendingmachine.controller.CoinController; +import vendingmachine.controller.MoneyController; +import vendingmachine.controller.OrderController; +import vendingmachine.controller.ProductController; +import vendingmachine.model.Products; + public class Application { public static void main(String[] args) { - // TODO: 프로그램 구현 + CoinController coinController = new CoinController(); + ProductController productController = new ProductController(); + MoneyController moneyController = new MoneyController(); + OrderController orderController = new OrderController(); + + int[] coins = coinController.initMoney(); + Products products = productController.initStock(); + int money = moneyController.receiveMoney(); + orderController.makeOrder(products, money); } } diff --git a/src/main/java/vendingmachine/controller/CoinController.java b/src/main/java/vendingmachine/controller/CoinController.java new file mode 100644 index 000000000..33bab4fb9 --- /dev/null +++ b/src/main/java/vendingmachine/controller/CoinController.java @@ -0,0 +1,62 @@ +package vendingmachine.controller; + +import static camp.nextstep.edu.missionutils.Randoms.pickNumberInList; +import static vendingmachine.view.InputView.inputInteger; +import static vendingmachine.view.OutputView.printCoins; +import static vendingmachine.view.OutputView.printHavingMoneyMessage; + +import java.util.ArrayList; +import java.util.List; +import vendingmachine.model.Coin; + +public class CoinController { + + private static final int size = Coin.values().length; + private static int[] coins = new int[size]; + + public int[] initMoney() { + printHavingMoneyMessage(); + return initCoins(inputInteger()); + } + + private int[] initCoins(int total) { + Coin[] coinType = Coin.values(); + int index = 0; + while (total > 0) { + //동전 합이 total이 될 때까지 loop + //0부터 size까지를 왔다갔다할 인덱스 + int coinPrice = coinType[index].getAmount(); + //현재 index에 해당하는 동전의 액면가 + int possibleCoin = total / coinPrice; + //현재 index의 동전의 최대 개수 + List numberRange = makeNumberRange(possibleCoin); + //pickNumberInList의 매개변수 타입에 맞게 0부터 possibleCoin까지의 모든 수를 넣은 List + if (possibleCoin > 0) { + //가능한 코인 개수가 있으면 + int pickedNumber = pickNumberInList(numberRange); + //랜덤 돌려서 + coins[index] += pickedNumber; + //코인 개수에 추가 + total -= coinPrice * pickedNumber; + //while문 조건에 반영하기 위해 동전개수 * 액면가를 total에서 제해줌 + } + index++; + if (index == size) { + //모든 종류의 코인 다 돌았으면 + index = 0; + //Index 초기화 + } + } + //보유 금액만큼 동전 생성 후 출력 + printCoins(coins); + return coins; + } + + private static List makeNumberRange(int possibleCoin) { + List numberRange = new ArrayList<>(); + for (int i = 0; i <= possibleCoin; i++) { + numberRange.add(i); + } + return numberRange; + } +} diff --git a/src/main/java/vendingmachine/controller/MoneyController.java b/src/main/java/vendingmachine/controller/MoneyController.java new file mode 100644 index 000000000..07e458f79 --- /dev/null +++ b/src/main/java/vendingmachine/controller/MoneyController.java @@ -0,0 +1,12 @@ +package vendingmachine.controller; + +import static vendingmachine.view.InputView.inputInteger; +import static vendingmachine.view.OutputView.printPurchasingMoneyMessage; + +public class MoneyController { + + public int receiveMoney() { + printPurchasingMoneyMessage(); + return inputInteger(); + } +} diff --git a/src/main/java/vendingmachine/controller/OrderController.java b/src/main/java/vendingmachine/controller/OrderController.java new file mode 100644 index 000000000..b81a743f8 --- /dev/null +++ b/src/main/java/vendingmachine/controller/OrderController.java @@ -0,0 +1,30 @@ +package vendingmachine.controller; + +import static vendingmachine.controller.ProductController.minPrice; +import static vendingmachine.controller.ProductController.validateEmptyStock; +import static vendingmachine.model.Products.getProductByName; +import static vendingmachine.view.InputView.input; +import static vendingmachine.view.OutputView.printPurchasingItemMessage; +import static vendingmachine.view.OutputView.printPurchasingMoney; + +import vendingmachine.model.Product; +import vendingmachine.model.Products; + +public class OrderController { + + public void makeOrder(Products products, int purchasingMoney) { + while (validateEmptyStock(products) && purchasingMoney > minPrice(products)) { + printPurchasingMoney(purchasingMoney); + //투입금액(남은금액) 출력 + printPurchasingItemMessage(); + String purchasingItemName = input(); + Product purchasingItem = getProductByName(purchasingItemName); + purchasingMoney -= purchasingItem.getPrice(); + } + giveChange(); + } + + public void giveChange() { + + } +} diff --git a/src/main/java/vendingmachine/controller/ProductController.java b/src/main/java/vendingmachine/controller/ProductController.java new file mode 100644 index 000000000..37a027d0d --- /dev/null +++ b/src/main/java/vendingmachine/controller/ProductController.java @@ -0,0 +1,47 @@ +package vendingmachine.controller; + +import static vendingmachine.view.InputView.input; +import static vendingmachine.view.OutputView.printOrderMessage; + +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; +import vendingmachine.model.Product; +import vendingmachine.model.Products; + +public class ProductController { + + public Products initStock() { + printOrderMessage(); + return makeProductList(); + } + + private Products makeProductList() { + String stockInput = input(); + StringTokenizer semicolonSt = new StringTokenizer(stockInput, ";"); + List products = new ArrayList<>(); + return processProductInput(semicolonSt, products); + } + + private Products processProductInput(StringTokenizer semicolonSt, List productList) { + while (semicolonSt.hasMoreTokens()) { + String processingInput = semicolonSt.nextToken(); + processingInput = processingInput.replace("[", "").replace("]", ""); + String name = processingInput.split(",")[0]; + int price = Integer.parseInt(processingInput.split(",")[1]); + int stock = Integer.parseInt(processingInput.split(",")[2]); + productList.add(new Product(name, price, stock)); + } + return new Products(productList); + } + + public static boolean validateEmptyStock(Products products) { + //재고가 하나라도 있으면 진행 가능 + return products.getProducts().stream().anyMatch(product -> product.getStock() > 0); + } + + public static int minPrice(Products products) { + return products.getProducts().stream().mapToInt(Product::getPrice).min() + .orElse(Integer.MAX_VALUE); + } +} diff --git a/src/main/java/vendingmachine/Coin.java b/src/main/java/vendingmachine/model/Coin.java similarity index 68% rename from src/main/java/vendingmachine/Coin.java rename to src/main/java/vendingmachine/model/Coin.java index c76293fbc..6779e7d40 100644 --- a/src/main/java/vendingmachine/Coin.java +++ b/src/main/java/vendingmachine/model/Coin.java @@ -1,4 +1,4 @@ -package vendingmachine; +package vendingmachine.model; public enum Coin { COIN_500(500), @@ -12,5 +12,7 @@ public enum Coin { this.amount = amount; } - // 추가 기능 구현 + public int getAmount() { + return amount; + } } diff --git a/src/main/java/vendingmachine/model/Product.java b/src/main/java/vendingmachine/model/Product.java new file mode 100644 index 000000000..b746cade9 --- /dev/null +++ b/src/main/java/vendingmachine/model/Product.java @@ -0,0 +1,30 @@ +package vendingmachine.model; + +public class Product { + + private String name; + private int price; + private int stock; + + public Product(String name, int price, int stock) { + this.name = name; + this.price = price; + this.stock = stock; + } + + public String getName() { + return name; + } + + public int getPrice() { + return price; + } + + public int getStock() { + return stock; + } + + public void reduceStock() { + this.stock -= 1; + } +} diff --git a/src/main/java/vendingmachine/model/Products.java b/src/main/java/vendingmachine/model/Products.java new file mode 100644 index 000000000..43cde67b6 --- /dev/null +++ b/src/main/java/vendingmachine/model/Products.java @@ -0,0 +1,21 @@ +package vendingmachine.model; + +import java.util.List; + +public class Products { + + private static List products; + + public Products(List productList) { + this.products = productList; + } + + public List getProducts() { + return products; + } + + public static Product getProductByName(String name) { + return products.stream().filter(product -> product.getName().equals(name)).findFirst() + .orElse(null); + } +} diff --git a/src/main/java/vendingmachine/view/InputView.java b/src/main/java/vendingmachine/view/InputView.java new file mode 100644 index 000000000..ecff4dbb8 --- /dev/null +++ b/src/main/java/vendingmachine/view/InputView.java @@ -0,0 +1,14 @@ +package vendingmachine.view; + +import static camp.nextstep.edu.missionutils.Console.readLine; + +public class InputView { + + public static int inputInteger() { + return Integer.parseInt(input()); + } + + public static String input() { + return 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..29c94d708 --- /dev/null +++ b/src/main/java/vendingmachine/view/OutputView.java @@ -0,0 +1,41 @@ +package vendingmachine.view; + +import vendingmachine.model.Coin; + +public class OutputView { + + private static final String HAVING_MONEY_MESSAGE = "자판기가 보유하고 있는 금액을 입력해 주세요."; + private static final String HAVING_COIN_MESSAGE = "\n자판기가 보유한 동전"; + private static final String ORDER_MESSAGE = "\n상품명과 가격, 수량을 입력해 주세요."; + private static final String PURCHASING_MONEY_MESSAGE = "\n투입 금액을 입력해 주세요."; + private static final String PURCHASING_MONEY = "\n투입 금액: %d원"; + private static final String PURCHASING_ITEM_MASSAGE = "구매할 상품명을 입력해 주세요."; + private static final String printForm = "%d원 - %d개"; + + public static void printHavingMoneyMessage() { + System.out.println(HAVING_MONEY_MESSAGE); + } + + public static void printCoins(int[] coins) { + System.out.println(HAVING_COIN_MESSAGE); + for (int i = 0; i < coins.length; i++) { + System.out.println(String.format(printForm, Coin.values()[i].getAmount(), coins[i])); + } + } + + public static void printOrderMessage() { + System.out.println(ORDER_MESSAGE); + } + + public static void printPurchasingMoneyMessage() { + System.out.println(PURCHASING_MONEY_MESSAGE); + } + + public static void printPurchasingMoney(int money) { + System.out.println(String.format(PURCHASING_MONEY, money)); + } + + public static void printPurchasingItemMessage() { + System.out.println(PURCHASING_ITEM_MASSAGE); + } +}