Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
44aff33
refactor: Money 로직 수정, Lotto 생성자 위치 수정
qwer920414-ctrl Dec 2, 2025
8ea7835
feat: 로또 2등 로직 추가 Rank -> LottoResult -> LottoGame, Lottos 순으로
qwer920414-ctrl Dec 2, 2025
03abc72
refactor: LottoNumber 포장객체 생성
qwer920414-ctrl Dec 3, 2025
e03eafc
Lotto 클래스 수정 -> LottoNumber 수정
qwer920414-ctrl Dec 3, 2025
e913f9d
refactor: WinningLotto 클래스 LottoNumber 포장
qwer920414-ctrl Dec 3, 2025
234d0e7
fix: LottoNumber toString() 추가
qwer920414-ctrl Dec 3, 2025
c6846fc
refactor: LottoGame - check() 메소드 이름 변경 - findWinner(): 직관적으로 당첨 로또를 …
qwer920414-ctrl Dec 3, 2025
d2b7dd3
refactor: WinningLotto match() 메소드 개선 -> Rank 구하기
qwer920414-ctrl Dec 3, 2025
da1c943
docs: 기능 구현 목록 수정
qwer920414-ctrl Dec 3, 2025
1485e32
refactor: ResultView.printLottos() 개선 - LottoGame을 파라미터로
qwer920414-ctrl Dec 3, 2025
04820e9
style: 불필요 메소드 삭제
qwer920414-ctrl Dec 3, 2025
8f4643c
refactor: Lotto 클래스 match 피드백 수정
qwer920414-ctrl Dec 4, 2025
84a3030
refactor: LottoGame - lottoCount() 메소드 수정
qwer920414-ctrl Dec 4, 2025
121b387
refactor: 수익률 계산로직 수정(LottoResult에서 계산, LottoGame testCode 삭제)
qwer920414-ctrl Dec 4, 2025
0076168
refactor: LottoNumber 캐싱 로직 적용
qwer920414-ctrl Dec 4, 2025
6981cba
refactor: ResultView - printResult 로직 수정
qwer920414-ctrl Dec 4, 2025
19e4582
test: Lottos - findResult 테스트 추가, LottoGame의 중복 테스트 제거
qwer920414-ctrl Dec 4, 2025
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
15 changes: 11 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,15 @@

## 기능 구현 목록
* domain
* LottoGenerator
* LottoChecker
* LottoFactory - 로또 생성 담당
* LottoGame - 당첨 복권 & 수익률 계산
* Lottos
* Lotto - 로또 복권 하나
* LottoNumber - 기본 로또 번호
* WinningLotto - Lotto + 보너스번호
* Money - 금액에 관련된 책임 수행
* LottoResult - 게임의 결과
* Rank - 당첨 순위 & 당첨금에 대한 역할
* view
* InputView
* ResultView
* InputView - 입력 담당
* ResultView - 출력 담당
12 changes: 6 additions & 6 deletions src/main/java/lotto/Main.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
package lotto;

import lotto.domain.Lotto;
import lotto.domain.LottoGame;
import lotto.domain.LottoResult;
import lotto.domain.Money;
import lotto.domain.WinningLotto;
import lotto.view.InputView;
import lotto.view.ResultView;

public class Main {
public static void main(String[] args) {
long price = InputView.initLottoPrice();
LottoGame lottoGame = new LottoGame(price);
ResultView.printLottos(lottoGame.lottos());

Lotto winningLotto = new Lotto(InputView.initWinningLotto());
LottoResult result = lottoGame.check(winningLotto);
ResultView.printResult(result, lottoGame);
ResultView.printLottos(lottoGame);

WinningLotto winningLotto = new WinningLotto(InputView.initWinningLotto(), InputView.initBonusNumber());
LottoResult result = lottoGame.findWinner(winningLotto);
ResultView.printResult(result, new Money(price));
}
}
57 changes: 31 additions & 26 deletions src/main/java/lotto/domain/Lotto.java
Original file line number Diff line number Diff line change
@@ -1,28 +1,48 @@
package lotto.domain;

import java.util.*;
import java.util.stream.Collectors;

