Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

자판기 미션 리뷰 부탁드립니다!! #193

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
b3834d0
feat: 리드미 수정
cwjjjjjj Nov 25, 2024
d6032eb
feat: getter 추가
cwjjjjjj Nov 25, 2024
58ddf68
feat: CoinController 추가
cwjjjjjj Nov 25, 2024
4b44d91
feat: InputView, OutputView 추가
cwjjjjjj Nov 25, 2024
d30e905
feat: Coins 추가
cwjjjjjj Nov 25, 2024
f23db6c
feat: 보유 금액만큼 동전 생성 로직 구현
cwjjjjjj Nov 26, 2024
3e26a3d
feat: Coins 삭제
cwjjjjjj Nov 26, 2024
e2e0cb8
remove: Coins 삭제
cwjjjjjj Nov 26, 2024
7e5c14b
Merge remote-tracking branch 'origin/main'
cwjjjjjj Nov 26, 2024
9fa0c91
feat: 보유 코인 출력 메서드 추가
cwjjjjjj Nov 26, 2024
70d42b8
feat: main에서 controller 인스턴스 초기화 및 보유 동전 설정
cwjjjjjj Nov 26, 2024
e9ab128
feat: 프로그램 흐름 조정하는 코드 추가
cwjjjjjj Nov 26, 2024
d477652
feat: return 타입 수정
cwjjjjjj Nov 26, 2024
9c838df
feat: int 변환 메소드 추가
cwjjjjjj Nov 26, 2024
0465174
feat: 출력 메소드 추가
cwjjjjjj Nov 26, 2024
a09f743
feat: 투입 금액 입력 클래스
cwjjjjjj Nov 26, 2024
60ffbb1
feat: 재고 상품의 데이터 담는 클래스
cwjjjjjj Nov 26, 2024
027e16b
feat: 재고 상품 집합 관리하는 클래스
cwjjjjjj Nov 26, 2024
2e708f5
feat: 재고 상품 집합 controller
cwjjjjjj Nov 26, 2024
bb6a233
feat: 주문 관련 controller
cwjjjjjj Nov 26, 2024
89b32e2
feat: 일정 조건 하에서 주문 계속 반복되는 로직 구현
cwjjjjjj Nov 26, 2024
565f565
feat: 투입금액과 안내문구 출력 메서드
cwjjjjjj Nov 26, 2024
81e10ce
feat: 재고 여부 확인, 가격 중 최소값 확인 메서드
cwjjjjjj Nov 26, 2024
02e8e9d
Merge remote-tracking branch 'origin/main'
cwjjjjjj Nov 26, 2024
02adde9
feat: static 키워드 추가
cwjjjjjj Nov 26, 2024
bc44def
feat: 리드미 수정
cwjjjjjj Nov 26, 2024
cb8df9b
Merge remote-tracking branch 'origin/main'
cwjjjjjj Nov 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 8 additions & 90 deletions README.md

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

다음번에는 시간을 재고 해보는 것도 좋을 것 같아요!

Original file line number Diff line number Diff line change
@@ -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]"로 시작하는 에러 메시지를 출력 후 해당 부분부터 다시 입력을
받는다.

### ✍🏻 입출력 요구사항

