Skip to content

[자판기] 김연진 미션 제출합니다. #4

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

Open
wants to merge 31 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7c70f78
docs: 기능 구현 목록 작성
homebdy Sep 21, 2023
928dc98
feat(OutputView): 자판기 보유 금액 메시지 출력
homebdy Sep 21, 2023
051a712
feat(InputView): 자판기 보유 금액 입력
homebdy Sep 21, 2023
58ee8ae
feat(Coins): 입력받은 보유 금액에 따라 동전 개수 랜덤 생성
homebdy Sep 21, 2023
ca55248
feat(Coins): 자판기가 보유하고 있는 동전 개수 출력
homebdy Sep 21, 2023
9c6fe5f
feat(OutputView): 상품 입력 메시지 출력
homebdy Sep 21, 2023
768cf7b
feat(InputView): 상품 입력 기능 구현
homebdy Sep 21, 2023
275bf4d
feat(Products): 상품들을 관리하는 객체 생성
homebdy Sep 21, 2023
eb4ed3d
feat(OutputView): 투입 금액 입력 안내 메시지 출력
homebdy Sep 21, 2023
901c056
feat(InputView): 투입 금액 입력 기능 구현
homebdy Sep 21, 2023
b01eb7f
feat(MachineController): 게임 진행 메서드 분리
homebdy Sep 21, 2023
cfc2108
feat(Money): 투입 금액 출력
homebdy Sep 21, 2023
b2b3ddc
feat(OutputView): 상품 구매 메세지 출력
homebdy Sep 21, 2023
b8c2867
feat(InputView): 구매할 상품명 입력
homebdy Sep 21, 2023
ce0ec1e
feat(Products): 입력받은 상품이 있는지 확인
homebdy Sep 21, 2023
ec03897
feat(Product): 상품 구매시 구매한 양만큼 개수 차감
homebdy Sep 21, 2023
6a3434a
feat(Money): 상품 계산 기능 구현
homebdy Sep 21, 2023
552b508
feat(Money): 보유한 금액이 상품의 가격보다 작은 경우 예외 발생
homebdy Sep 21, 2023
f45d11a
feat(MachineService): 상품 구매 기능 구현
homebdy Sep 21, 2023
86be7f1
feat(MachineService): 남은 금액이 상품의 최저 가격보다 적거나, 모든 상품이 소진된 경우 종료
homebdy Sep 21, 2023
8565902
feat: 잔돈 출력 기능 구현
homebdy Sep 21, 2023
5440687
feat(MachineController): 오류 발생 지점부터 재시작
homebdy Sep 21, 2023
370148d
feat(Price): 상품 가격 입력 오류 처리
homebdy Sep 21, 2023
ec97ffc
feat(OutputView): 줄 바꿈 출력
homebdy Sep 21, 2023
bff4db8
feat(MachineService): 상품명으로 상품 찾는 기능 구현
homebdy Sep 21, 2023
e6c33e7
test(Money): 상품 구매 테스트
homebdy Sep 22, 2023
940faa6
test(Price): 제품 가격 입력 테스트
homebdy Sep 22, 2023
1493d7a
feat(InputValidator): 입력값 검증 기능 구현
homebdy Sep 22, 2023
db6d890
test(InputValidator): 입력값 검증 테스트
homebdy Sep 22, 2023
238f450
refactor(Coins): 거스름돈 계산 메서드 분리
homebdy Sep 25, 2023
055b294
refactor(Coin): getter 제거
homebdy Sep 25, 2023
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
19 changes: 19 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
## 기능 구현 목록

- [x] 자판기 보유 금액을 입력한다.
- [x] 10원 단위로 나누어 떨어지지 않을 경우 예외 발생
- [x] 입력받은 보유 금액에 따라 동전 개수 랜덤 생성
- [x] 자판기가 보유하고 있는 동전의 개수를 출력한다.
- [x] 자판기에 상품을 입력한다.
- [x] 상품의 가격이 100원이하일 경우 예외
- [x] 상품의 가격이 10원 단위로 나누어 떨어지지 않을 경우 예외
- [x] 사용자가 투입할 금액을 입력한다.
- [x] 구매할 상품명 입력
- [x] 해당 상품이 존재하지 않을 경우 예외
- [ ] 상품 구매
- [x] 입력받은 상품이 있는지 확인
- [x] 금액 계산
- [x] 보유한 금액이 상품의 가격보다 작은 경우 예외 발생
- [x] 상품 개수 하나 줄이기
- [x] 남은 금액이 상품의 최저 가격보다 적거나, 모든 상품이 소진된 경우 종료
- [x] 잔돈 출력
5 changes: 4 additions & 1 deletion src/main/java/vendingmachine/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package vendingmachine;

