diff --git a/docs/README.md b/docs/README.md index e69de29bb2..923e897001 100644 --- a/docs/README.md +++ b/docs/README.md @@ -0,0 +1,93 @@ +# 구현할 기능 목록 +[x] 랜덤 번호 생성 및 저장 기능 +[x] 구입 금액 입력 기능 +[x] 구입 금액 입력값 검증 기능 +[x] 로또 발행 개수 계산 기능 +[x] 로또 발행 기능 +[x] 발행한 로또 수량 및 번호 출력 기능 +[x] 당첨 번호 입력 기능 +[x] 당첨 번호 입력값 검증 기능 +[x] 번호 일치 여부 확인 및 당첨 여부 확인 기능 +[x] 당첨 내역 출력 기능 +[x] 수익률 계산 및 반올림 기능 +[x] 수익률 출력 기능 +[x] 예외 처리 및 에러 문구 출력 기능 + +# 구현할 클래스 목록 +## WinningNumber - 당첨 번호 +- compareWithLottoList() 당첨 번호와 로또 번호 비교 + +## Lotto - 로또 정보 +- getNumbers() 로또 번호 조회 + +## FrontController - 프론트 컨트롤러 +- playLotto() 로또 플레이 + +## LottoController - 로또 컨트롤러 +- saveLotto() 로또 저장 +- getPublishNum() 로또 발행량 계산 +- publishLotto() 로또 티켓 발행 +- getWinningNumber() 당첨 번호 및 보너스 번호 저장후 조회 +- checkEarningRate() 수익률 조회 + +## LottoService - 로또 서비스 +- saveLotto() 랜덤한 숫자 생성을 통한 Lotto 생성 및 저장 +- savePublishNum() 로또 발행량 계산 및 저장 +- publishLotto() 로또 티켓 발행 +- saveWinningNumber() 당첨 번호 저장 +- checkWinningNumber() 당점 정보 비교 및 결과 계산 +- checkEarningRate() 수익률 조회 + +## LottoRepository - 로또 레포지토리 +- saveLotto() Lotto 객체 저장 +- saveWinningNumber() WinningNumber 객체 저장 +- findAllLotto() 로또 전체 조회 +- findWinningNumber() 당첨 번호 조회 +- savePublishNumber() 발행량 저장 +- findPublishNumber() 발행량 조회 +- saveRewardAmount() 당첨금 저장 +- findRewardAmount() 당첨금 조회 + +## InputManager - 입력 받기 +- buyAmountInput() 구매 금액 입력 +- winningNumberInput() 당첨 번호 입력 +- bonusNumberInput() 보너스 번호 입력 + +## InputValidator - 입력 검증 +- validateIsNumeric() 입력값 숫자인지 검증 +- validateMultiplyOfThousand() 1000의 배수인지 검증 +- validateIsListWithComma() 콤마로 구분된 숫자들의 나열인지 검증 + +## OutputManager - 출력하기 +- printStartMessage() 로또 시작 메시지 출력 +- printPublishNum() 로또 발행 개수 메시지 출력 +- printLottoList() 발행한 로또 티켓 출력 +- printCreateWinningNumber() 당첨 번호 입력 메시지 출력 +- printCreateBonusNumber() 보너스 번호 입력 메시지 출력 +- printResult() 결과 정보 출력 +- printEarningRate() 수익률 출력 +- printErrorMessage() 에러 메시지 출력 + +## ResultMap - 결과 종류 마다의 개수 저장 +- getResultMap() 결과 관련 map 조회 +- getRewardAmount() 최종 얻은 수익 계산 + +## RewardMoneyMap - 당첨금 관련 매핑 정보 저장 +- getRewardMoney() 개수에 따른 RewardMoney 조회 +- getMatchNum() RewardMoney에 따른 개수 조회 + +## OutputFormatter - 출력 String 포매팅 +- formatResultString() 결과 string 포맷 +- formatEarningRateString() 수익률 string 포맷 +- formatPublishNum() 발행 개수 string 포맷 + +## ComponentFactory - 컴포넌트들의 생성 + +# Enum 목록 +## LottoMessage - 로또 진행 메시지 + +## ErrorMessage - 에러 메시지 + +## LottoNumberRange - 로또 생성 범위 + +## RewardMoney - 당첨금 정보 diff --git a/src/main/java/lotto/Application.java b/src/main/java/lotto/Application.java index d190922ba4..2be7e5eb86 100644 --- a/src/main/java/lotto/Application.java +++ b/src/main/java/lotto/Application.java @@ -2,6 +2,8 @@ public class Application { public static void main(String[] args) { - // TODO: 프로그램 구현 + final ComponentFactory componentFactory = new ComponentFactory(); + final FrontController frontController = componentFactory.frontController(); + frontController.playLotto(); } } diff --git a/src/main/java/lotto/ComponentFactory.java b/src/main/java/lotto/ComponentFactory.java new file mode 100644 index 0000000000..e8ff937cfe --- /dev/null +++ b/src/main/java/lotto/ComponentFactory.java @@ -0,0 +1,36 @@ +package lotto; + +public class ComponentFactory { + + public FrontController frontController() { + return new FrontController(lottoController(), outputManager()); + } + + private LottoController lottoController() { + return new LottoController(lottoService(), outputManager(), inputManager()); + } + + private LottoService lottoService() { + return new LottoService(lottoRepository()); + } + + private LottoRepository lottoRepository() { + return new LottoRepository(); + } + + private OutputManager outputManager() { + return new OutputManager(outputFormatter()); + } + + private OutputFormatter outputFormatter() { + return new OutputFormatter(); + } + + private InputManager inputManager() { + return new InputManager(inputValidator()); + } + + private InputValidator inputValidator() { + return new InputValidator(); + } +} diff --git a/src/main/java/lotto/ErrorMessage.java b/src/main/java/lotto/ErrorMessage.java new file mode 100644 index 0000000000..790322d881 --- /dev/null +++ b/src/main/java/lotto/ErrorMessage.java @@ -0,0 +1,23 @@ +package lotto; + +public enum ErrorMessage { + ERROR_PREFIX("[ERROR] "), + NON_NUMERIC_ERROR("숫자값만 입력 가능합니다."), + NOT_MULTIPLY_ERROR("%d의 배수만 입력 가능합니다."), + NOT_LIST_WITH_COMMA_ERROR("콤마로 구분된 %d개의 숫자 형태로만 입력 가능합니다."), + NOT_VALID_ELEMENTS_NUM("리스트의 원소 개수는 %d개여야 합니다."), + NOT_UNIQUE_ELEMENTS("리스트에 중복 원소가 존재합니다."), + CANNOT_FIND_RESULT("결과를 찾을 수 없습니다."), + NOT_VALID_LOTTO_NUMBER_ERROR("로또 번호는 1부터 45 사이의 숫자여야 합니다."); + + private final String message; + + ErrorMessage(final String message) { + this.message = message; + } + + @Override + public String toString() { + return ERROR_PREFIX.message + this.message; + } +} diff --git a/src/main/java/lotto/FrontController.java b/src/main/java/lotto/FrontController.java new file mode 100644 index 0000000000..3bc14daa86 --- /dev/null +++ b/src/main/java/lotto/FrontController.java @@ -0,0 +1,23 @@ +package lotto; + +public class FrontController { + private final LottoController lottoController; + private final OutputManager outputManager; + + public FrontController(final LottoController lottoController, final OutputManager outputManager) { + this.lottoController = lottoController; + this.outputManager = outputManager; + } + + public void playLotto() { + try { + lottoController.getPublishNum(); + lottoController.publishLotto(); + lottoController.getWinningNumber(); + lottoController.checkWinningNumber(); + lottoController.checkEarningRate(); + } catch (final IllegalArgumentException e) { + outputManager.printErrorMessage(e.getMessage()); + } + } +} diff --git a/src/main/java/lotto/InputManager.java b/src/main/java/lotto/InputManager.java new file mode 100644 index 0000000000..fa3a58a607 --- /dev/null +++ b/src/main/java/lotto/InputManager.java @@ -0,0 +1,46 @@ +package lotto; + +import camp.nextstep.edu.missionutils.Console; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class InputManager { + private static final String COMMA_SPLITTER = ","; + private final InputValidator inputValidator; + + public InputManager(final InputValidator inputValidator) { + this.inputValidator = inputValidator; + } + + public Integer buyAmountInput() { + final String input = Console.readLine(); + inputValidator.validateIsNumeric(input); + + final Integer inputNumber = Integer.valueOf(input); + inputValidator.validateMultiplyOfThousand(inputNumber); + return inputNumber; + } + + public List winningNumberInput() { + final String input = Console.readLine(); + inputValidator.validateIsListWithComma(input); + return convertToList(input); + } + + private List convertToList(final String input) { + return Arrays.stream(input.split(COMMA_SPLITTER)) + .map(Integer::valueOf) + .peek(inputValidator::validateLottoNumber) + .collect(Collectors.toList()); + } + + public Integer bonusNumberInput() { + final String input = Console.readLine(); + inputValidator.validateIsNumeric(input); + final Integer bonusNumber = Integer.valueOf(input); + inputValidator.validateLottoNumber(bonusNumber); + return bonusNumber; + } +} diff --git a/src/main/java/lotto/InputValidator.java b/src/main/java/lotto/InputValidator.java new file mode 100644 index 0000000000..08f59e6ec7 --- /dev/null +++ b/src/main/java/lotto/InputValidator.java @@ -0,0 +1,34 @@ +package lotto; + +import java.util.regex.Pattern; + +public class InputValidator { + private static final String NUMERIC_MATCHER = "-?\\d+"; + private static final String LIST_WITH_COMMA_MATCHER = "\\d+,\\d+,\\d+,\\d+,\\d+,\\d+"; + private static final Integer NONE = 0; + private static final Integer MONEY_UNIT = 1000; + + public void validateIsNumeric(final String buyAmountInput) { + if (!buyAmountInput.matches(NUMERIC_MATCHER)) { + throw new IllegalArgumentException(ErrorMessage.NON_NUMERIC_ERROR.toString()); + } + } + + public void validateMultiplyOfThousand(final Integer buyAmountInput) { + if (buyAmountInput % MONEY_UNIT != NONE) { + throw new IllegalArgumentException(String.format(ErrorMessage.NOT_MULTIPLY_ERROR.toString(), MONEY_UNIT)); + } + } + + public void validateIsListWithComma(final String input) { + if (!Pattern.matches(LIST_WITH_COMMA_MATCHER, input)) { + throw new IllegalArgumentException(String.format(ErrorMessage.NOT_LIST_WITH_COMMA_ERROR.toString(), LottoNumberRange.NUMBER_NUM)); + } + } + + public void validateLottoNumber(final Integer lottoNumberInput) { + if (lottoNumberInput < LottoNumberRange.MIN_RANGE.toValue() || lottoNumberInput > LottoNumberRange.MAX_RANGE.toValue()) { + throw new IllegalArgumentException(String.format(ErrorMessage.NOT_VALID_LOTTO_NUMBER_ERROR.toString())); + } + } +} diff --git a/src/main/java/lotto/Lotto.java b/src/main/java/lotto/Lotto.java index 519793d1f7..3c3068aba4 100644 --- a/src/main/java/lotto/Lotto.java +++ b/src/main/java/lotto/Lotto.java @@ -1,20 +1,40 @@ package lotto; +import java.util.HashSet; import java.util.List; public class Lotto { private final List numbers; - public Lotto(List numbers) { + public Lotto(final List numbers) { validate(numbers); this.numbers = numbers; } - private void validate(List numbers) { - if (numbers.size() != 6) { - throw new IllegalArgumentException(); + private void validate(final List numbers) { + validateSize(numbers); + validateUnique(numbers); + } + + private void validateUnique(final List numbers) { + if (numbers.size() != new HashSet<>(numbers).size()) { + throw new IllegalArgumentException(ErrorMessage.NOT_UNIQUE_ELEMENTS.toString()); + } + } + + private void validateSize(final List numbers) { + if (numbers.size() != LottoNumberRange.NUMBER_NUM.toValue()) { + throw new IllegalArgumentException( + String.format(ErrorMessage.NOT_VALID_ELEMENTS_NUM.toString(), LottoNumberRange.NUMBER_NUM.toValue())); } } - // TODO: 추가 기능 구현 + public List getNumbers() { + return this.numbers; + } + + @Override + public String toString() { + return this.numbers.toString(); + } } diff --git a/src/main/java/lotto/LottoController.java b/src/main/java/lotto/LottoController.java new file mode 100644 index 0000000000..6dfe5c4934 --- /dev/null +++ b/src/main/java/lotto/LottoController.java @@ -0,0 +1,50 @@ +package lotto; + +import java.util.List; + +public class LottoController { + private final LottoService lottoService; + private final OutputManager outputManager; + private final InputManager inputManager; + + public LottoController(final LottoService lottoService, final OutputManager outputManager, final InputManager inputManager) { + this.lottoService = lottoService; + this.outputManager = outputManager; + this.inputManager = inputManager; + } + + public void getPublishNum() { + outputManager.printStartMessage(); + + final Integer buyAmount = inputManager.buyAmountInput(); + final Integer publishNum = lottoService.savePublishNumber(buyAmount); + + outputManager.printPublishNum(publishNum); + } + + public void publishLotto() { + final List lottoList = lottoService.publishLotto(); + outputManager.printLottoList(lottoList); + } + + public void getWinningNumber() { + outputManager.printCreateWinningNumber(); + final List winningNumberInput = inputManager.winningNumberInput(); + + outputManager.printCreateBonusNumber(); + final Integer bonusNumberInput = inputManager.bonusNumberInput(); + + lottoService.saveWinningNumber(winningNumberInput, bonusNumberInput); + } + + public void checkWinningNumber() { + outputManager.printWinningStatistics(); + final ResultMap resultMap = lottoService.checkWinningNumber(); + outputManager.printResult(resultMap); + } + + public void checkEarningRate() { + final Double earningRate = lottoService.checkEarningRate(); + outputManager.printEarningRate(earningRate); + } +} diff --git a/src/main/java/lotto/LottoMessage.java b/src/main/java/lotto/LottoMessage.java new file mode 100644 index 0000000000..a90a16d47d --- /dev/null +++ b/src/main/java/lotto/LottoMessage.java @@ -0,0 +1,23 @@ +package lotto; + +public enum LottoMessage { + BUY_TO_START("구입금액을 입력해 주세요."), + PUBLISH_NUM("%d개를 구매했습니다."), + CREATE_WINNING_NUMBER("당첨 번호를 입력해 주세요."), + CREATE_BONUS_NUMBER("보너스 번호를 입력해 주세요."), + WINNING_STATISTICS("당첨 통계\n---"), + NUM_OF_MATCH("%d개 일치 (%s원) - %d개"), + BONUS_MATCH("%d개 일치, 보너스 볼 일치 (%s원) - %d개"), + EARNING_RATE("총 수익률은 %.1f%%입니다."); + + private final String message; + + LottoMessage(final String message) { + this.message = message; + } + + @Override + public String toString() { + return this.message; + } +} diff --git a/src/main/java/lotto/LottoNumberRange.java b/src/main/java/lotto/LottoNumberRange.java new file mode 100644 index 0000000000..ad64002b36 --- /dev/null +++ b/src/main/java/lotto/LottoNumberRange.java @@ -0,0 +1,16 @@ +package lotto; + +public enum LottoNumberRange { + MIN_RANGE(1), + MAX_RANGE(45), + NUMBER_NUM(6); + private final int value; + + LottoNumberRange(int value) { + this.value = value; + } + + public int toValue() { + return value; + } +} diff --git a/src/main/java/lotto/LottoRepository.java b/src/main/java/lotto/LottoRepository.java new file mode 100644 index 0000000000..34c4cccd0f --- /dev/null +++ b/src/main/java/lotto/LottoRepository.java @@ -0,0 +1,42 @@ +package lotto; + +import java.util.List; + +public class LottoRepository { + private List lotto; + private WinningNumber winningNumber; + private Integer publishNumber; + private Long rewardAmount; + + public void savePublishNumber(final Integer publishNumber) { + this.publishNumber = publishNumber; + } + + public Integer findPublishNumber() { + return this.publishNumber; + } + + public void saveAllLotto(final List lotto) { + this.lotto = lotto; + } + + public void saveWinningNumber(final WinningNumber winningNumber) { + this.winningNumber = winningNumber; + } + + public List findAllLotto() { + return this.lotto; + } + + public WinningNumber findWinningNumber() { + return this.winningNumber; + } + + public void saveRewardAmount(final Long rewardAmount) { + this.rewardAmount = rewardAmount; + } + + public Long findRewardAmount() { + return this.rewardAmount; + } +} diff --git a/src/main/java/lotto/LottoService.java b/src/main/java/lotto/LottoService.java new file mode 100644 index 0000000000..76d7bed894 --- /dev/null +++ b/src/main/java/lotto/LottoService.java @@ -0,0 +1,79 @@ +package lotto; + +import camp.nextstep.edu.missionutils.Randoms; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public class LottoService { + private static final Integer ZERO = 0; + private static final Integer MONEY_UNIT = 1000; + private static final Double PERCENTAGE = 100.0; + private final LottoRepository lottoRepository; + + public LottoService(final LottoRepository lottoRepository) { + this.lottoRepository = lottoRepository; + } + + private List createRandomNumbers() { + return Randoms.pickUniqueNumbersInRange( + LottoNumberRange.MIN_RANGE.toValue(), + LottoNumberRange.MAX_RANGE.toValue(), + LottoNumberRange.NUMBER_NUM.toValue()); + } + + public Integer savePublishNumber(final Integer buyAmount) { + final Integer publishNumber = divideByMoneyUnit(buyAmount); + lottoRepository.savePublishNumber(publishNumber); + return publishNumber; + } + + private Integer divideByMoneyUnit(final Integer dividend) { + return dividend / MONEY_UNIT; + } + + public List publishLotto() { + final Integer publishNumber = lottoRepository.findPublishNumber(); + final List lottoList = createLottoList(publishNumber); + lottoRepository.saveAllLotto(lottoList); + return lottoList; + } + + private List createLottoList(final Integer publishNum) { + return IntStream.range(ZERO, publishNum) + .mapToObj(i -> createRandomNumbers()) + .map(Lotto::new) + .collect(Collectors.toList()); + } + + public void saveWinningNumber( + final List winningNumberInput, + final Integer bonusNumberInput) { + + final WinningNumber winningNumber = new WinningNumber(winningNumberInput, bonusNumberInput); + lottoRepository.saveWinningNumber(winningNumber); + } + + public ResultMap checkWinningNumber() { + final List lottoList = lottoRepository.findAllLotto(); + final WinningNumber winningNumber = lottoRepository.findWinningNumber(); + final ResultMap resultMap = winningNumber.compareWithLottoList(lottoList); + lottoRepository.saveRewardAmount(resultMap.getRewardAmount()); + return resultMap; + } + + public Double checkEarningRate() { + final long earnedAmount = lottoRepository.findRewardAmount(); + final int investmentAmount = getInvestment(); + return getEarningRate(earnedAmount, investmentAmount); + } + + private double getEarningRate(final long earnedAmount, final int investmentAmount) { + return ((double)earnedAmount / (double)investmentAmount) * PERCENTAGE; + } + + private int getInvestment() { + return lottoRepository.findPublishNumber() * MONEY_UNIT; + } +} diff --git a/src/main/java/lotto/OutputFormatter.java b/src/main/java/lotto/OutputFormatter.java new file mode 100644 index 0000000000..ee1998f4a7 --- /dev/null +++ b/src/main/java/lotto/OutputFormatter.java @@ -0,0 +1,32 @@ +package lotto; + +import java.text.NumberFormat; + +public class OutputFormatter { + private final NumberFormat numberFormat; + + public OutputFormatter() { + this.numberFormat = NumberFormat.getInstance(); + } + + public String formatResultString(final RewardMoney rewardMoney, final Integer numOfResult) { + RewardMoneyMap rewardMoneyMap = RewardMoneyMap.getInstance(); + + if (rewardMoney.equals(RewardMoney.MATCH_WITH_BONUS)) { + return String.format( + LottoMessage.BONUS_MATCH.toString(), + rewardMoneyMap.getMatchNum(rewardMoney), numberFormat.format(rewardMoney.toValue()), numOfResult); + } + return String.format( + LottoMessage.NUM_OF_MATCH.toString(), + rewardMoneyMap.getMatchNum(rewardMoney), numberFormat.format(rewardMoney.toValue()), numOfResult); + } + + public String formatEarningRateString(final Double earningRate) { + return String.format(LottoMessage.EARNING_RATE.toString(), earningRate); + } + + public String formatPublishNum(final Integer publishNum) { + return String.format(LottoMessage.PUBLISH_NUM.toString(), publishNum); + } +} diff --git a/src/main/java/lotto/OutputManager.java b/src/main/java/lotto/OutputManager.java new file mode 100644 index 0000000000..b2f2406f41 --- /dev/null +++ b/src/main/java/lotto/OutputManager.java @@ -0,0 +1,51 @@ +package lotto; + +import java.util.List; + +public class OutputManager { + private final OutputFormatter outputFormatter; + + public OutputManager(final OutputFormatter outputFormatter) { + this.outputFormatter = outputFormatter; + } + + public void printStartMessage() { + System.out.println(LottoMessage.BUY_TO_START); + } + + public void printPublishNum(final Integer publishNum) { + System.out.println(outputFormatter.formatPublishNum(publishNum)); + } + + public void printLottoList(final List lottoList) { + lottoList.forEach(System.out::println); + } + + public void printCreateWinningNumber() { + System.out.println(LottoMessage.CREATE_WINNING_NUMBER); + } + + public void printCreateBonusNumber() { + System.out.println(LottoMessage.CREATE_BONUS_NUMBER); + } + + public void printWinningStatistics() { + System.out.println(LottoMessage.WINNING_STATISTICS); + } + + public void printResult(final ResultMap resultMap) { + resultMap.getResultMap().forEach(((rewardMoney, numOfResult) ->{ + if (!rewardMoney.equals(RewardMoney.NONE)) { + System.out.println(outputFormatter.formatResultString(rewardMoney, numOfResult)); + } + })); + } + + public void printEarningRate(final Double earningRate) { + System.out.println(outputFormatter.formatEarningRateString(earningRate)); + } + + public void printErrorMessage(final String errorMessage) { + System.out.println(errorMessage); + } +} diff --git a/src/main/java/lotto/ResultMap.java b/src/main/java/lotto/ResultMap.java new file mode 100644 index 0000000000..c4d478904b --- /dev/null +++ b/src/main/java/lotto/ResultMap.java @@ -0,0 +1,38 @@ +package lotto; + +import java.util.*; + +public class ResultMap { + private static final Long NONE = 0L; + private static final Integer ONE = 1; + private final Map resultMap = new EnumMap<>(RewardMoney.class); + + public ResultMap(final List resultList) { + initializeResultMap(); + putData(resultList); + } + + private void initializeResultMap() { + Arrays.stream(RewardMoney.values()).forEach(rewardMoney -> { + if (!rewardMoney.equals(RewardMoney.MATCH_WITH_BONUS)) { + this.resultMap.put(rewardMoney, NONE.intValue()); + return; + } + this.resultMap.put(rewardMoney, NONE.intValue()); + }); + } + + private void putData(final List rewardMoneyList) { + rewardMoneyList.forEach(result -> this.resultMap.put(result, this.resultMap.get(result) + ONE)); + } + + public Map getResultMap() { + return this.resultMap; + } + + public Long getRewardAmount() { + return this.resultMap.keySet().stream() + .map(rewardMoney -> rewardMoney.toValue() * resultMap.get(rewardMoney)) + .reduce(NONE, Long::sum); + } +} diff --git a/src/main/java/lotto/RewardMoney.java b/src/main/java/lotto/RewardMoney.java new file mode 100644 index 0000000000..0d7500f05e --- /dev/null +++ b/src/main/java/lotto/RewardMoney.java @@ -0,0 +1,20 @@ +package lotto; + +public enum RewardMoney { + THREE_MATCH(5_000L), + FOUR_MATCH(50_000L), + FIVE_MATCH(1_500_000L), + MATCH_WITH_BONUS(30_000_000L), + SIX_MATCH(2_000_000_000L), + NONE(0L); + + private final Long money; + + RewardMoney(final Long money) { + this.money = money; + } + + public Long toValue() { + return this.money; + } +} diff --git a/src/main/java/lotto/RewardMoneyMap.java b/src/main/java/lotto/RewardMoneyMap.java new file mode 100644 index 0000000000..6e33cccc29 --- /dev/null +++ b/src/main/java/lotto/RewardMoneyMap.java @@ -0,0 +1,56 @@ +package lotto; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class RewardMoneyMap { + private static final Integer DEFAULT_INDEX = 0; + private static final Integer BONUS_INDEX = 1; + private final Map> moneyMap = new HashMap<>(); + private static RewardMoneyMap rewardMoneyMap; + + public static RewardMoneyMap getInstance() { + if (rewardMoneyMap == null) { + rewardMoneyMap = new RewardMoneyMap(); + } + return rewardMoneyMap; + } + + private RewardMoneyMap() { + initializeMoneyMap(); + } + + private void initializeMoneyMap() { + moneyMap.put(0, List.of(RewardMoney.NONE)); + moneyMap.put(1, List.of(RewardMoney.NONE)); + moneyMap.put(2, List.of(RewardMoney.NONE)); + moneyMap.put(3, List.of(RewardMoney.THREE_MATCH)); + moneyMap.put(4, List.of(RewardMoney.FOUR_MATCH)); + moneyMap.put(5, List.of(RewardMoney.FIVE_MATCH, RewardMoney.MATCH_WITH_BONUS)); + moneyMap.put(6, List.of(RewardMoney.SIX_MATCH)); + } + + public RewardMoney getRewardMoney(final int matchNum, final boolean isBonusMatch) { + final List rewardMoneyList = moneyMap.get(matchNum); + if (matchNum != 5) { + return rewardMoneyList.get(DEFAULT_INDEX); + } + return checkBonus(rewardMoneyList, isBonusMatch); + } + + public Integer getMatchNum(final RewardMoney rewardMoney) { + return moneyMap.entrySet().stream() + .filter(entry -> entry.getValue().contains(rewardMoney)) + .map(Map.Entry::getKey) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException(ErrorMessage.CANNOT_FIND_RESULT.toString())); + } + + private RewardMoney checkBonus(final List rewardMoneyList, final boolean isBonusMatch) { + if (isBonusMatch) { + return rewardMoneyList.get(BONUS_INDEX); + } + return rewardMoneyList.get(DEFAULT_INDEX); + } +} diff --git a/src/main/java/lotto/WinningNumber.java b/src/main/java/lotto/WinningNumber.java new file mode 100644 index 0000000000..a9c005d2cd --- /dev/null +++ b/src/main/java/lotto/WinningNumber.java @@ -0,0 +1,52 @@ +package lotto; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class WinningNumber { + private static final Integer ONE = 1; + private static final Integer BONUS_AVAILABLE = 5; + private final Lotto winningNumbers; + private final Integer bonusNumber; + + public WinningNumber(final List winningNumbers, final Integer bonusNumber) { + this.winningNumbers = new Lotto(winningNumbers); + this.bonusNumber = bonusNumber; + } + + public ResultMap compareWithLottoList(final List lottoList) { + final List numbers = this.winningNumbers.getNumbers(); + numbers.add(bonusNumber); + + final List resultList = lottoList.stream() + .map(Lotto::getNumbers) + .map(ArrayList::new) + .peek(lotto -> lotto.retainAll(numbers)) + .map(this::getResult) + .collect(Collectors.toList()); + + return new ResultMap(resultList); + } + + private RewardMoney getResult(final List lotto) { + final boolean isBonusMatch = lotto.contains(bonusNumber); + final int matchNum = getMatchNum(isBonusMatch, lotto.size()); + return getResultWithMatchNum(matchNum, isBonusMatch); + } + + private RewardMoney getResultWithMatchNum(final int matchNum, final boolean isBonusMatch) { + final RewardMoneyMap rewardMoneyMap = RewardMoneyMap.getInstance(); + if (matchNum != BONUS_AVAILABLE) { + return rewardMoneyMap.getRewardMoney(matchNum, false); + } + return rewardMoneyMap.getRewardMoney(matchNum, isBonusMatch); + } + + private int getMatchNum(final boolean isBonusMatch, final Integer size) { + if (isBonusMatch) { + return size - ONE; + } + return size; + } +} diff --git a/src/test/java/lotto/InputValidatorTest.java b/src/test/java/lotto/InputValidatorTest.java new file mode 100644 index 0000000000..b6ab0e2091 --- /dev/null +++ b/src/test/java/lotto/InputValidatorTest.java @@ -0,0 +1,70 @@ +package lotto; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@DisplayName("InputValidator의 검증 기능 중") +class InputValidatorTest { + private InputValidator inputValidator = new InputValidator(); + + @Nested + @DisplayName("숫자 검증에서") + class ValidateIsNumeric { + @Test + @DisplayName("숫자를 입력했을때가 검증되는가") + void validateNumber() { + inputValidator.validateIsNumeric("123"); + } + + @Test + @DisplayName("일반 문자열을 입력했을때가 검증되는가") + void validateNonNumber() { + assertThatThrownBy(() -> inputValidator.validateIsNumeric("abc")) + .isInstanceOf(IllegalArgumentException.class); + } + } + + @Nested + @DisplayName("1000의 배수 검증에서") + class ValidateMultiplyOfThousand { + @Test + @DisplayName("1000의 배수를 입력했을떄가 검증되는가") + void validateMultiplyOfThousand() { + inputValidator.validateMultiplyOfThousand(1000); + } + + @Test + @DisplayName("1000의 배수가 아닌 수를 입력했을떄가 검증되는가") + void validateAnyNumber() { + assertThatThrownBy(() -> inputValidator.validateMultiplyOfThousand(1001)) + .isInstanceOf(IllegalArgumentException.class); + } + } + + @Nested + @DisplayName("당첨 번호 입력값 검증에서") + class ValidateIsListWithComma { + @Test + @DisplayName("정상 입력이 검증되는가") + void validateRightInput() { + inputValidator.validateIsListWithComma("1,2,3,4,5,6"); + } + + @Test + @DisplayName("비정상 입력이 검증되는가") + void validateWrongInputWithMissingNumber() { + assertThatThrownBy(() -> inputValidator.validateIsListWithComma("1,2,3,4,5,")) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("비정상 입력이 검증되는가") + void validateWrongInput() { + assertThatThrownBy(() -> inputValidator.validateIsListWithComma("1,2,3,4,5 ,6")) + .isInstanceOf(IllegalArgumentException.class); + } + } +} diff --git a/src/test/java/lotto/LottoServiceTest.java b/src/test/java/lotto/LottoServiceTest.java new file mode 100644 index 0000000000..21c2745206 --- /dev/null +++ b/src/test/java/lotto/LottoServiceTest.java @@ -0,0 +1,39 @@ +package lotto; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.*; + +@DisplayName("LottoService의 서비스 로직 중") +class LottoServiceTest { + private LottoRepository lottoRepository = new LottoRepository(); + private LottoService lottoService = new LottoService(lottoRepository); + + @Test + @DisplayName("발행 번호 조회가 수행되는가") + void getPublishNum() { + //given + + //when + final Integer publishNum = lottoService.savePublishNumber(1000); + + //then + assertThat(publishNum).isEqualTo(1); + } + + @Test + @DisplayName("로또 티켓 발행이 수행되는가") + void publishLotto() { + //given + lottoService.savePublishNumber(1000); + + //when + final List lottoList = lottoService.publishLotto(); + + //then + assertThat(lottoList).hasSize(1); + } +}