Skip to content

[이예진] 로또 1,2단계 구현 #126

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 5 commits into
base: yaevrai
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
70 changes: 70 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# java-lotto : 로또 - 클린코드

## Level1. 로또 자동 구매

### - 요구사항

- [x] 로또 구입 금액을 입력하면 구입 금액에 해당하는 로또를 발급해야 한다.
- [x] 로또 1장의 가격은 1000원이다.

### - 새로운 프로그래밍 요구사항

- [x] 배열 대신 컬렉션을 사용한다.
- [x] 줄여 쓰지 않는다(축약 금지).
- [x] 함수(또는 메서드)의 길이가 10라인을 넘어가지 않도록 구현한다.
- [x] 함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라.

## Level2. 로또 당첨

### - 요구사항

- [x] 로또 당첨 번호를 받아 일치한 번호 수에 따라 당첨 결과를 보여준다.

### - 새로운 프로그래밍 요구사항

- [x] 모든 원시 값과 문자열을 포장한다.
- [x] 일급 컬렉션을 쓴다.

## Level3. 로또 2등 당첨

### - 요구사항

- [ ] 2등을 위한 보너스볼을 추첨한다.
- [ ] 당첨 통계에 2등을 추가한다.
- [ ] 2등 당첨 조건은 당첨 번호 5개 일치 + 보너스 볼 일치다.

### - 새로운 프로그래밍 요구사항

- [ ] Java Enum을 적용한다.

## Level4. 로또 수동 구매

### - 요구사항

- [ ] 현재 로또 생성기는 자동 생성 기능만 제공한다. 사용자가 수동으로 추첨 번호를 입력할 수 있도록 해야 한다.
- [ ] 입력한 금액, 자동 생성 숫자, 수동 생성 번호를 입력하도록 해야 한다.

## Level5. 리팩토링

### - 새로운 프로그래밍 요구사항

- [ ] 기존 프로그래밍 요구사항을 다시 한번 확인하고, 학습 테스트를 통해 학습한 내용을 반영한다.

### - 기존 프로그래밍 요구사항

- [ ] 자바 코드 컨벤션을 지키면서 프로그래밍한다.
- [ ] 기본적으로 Java Style Guide을 원칙으로 한다.
- [ ] indent(인덴트, 들여쓰기) depth를 2를 넘지 않도록 구현한다. 1까지만 허용한다.
- [ ] 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다. 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메서드)를 분리하면
된다.
- [ ] 3항 연산자를 쓰지 않는다.
- [ ] else 예약어를 쓰지 않는다.
- [ ] else 예약어를 쓰지 말라고 하니 switch/case로 구현하는 경우가 있는데 switch/case도 허용하지 않는다. 힌트: if문에서 값을 반환하는 방식으로
구현하면 else 예약어를 사용하지 않아도 된다.
- [ ] 배열 대신 컬렉션을 사용한다.
- [ ] 줄여 쓰지 않는다(축약 금지).
- [ ] 함수(또는 메서드)의 길이가 10라인을 넘어가지 않도록 구현한다.
- [ ] 함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라.
- [ ] 모든 원시 값과 문자열을 포장한다.
- [ ] 일급 컬렉션을 쓴다.
- [ ] Java Enum을 적용한다.
12 changes: 12 additions & 0 deletions src/main/java/lotto/LottoController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package lotto;

import lotto.view.LottoInputView;
import lotto.view.LottoOutputView;