Expand Down Expand Up @@ -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) 를 참고한다.
```
16 changes: 15 additions & 1 deletion src/main/java/vendingmachine/Application.java
Original file line number Diff line number Diff line change
@@ -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();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Service나 Domain 계층에 책임을 위임하지 않고, Controller에 구현하신 우진님만의 의도가 궁금합니다!

Comment on lines +11 to +14

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

컨트롤러가 많네요..
구현과 동시에 리팩토링을 추구하신 건가요??


int[] coins = coinController.initMoney();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Collection이 아닌 배열을 사용하신 이유가 궁금합니다!

Products products = productController.initStock();
int money = moneyController.receiveMoney();
orderController.makeOrder(products, money);
}
}
62 changes: 62 additions & 0 deletions src/main/java/vendingmachine/controller/CoinController.java
Original file line number Diff line number Diff line change
@@ -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<Integer> numberRange = makeNumberRange(possibleCoin);
//pickNumberInList의 매개변수 타입에 맞게 0부터 possibleCoin까지의 모든 수를 넣은 List
if (possibleCoin > 0) {
//가능한 코인 개수가 있으면
int pickedNumber = pickNumberInList(numberRange);
//랜덤 돌려서
coins[index] += pickedNumber;
//코인 개수에 추가
total -= coinPrice * pickedNumber;
//while문 조건에 반영하기 위해 동전개수 * 액면가를 total에서 제해줌
Comment on lines +26 to +41

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

주석은 설계 과정에서 스스로 쉽게 알아보기 위해 작성한 건가요?!

}
index++;
if (index == size) {
//모든 종류의 코인 다 돌았으면
index = 0;
//Index 초기화
}
}
//보유 금액만큼 동전 생성 후 출력
printCoins(coins);
return coins;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

주석을 남겨두신 이유가 궁금해요!🧐


private static List<Integer> makeNumberRange(int possibleCoin) {
List<Integer> numberRange = new ArrayList<>();
for (int i = 0; i <= possibleCoin; i++) {
numberRange.add(i);
}
return numberRange;
}
}
12 changes: 12 additions & 0 deletions src/main/java/vendingmachine/controller/MoneyController.java
Original file line number Diff line number Diff line change
@@ -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();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Console과 같은 라이브러리뿐만 아니라, InputView와 같은 클래스까지 모두 static import 하신 의도가 궁금합니다!
InputView를 명시해주는 편이 해당 클래스 내에서 역할 및 관계를 명확히 이해할 수 있다고 생각하는데 우진님의 의견은 어떠신가요??

}
}
Comment on lines +6 to +12

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

컨트롤러 분리가 조금 과한 감이 있는 것 같아요. 코드가 많이 길어지면 그 때 가서 분리해도 좋을 것 같습니다!

30 changes: 30 additions & 0 deletions src/main/java/vendingmachine/controller/OrderController.java
Original file line number Diff line number Diff line change
@@ -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() {

}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분은 아직 작성되지 않은건가요?!

}
47 changes: 47 additions & 0 deletions src/main/java/vendingmachine/controller/ProductController.java
Original file line number Diff line number Diff line change
@@ -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<Product> products = new ArrayList<>();
return processProductInput(semicolonSt, products);
}

private Products processProductInput(StringTokenizer semicolonSt, List<Product> 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);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

재고 확인 검증을 Domain이 아닌 Controller에서 하신 이유가 궁금합니다!


public static int minPrice(Products products) {
return products.getProducts().stream().mapToInt(Product::getPrice).min()
.orElse(Integer.MAX_VALUE);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package vendingmachine;
package vendingmachine.model;

public enum Coin {
COIN_500(500),
Expand All @@ -12,5 +12,7 @@ public enum Coin {
this.amount = amount;
}

// 추가 기능 구현
public int getAmount() {
return amount;
}
}
30 changes: 30 additions & 0 deletions src/main/java/vendingmachine/model/Product.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
21 changes: 21 additions & 0 deletions src/main/java/vendingmachine/model/Products.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package vendingmachine.model;

import java.util.List;

public class Products {

private static List<Product> products;

public Products(List<Product> productList) {
this.products = productList;
}

public List<Product> getProducts() {
return products;
}

public static Product getProductByName(String name) {
return products.stream().filter(product -> product.getName().equals(name)).findFirst()
.orElse(null);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

상품 이름으로 Product를 반환하는 메서드라면, findAny()를 사용하는게 더 좋지 않을까요?? 우진님의 의견은 어떠신지 궁금해요!

}
14 changes: 14 additions & 0 deletions src/main/java/vendingmachine/view/InputView.java
Original file line number Diff line number Diff line change
@@ -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();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

항상 Console.readLint()을 사용해왔는데, 해당 라인처럼 더 간결하게 표현할 수 있어서 좋은 것 같아요!👍

}
}
Loading