import vendingmachine.controller.MachineController;

public class Application {
public static void main(String[] args) {
// TODO: 프로그램 구현
MachineController controller = new MachineController();
controller.start();
}
}
16 changes: 0 additions & 16 deletions src/main/java/vendingmachine/Coin.java

This file was deleted.

17 changes: 17 additions & 0 deletions src/main/java/vendingmachine/constant/Constant.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package vendingmachine.constant;

public enum Constant {

PRICE_UNIT(10),
MIN_PRICE(100);

private final int value;

Constant(int value) {
this.value = value;
}

public int getValue() {
return value;
}
}
24 changes: 24 additions & 0 deletions src/main/java/vendingmachine/constant/ExceptionMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package vendingmachine.constant;


public enum ExceptionMessage {

NOT_EXIST_PRODUCT("존재하지 않는 상품입니다."),
NOT_ENOUGH_MONEY("보유한 금액이 부족해 상품을 구매할 수 없습니다."),
INVALID_UNIT("10원 단위로 입력해야합니다."),
MIN_PRICE("상품의 가격은 100원 이상부터 입력할 수 있습니다."),
INCORRECT_FORMAT("형식에 맞춰 입력해주십시오."),
NOT_INTEGER("숫자로 입력해야 합니다."),;

private static final String PREFIX = "[ERROR] ";
private final String message;

ExceptionMessage(String message) {
this.message = message;
}

@Override
public String toString() {
return PREFIX + message;
}
}
23 changes: 23 additions & 0 deletions src/main/java/vendingmachine/constant/OutputMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package vendingmachine.constant;

public enum OutputMessage {

READ_MACHINE_CHANGE("자판기가 보유하고 있는 금액을 입력해 주세요."),
CHANGE_MESSAGE("자판기가 보유한 동전"),
CHANGE("%d원 - %d개\n"),
READ_PRODUCT("상품명과 가격, 수량을 입력해 주세요."),
READ_INPUT_AMOUNT("투입 금액을 입력해 주세요."),
INPUT_AMOUNT("투입 금액: %d원"),
PURCHASE_GOODS("구매할 상품명을 입력해 주세요.");

private final String message;

OutputMessage(String message) {
this.message = message;
}

@Override
public String toString() {
return message;
}
}
80 changes: 80 additions & 0 deletions src/main/java/vendingmachine/controller/MachineController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package vendingmachine.controller;

import vendingmachine.domain.Coins;
import vendingmachine.domain.Money;
import vendingmachine.domain.Product;
import vendingmachine.domain.Products;
import vendingmachine.service.MachineService;
import vendingmachine.view.InputView;
import vendingmachine.view.OutputView;

import java.util.function.Supplier;

public class MachineController {

private final InputView inputView = new InputView();
private final OutputView outputView = new OutputView();
private MachineService service;

public void start() {
service = new MachineService(readChangePrice(), readProducts(), readMoney());
while (service.isOnSale()) {
purchaseGoods();
}
outputView.printChange(service.calculateChange());
}

private Coins readChangePrice() {
return readInput(() -> {
outputView.printChangeMessage();
Coins coins = inputView.readChangePrice();
outputView.printNewLine();
outputView.printChange(coins);
return coins;
});
}

private Products readProducts() {
return readInput(() -> {
outputView.printProduct();
Products products = inputView.readProducts();
outputView.printNewLine();
return products;
});
}

private Money readMoney() {
return readInput(() -> {
outputView.printInputAmountMessage();
Money money = inputView.readInputAmount();
outputView.printNewLine();
return money;
});
}

private void purchaseGoods() {
readInput(() -> {
outputView.printInputAmount(service.getMoney());
service.purchaseProduct(readProduct());
return null;
});
}

private Product readProduct() {
return readInput(() -> {
outputView.printPurchaseGoods();
String productName = inputView.readProduct();
outputView.printNewLine();
return service.findProductByName(productName);
});
}

private <T> T readInput(Supplier<T> supplier) {
try {
return supplier.get();
} catch (IllegalArgumentException e) {
outputView.printExceptionMessage(e.getMessage());
return supplier.get();
}
}
}
49 changes: 49 additions & 0 deletions src/main/java/vendingmachine/domain/Coin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package vendingmachine.domain;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

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;
}