public class LottoController {

public static void main(String[] args) {
LottoService service = new LottoService(new LottoInputView(), new LottoOutputView());
Copy link
Author

Choose a reason for hiding this comment

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

  1. 지난 미션에선 Main이란 이름의 클래스에서 Controller를 조작하도록 구현했었는데,
    Controller가 하는 일과 네이밍이 잘 안맞는 듯해서 이번엔 Controller내 메인 클래스를 두고 프로그램 조작만을 담당하도록 했습니다.!

  2. 지난 미션에선 하나의 View 객체를 만들어서 입/출력 관련 역할을 모았었는데요,
    스캐너를 사용하는 건 같지만 출력/입력은 다른 역할이라는 생각이 들어 입/출력 View 클래스를 나누어 보았습니다.

Choose a reason for hiding this comment

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

예진님이 생각하시는 Controller의 역할은 무엇인가요? 그렇다면 Service의 역할은 무엇인가요?

Copy link
Author

Choose a reason for hiding this comment

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

Controller : 외부 요청을 받고, 그 요청을 처리할 적절한 로직으로 연결해주는 역할
Service : 비즈니스 로직을 담당하는 역할

이번 미션에선 요청이라고는 할 수 없지만, 실행 및 흐름을 조작을 하는 역할을 하고, 나머지 기능은 서비스단에서 구현하도록 했습니ㄷ!

service.start();
}
}
48 changes: 48 additions & 0 deletions src/main/java/lotto/LottoService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package lotto;

import java.util.List;
import java.util.stream.Collectors;
import lotto.model.LottoGenerator;
import lotto.model.Lottos;
import lotto.model.MatchCount;
import lotto.model.Money;
import lotto.model.PurchaseLotto;
import lotto.model.WinningNumbers;
import lotto.model.WinningResult;
import lotto.view.LottoInputView;
import lotto.view.LottoOutputView;

public class LottoService {

private final LottoInputView inputView;
private final LottoOutputView outputView;
private final LottoGenerator lottoGenerator;

public LottoService(LottoInputView inputView, LottoOutputView outputView) {
this.inputView = inputView;
this.outputView = outputView;
this.lottoGenerator = new LottoGenerator();
}

public void start() {
Money money = new Money(inputView.inputMoney());
PurchaseLotto purchaseLotto = new PurchaseLotto(money, lottoGenerator);
Lottos lottos = purchaseLotto.getLottos();

outputView.printPurchasedLottoCount(purchaseLotto.purchaseCount());
outputView.printLottos(lottos.asList());

WinningNumbers winningNumbers = new WinningNumbers(inputView.inputWinningNumber());
WinningResult winningResult = calculateWinningResult(lottos, winningNumbers);

outputView.printWinningStatistics(money.getAmount(), winningResult.getWinningStatistics());
}

private WinningResult calculateWinningResult(Lottos lottos, WinningNumbers winningNumbers) {
List<MatchCount> matchCounts = lottos.asList().stream()
.map(lotto -> lotto.match(winningNumbers))
.collect(Collectors.toList());

return new WinningResult(matchCounts);
}
}
35 changes: 35 additions & 0 deletions src/main/java/lotto/model/LottoGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package lotto.model;

import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class LottoGenerator {

private static final int MAX_NUMBER = 45;
private static final int LOTTO_SIZE = 6;

public LottoNumbers generate() {
List<Integer> shuffledNumbers = createShuffledNumbers();
List<Integer> pickSixNumber = pickRandomSixSorted(shuffledNumbers);
return new LottoNumbers(pickSixNumber);
}

private List<Integer> createShuffledNumbers() {
List<Integer> numbers = IntStream.rangeClosed(1, MAX_NUMBER)
.boxed()
.collect(Collectors.toList());

Collections.shuffle(numbers);
return numbers;
}

private List<Integer> pickRandomSixSorted(List<Integer> numbers) {
return numbers.stream()
.limit(LOTTO_SIZE)
.sorted()
.collect(Collectors.toList());
}
}

31 changes: 31 additions & 0 deletions src/main/java/lotto/model/LottoNumbers.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package lotto.model;

import java.util.List;

public class LottoNumbers {

private static final int LOTTO_SIZE = 6;
private final List<Integer> numbers;

public LottoNumbers(List<Integer> numbers) {
validate(numbers);
this.numbers = List.copyOf(numbers);
}

private void validate(List<Integer> numbers) {
if (numbers.size() != LOTTO_SIZE) {
throw new IllegalArgumentException("로또 번호는 6개여야 합니다.");
}
}

public MatchCount match(WinningNumbers winningNumbers) {
long count = numbers.stream()
.filter(winningNumbers.getNumbers()::contains)
.count();
return new MatchCount((int) count);
}

public List<Integer> getNumbers() {
return numbers;
}
}
21 changes: 21 additions & 0 deletions src/main/java/lotto/model/Lottos.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package lotto.model;