public class Lotto {
public static final int LOTTO_NUMBER_SIZE = 6;

private final Set<Integer> numbers;
private final Set<LottoNumber> numbers;

public Lotto() {
this(new TreeSet<>(LottoFactory.generateLotto()));
this(LottoFactory.generateLotto());
}

public Lotto(String value) {
this(splitAndParseInt(value));
}

private static Set<Integer> splitAndParseInt(String value) {
public Lotto(Integer... numbers) {
this(Arrays.asList(numbers));
}

public Lotto(List<Integer> numbers) {
this(convert(numbers));
Copy link
Contributor

Choose a reason for hiding this comment

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

기존 생성자를 유지함으로써 외부 코드엥 영향을 미치지 않도록 리팩터링한 점 👍

}

public Lotto(Set<LottoNumber> numbers) {
validate(numbers);
this.numbers = Collections.unmodifiableSet(numbers);
}

private static Set<LottoNumber> convert(List<Integer> numbers) {
return numbers.stream()
.map(LottoNumber::from)
.collect(Collectors.toSet());
}

private static List<Integer> splitAndParseInt(String value) {
String[] split = getSplit(value);
Set<Integer> numbers = strToIntSet(split);
List<Integer> numbers = strToIntSet(split);
return numbers;
}

private static Set<Integer> strToIntSet(String[] split) {
Set<Integer> numbers = new TreeSet<>();
private static List<Integer> strToIntSet(String[] split) {
List<Integer> numbers = new ArrayList<>();
for (String s : split) {
numbers.add(Integer.parseInt(s));
}
Expand All @@ -33,39 +53,24 @@ private static String[] getSplit(String value) {
return value.split(",");
}

public Lotto(Integer... numbers) {
this(new TreeSet<>(Arrays.asList(numbers)));
}

public Lotto(Set<Integer> numbers) {
validate(numbers);
this.numbers = Collections.unmodifiableSet(numbers);
}

private void validate(Set<Integer> numbers) {
private void validate(Set<LottoNumber> numbers) {
if (numbers.size() != LOTTO_NUMBER_SIZE) {
throw new IllegalArgumentException();
}

boolean invalidNumber = numbers.stream()
.anyMatch(n -> n < 1 || n > 45);
if (invalidNumber) {
throw new IllegalArgumentException();
}
}

public int match(Lotto winningLotto) {
int count = 0;
for (Integer number : numbers) {
if (winningLotto.numbers().contains(number)) {
for (LottoNumber number : numbers) {
if (winningLotto.contains(number)) {
count++;
}
}
return count;
}

private Set<Integer> numbers() {
return numbers;
public boolean contains(LottoNumber number) {
return numbers.contains(number);
}

@Override
Expand Down
17 changes: 10 additions & 7 deletions src/main/java/lotto/domain/LottoGame.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package lotto.domain;

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

public class LottoGame {
private final Lottos lottos;
Expand All @@ -19,15 +19,18 @@ public LottoGame(Lottos lottos, Money money) {
this.money = money;
}

public List<Lotto> lottos() {
return lottos.values();
public LottoResult findWinner(WinningLotto winningLotto) {
return lottos.findResult(winningLotto);
}

public LottoResult check(Lotto winningLotto) {
return lottos.findResult(winningLotto);
public int lottoCount() {
return lottos.size();
}

public double rateOfReturn(LottoResult result) {
return money.rateOfReturn(result.getTotal());
@Override
public String toString() {
return lottos.values().stream()
.map(Lotto::toString)
.collect(Collectors.joining("\n"));
}
}
53 changes: 53 additions & 0 deletions src/main/java/lotto/domain/LottoNumber.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package lotto.domain;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.stream.IntStream;

public class LottoNumber {
Copy link
Contributor

Choose a reason for hiding this comment

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

원시값 포장 👍
단, 불변 객체로 구현할 경우 발생하는 상황은?

상황

  • 웹 백엔드 서버로 1만명의 사용자가 동시에 당첨 여부를 확인할 수 있어야 한다.
  • 1명의 사용자는 평균 5장의 로또를 구매한 상태이다.

위 요구사항을 서버에서 생성되는 LottoNumber의 인스턴스의 갯수는?
1 * 5장 * 6개 숫자/1장 * 1만명 = 30만개이다.

동시에 생성되는 인스턴스 갯수가 너무 많다.
인스턴스 갯수를 줄일 수 있는 방법은?
로또 숫자 값을 LottoNumber 객체로 래핑하는 경우 매번 인스턴스가 생성되기 때문에 인스턴스의 갯수가 너무 많아져 성능이 떨어질 수 있다.
LottoNumber 인스턴스를 생성한 후 재사용할 수 있도록 구현한다.

힌트 : Map과 같은 곳에 인스턴스를 생성한 후 재사용하는 방법을 찾아본다.

public static final int MIN_NUMBER = 1;
public static final int MAX_NUMBER = 45;

private static final Map<Integer, LottoNumber> CACHE = new HashMap<>();

static {
IntStream.rangeClosed(MIN_NUMBER, MAX_NUMBER)
.forEach(i -> CACHE.put(i, new LottoNumber(i)));
}
Comment on lines +12 to +17
Copy link
Contributor

Choose a reason for hiding this comment

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

캐싱 적용 👍


private final int lottoNumber;

private LottoNumber(int lottoNumber) {
Copy link
Contributor

Choose a reason for hiding this comment

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

private 으로 구현 👍

this.lottoNumber = lottoNumber;
}

public static LottoNumber from(int lottoNumber) {
validate(lottoNumber);
return CACHE.get(lottoNumber);
}

private static void validate(int lottoNumber) {
if (lottoNumber < MIN_NUMBER || lottoNumber > MAX_NUMBER) {
throw new IllegalArgumentException();
}
}

@Override
public boolean equals(Object object) {
if (this == object) return true;
if (object == null || getClass() != object.getClass()) return false;
LottoNumber that = (LottoNumber) object;
return lottoNumber == that.lottoNumber;
}

@Override
public int hashCode() {
return Objects.hashCode(lottoNumber);
}

@Override
public String toString() {
return String.valueOf(lottoNumber);
}
}
7 changes: 5 additions & 2 deletions src/main/java/lotto/domain/LottoResult.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ public LottoResult() {
}
}

public void addMatch(int match) {
Rank rank = Rank.from(match);
public void addMatch(Rank rank) {
lottoResult.put(rank, lottoResult.get(rank) + 1);
}

Expand All @@ -26,4 +25,8 @@ public long getTotal() {
.mapToLong(entry -> entry.getKey().prize() * entry.getValue())
.sum();
}

public double profitRate(Money money) {
return getTotal() / money.getMoney();
}
}
9 changes: 6 additions & 3 deletions src/main/java/lotto/domain/Lottos.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@ public List<Lotto> values() {
return lottos;
}

public LottoResult findResult(Lotto winningLotto) {
public LottoResult findResult(WinningLotto winningLotto) {
LottoResult result = new LottoResult();
for (Lotto lotto : lottos) {
int match = lotto.match(winningLotto);
result.addMatch(match);
result.addMatch(winningLotto.match(lotto));
}
return result;
}

public int size() {
return lottos.size();
}
}
5 changes: 0 additions & 5 deletions src/main/java/lotto/domain/Matchable.java

This file was deleted.

4 changes: 2 additions & 2 deletions src/main/java/lotto/domain/Money.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public int buyCount() {
return (int) (money / LOTTO_PRICE);
}

public double rateOfReturn(long getTotal) {
return Math.round((double) (getTotal / money) * 100) / 100.0;
public double getMoney() {
return money;
}
}
11 changes: 10 additions & 1 deletion src/main/java/lotto/domain/Rank.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

public enum Rank {
FIRST(6, 2_000_000_000),
SECOND(5, 30_000_000),
THIRD(5, 1_500_000),
FOURTH(4, 50_000),
FIFTH(3, 5_000),
Expand All @@ -29,10 +30,18 @@ public boolean isMatch(int match) {
return this.match == match;
}

public static Rank from(int matchCount) {
public static Rank from(int matchCount, boolean matchBonus) {
return Arrays.stream(values())
.filter(rank -> rank.isMatch(matchCount))
.findFirst()
.map(rank -> bonusCheck(rank, matchBonus))
.orElse(NONE);
}

private static Rank bonusCheck(Rank rank, boolean matchBonus) {
if (rank == Rank.THIRD && matchBonus) {
return Rank.SECOND;
}
return rank;
}
}
30 changes: 30 additions & 0 deletions src/main/java/lotto/domain/WinningLotto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package lotto.domain;

public class WinningLotto {
Copy link
Contributor

Choose a reason for hiding this comment

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

새로운 객체로 분리 👍

private final Lotto winningLotto;
private final LottoNumber bonusNumber;

public WinningLotto(String lotto, int bonusNumber) {
this(new Lotto(lotto), bonusNumber);
}

public WinningLotto(Lotto lotto, int bonusNumber) {
Copy link
Contributor

Choose a reason for hiding this comment

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

주 생성자는 생성자의 마지막에 구현하는 것이 관례

this(lotto, LottoNumber.from(bonusNumber));
}

public WinningLotto(Lotto lotto, LottoNumber bonusNumber) {
validateBonusNumber(lotto, bonusNumber);
this.winningLotto = lotto;
this.bonusNumber = bonusNumber;
}

private void validateBonusNumber(Lotto lotto, LottoNumber bonusNumber) {
if (lotto.contains(bonusNumber)) {
throw new IllegalArgumentException();
}
}

public Rank match(Lotto lotto) {
return Rank.from(winningLotto.match(lotto), lotto.contains(bonusNumber));
}
}
5 changes: 5 additions & 0 deletions src/main/java/lotto/view/InputView.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,9 @@ public static String initWinningLotto() {
System.out.println("지난 주 당첨 번호를 입력해 주세요.");
return SCANNER.nextLine();
}

public static int initBonusNumber() {
System.out.println("보너스 볼을 입력해 주세요.");
return SCANNER.nextInt();
}
}
Loading