public static List<Integer> getCoinKind() {
return Arrays.stream(Coin.values())
.map(coin -> coin.amount)
.sorted()
.collect(Collectors.toList());
}

public static Coin getCoin(int value) {
return Arrays.stream(Coin.values())
.filter(coin -> coin.amount == value)
.findAny()
.orElseThrow(() -> new IllegalArgumentException(""));

}

public int getPrice() {
return this.amount;
}

public boolean isChangeable(int price) {
return price > this.amount;
}

public int calculateTotalAmount(int count) {
return this.amount * count;
}

public int calculateCoinCount(int money) {
return money % this.amount;
}
}
102 changes: 102 additions & 0 deletions src/main/java/vendingmachine/domain/Coins.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package vendingmachine.domain;

import camp.nextstep.edu.missionutils.Randoms;
import vendingmachine.constant.Constant;
import vendingmachine.constant.ExceptionMessage;
import vendingmachine.constant.OutputMessage;

import java.util.EnumMap;
import java.util.Map;

public class Coins {

private static final int DEFAULT = 0;
private final Map<Coin, Integer> elements;

public Coins(int price) {
this.elements = new EnumMap<>(Coin.class);
validatePrice(price);
Coin.getCoinKind()
.forEach(value -> elements.put(Coin.getCoin(value), DEFAULT));
createRandomCoins(price);
}

private void validatePrice(int price) {
if (price % Constant.PRICE_UNIT.getValue() != 0) {
throw new IllegalArgumentException(ExceptionMessage.INVALID_UNIT.toString());
}
}
private void createRandomCoins(int price) {
while (price > 0) {
int value = Randoms.pickNumberInList(Coin.getCoinKind());
if (value <= price) {
price -= value;
Coin coin = Coin.getCoin(value);
elements.put(coin, elements.get(coin)+1);
}
}
}

public String getChangeCoins() {
StringBuilder sb = new StringBuilder();
elements.keySet()
.forEach(value -> {
String message = OutputMessage.CHANGE.toString();
sb.append(String.format(message, value.getPrice(), elements.get(value)));
}
);
return sb.toString();
}

public String getChange(int money) {
Map<Coin, Integer> change = new EnumMap<>(Coin.class);
calculateChange(change, money);
StringBuilder sb = new StringBuilder();
return makeScreen(change, sb);
}

private void calculateChange(Map<Coin, Integer> change, int money) {
for (Coin coin: Coin.values()) {
int coinNumber = returnChange(coin, money, change);
money -= coin.calculateTotalAmount(coinNumber);
if (money <= 0 || isExistChange()) {
break;
}
}
}

private int returnChange(Coin coin, int money, Map<Coin, Integer> change) {
int coinNumber = 0;
if (coin.isChangeable(money)) {
coinNumber = calculateCoinNumber(money, coin);
elements.put(coin, elements.get(coin) - coinNumber);
change.put(coin, coinNumber);
}
return coinNumber;
}

private String makeScreen(Map<Coin, Integer> change, StringBuilder sb) {
change.keySet()
.forEach(value -> {
String message = OutputMessage.CHANGE.toString();
sb.append(String.format(message, value.getPrice(), change.get(value)));
}
);
return sb.toString();
}

private boolean isExistChange() {
return elements.values()
.stream()
.mapToInt(i -> i)
.sum() <= 0;
}

private int calculateCoinNumber(int money, Coin coin) {
int num = coin.calculateCoinCount(money);
if (elements.get(coin) > num) {
return elements.get(coin);
}
return num;
}
}
36 changes: 36 additions & 0 deletions src/main/java/vendingmachine/domain/Money.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package vendingmachine.domain;

import vendingmachine.constant.ExceptionMessage;
import vendingmachine.constant.OutputMessage;

public class Money {

private int value;

public Money(int value) {
this.value = value;
}

public void pay(int price) {
validateLeftMoney(price);
value -= price;
}

public void validateLeftMoney(int price) {
if (price > value) {
throw new IllegalArgumentException(ExceptionMessage.NOT_ENOUGH_MONEY.toString());
}
}

public boolean isEnoughMoney(int minPrice) {
return value >= minPrice;
}

public String getInputAmount() {
return String.format(OutputMessage.INPUT_AMOUNT.toString() , value);
}

public int getValue() {
return value;
}
}
Loading