import java.util.List;

public class Lottos {

private final List<LottoNumbers> lottoNumbers;

public Lottos(List<LottoNumbers> lottoNumbers) {
this.lottoNumbers = List.copyOf(lottoNumbers);
}

public List<LottoNumbers> asList() {
return lottoNumbers;
}

public int size() {
return lottoNumbers.size();
}

}
14 changes: 14 additions & 0 deletions src/main/java/lotto/model/MatchCount.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package lotto.model;

public class MatchCount {

private final int count;

public MatchCount(int count) {
this.count = count;
}

public int getCount() {
return count;
}
}
43 changes: 43 additions & 0 deletions src/main/java/lotto/model/Money.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package lotto.model;

import java.util.Objects;

public class Money {

Choose a reason for hiding this comment

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

int money = 1000 대신 Money money = new Money(1000)과 같이 사용하면, 명확하게 돈이라는 의미를 가지게 할 수 있겠군요!

또한 내부 필드가 final으로 지정되어, 여러 스레드에서 공유해도 안전할 것 같네요!

이러한 객체 구현을 "값 객체(Value Object)"라고 불러요.

값 객체는 상태를 변경 메서드를 제공하지 않으며, 그 자체로 식별성을 가져요.

하지만 지금 구현에서도 그럴까요?

Money first = new Money(100);
Money second = new Money(100);

assertThat(first).isEqualTo(second);

Copy link
Author

Choose a reason for hiding this comment

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

감사합니다 수정했습니다 !!


private final int amount;

public Money(int amount) {
validate(amount);
this.amount = amount;
}

public int getAmount() {
return amount;
}

private void validate(int amount) {
if (amount < 0) {
throw new IllegalArgumentException("금액은 음수일 수 없습니다.");
}
if (amount % 1000 != 0) {
throw new IllegalArgumentException("금액은 1000원 단위여야 합니다.");
}
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Money money = (Money) o;
return amount == money.amount;
}

@Override
public int hashCode() {
return Objects.hash(this.amount);
}
}
33 changes: 33 additions & 0 deletions src/main/java/lotto/model/PurchaseLotto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package lotto.model;

import java.util.List;
import java.util.Objects;

public class PurchaseLotto {

public static final int PRICE = 1000;
private final Money money;
private final Lottos lottos;

public PurchaseLotto(Money money, LottoGenerator lottoGenerator) {
Objects.requireNonNull(money);
Objects.requireNonNull(lottoGenerator);

this.money = money;
this.lottos = new Lottos(generateLottos(lottoGenerator, purchaseCount()));
}

public int purchaseCount() {
return money.getAmount() / PRICE;
}

public Lottos getLottos() {
return lottos;
}

private List<LottoNumbers> generateLottos(LottoGenerator generator, int count) {
return java.util.stream.IntStream.range(0, count)
.mapToObj(i -> generator.generate())
.collect(java.util.stream.Collectors.toList());
}
}
30 changes: 30 additions & 0 deletions src/main/java/lotto/model/Rank.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package lotto.model;

public enum Rank {
MATCH_3(3, 5_000, "3개 일치 (5000원)"),
MATCH_4(4, 50_000, "4개 일치 (50000원)"),
MATCH_5(5, 1_500_000, "5개 일치 (1500000원)"),
MATCH_6(6, 2_000_000_000, "6개 일치 (2000000000원)");

private final int matchCount;
private final int prize;
private final String display;

Rank(int matchCount, int prize, String display) {
this.matchCount = matchCount;
this.prize = prize;
this.display = display;
}

public int getMatchCount() {
return matchCount;
}

public int getPrize() {
return prize;
}

public String getDisplay() {
return display;
}
}
16 changes: 16 additions & 0 deletions src/main/java/lotto/model/WinningNumbers.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package lotto.model;

import java.util.List;

public class WinningNumbers {

private final LottoNumbers numbers;

public WinningNumbers(List<Integer> numbers) {
this.numbers = new LottoNumbers(numbers);
}

public List<Integer> getNumbers() {
return numbers.getNumbers();
}
}
Loading