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

[Step4] 로또(2등) #1676

Merged
merged 6 commits into from
May 26, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 27 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,33 @@ LotteryTicket - 로또


# [4단계] 로또(2등)

// TODO
```text
[... 생략 ...]

지난 주 당첨 번호를 입력해 주세요.
1, 2, 3, 4, 5, 6
보너스 볼을 입력해 주세요.
7

당첨 통계
---------
3개 일치 (5000원)- 1개
4개 일치 (50000원)- 0개
5개 일치 (1500000원)- 0개
5개 일치, 보너스 볼 일치(30000000원) - 0개
6개 일치 (2000000000원)- 0개
총 수익률은 0.35입니다.(기준이 1이기 때문에 결과적으로 손해라는 의미임)
```

- [x] 보너스볼 변수 생성
- [x] 보너스볼 체크 로직 추가
- [x] 보너스볼 당첨시 전체 금액 확인
- [x] 보너스볼을 물어보는 메세지 출력
- [x] 보너스볼이 포함된 메세지 출력

--- 그외 고려해볼만한 리팩토링
- [x] matchCount 의 integer을 통해 matchType 개선
- [x] infoCenter 테스트 코드 리팩토링

# [5단계] 로또(수동)

Expand Down
18 changes: 12 additions & 6 deletions src/main/java/lottery/InfoCenter.java
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
package lottery;

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

import java.util.stream.Collectors;

public class InfoCenter {

private final int MATCH_THREE_NUMBER = 3;
private Ticket lastWeekWinningTicket;
private int bonus;

Choose a reason for hiding this comment

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

Ticket을 상속 혹은 위임해서 winningTicket에 보너스 번호를 추가하고 로또의 제약이나 기능을 그대로 사용할 수 있지 않을까요?

Copy link
Author

Choose a reason for hiding this comment

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

생각해보니, 그렇게 할수 있겠네요. 한번 시도해보겠습니다!


public void setLastWeekWinningTicket(Ticket lastWeekWinningTicket) {
this.lastWeekWinningTicket = lastWeekWinningTicket;
}

void setBonusNumber(int number) {
this.bonus = number;
}

public Result confirmTicket(Tickets buyerTickets) {
requireNonNull(lastWeekWinningTicket, "지난 주 티켓이 없습니다.");
requireNonNull(bonus, "보너스 볼이 없습니다.");

return new Result(LotteryMatchTypeMap.of(buyerTickets.getValues().stream()
.map(ticket -> ticket.numbers().matchCountWith(lastWeekWinningTicket.numbers()))
.filter(matchCount -> matchCount >= MATCH_THREE_NUMBER)
.collect(Collectors.groupingBy(LotteryMatchType::fromInteger, summingInt(a -> 1)))));
.map(ticket -> ticket.numbers().getMatchTypeWith(lastWeekWinningTicket.numbers(), bonus))

Choose a reason for hiding this comment

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

규칙 4: 한 줄에 점을 하나만 찍는다.

위 객체지향 생활 체조 원칙 관점에서 봤을 때 아래와 같이 호출되는건 어떨까요?

Suggested change
.map(ticket -> ticket.numbers().getMatchTypeWith(lastWeekWinningTicket.numbers(), bonus))
.map(ticket -> ticket.getMatchTypeWith(lastWeekWinningTicket.numbers(), bonus))

.filter(matchType -> LotteryMatchType.MISS_MATCH != matchType)
.collect(groupingBy(a -> a, summingInt(a -> 1)))));
}

public Ticket lastWeekWinningNumbers() {
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/lottery/LotteryMatchType.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public enum LotteryMatchType {
THREE_MATCH(3, Money.won(5_000L)),
FOUR_MATCH(4, Money.won(50_000L)),
FIVE_MATCH(5, Money.won(1_500_000L)),
FIVE_MATCH_WITH_BONUS(5, Money.won(30_000_000L)),
SIX_MATCH(6, Money.won(2_000_000_000L));

private final int matchCount;
Expand All @@ -27,7 +28,7 @@ public int matchCount() {

public static LotteryMatchType fromInteger(int matchCount) {
return Arrays.stream(LotteryMatchType.values())
.filter(a -> a.matchCount == matchCount)
.filter(matchType -> matchType.matchCount == matchCount)
.findFirst()
.orElse(MISS_MATCH);
}
Expand Down
21 changes: 20 additions & 1 deletion src/main/java/lottery/LottoNumbers.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,30 @@ public Set<Integer> getValues() {
return Collections.unmodifiableSet(numbers);
}

public int matchCountWith(LottoNumbers numbers){
public LotteryMatchType getMatchTypeWith(LottoNumbers numbers, int bonus) {
LotteryMatchType matchType = LotteryMatchType.fromInteger(getSameNumberCount(numbers));
if (isBonusMatchCount(bonus, matchType)) {
return LotteryMatchType.FIVE_MATCH_WITH_BONUS;
}
if (isNotContainsBonus(bonus, matchType)) {
return LotteryMatchType.FIVE_MATCH;
}
return matchType;
}

Choose a reason for hiding this comment

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

규칙 2: else 예약어를 쓰지 않는다.

if를 2개 사용한다고 위 원칙이 지켜진다고 보긴 어려울 것 같습니다.

enum의 values()는 순서를 보장했던 것 같은데 그렇다고 하면 FIVE_MATCH가 우선적으로 반환되어 아래와 같이도 사용할 수 있을 것 같아요.

다만 조금 혼란을 줄 순 있으니 여러 방법들을 한번 고민해보시면 좋을 것 같습니다.

Suggested change
if (isBonusMatchCount(bonus, matchType)) {
return LotteryMatchType.FIVE_MATCH_WITH_BONUS;
}
if (isNotContainsBonus(bonus, matchType)) {
return LotteryMatchType.FIVE_MATCH;
}
return matchType;
}
if (isBonusMatchCount(bonus, matchType)) {
return LotteryMatchType.FIVE_MATCH_WITH_BONUS;
}
return matchType;
}

Copy link
Author

Choose a reason for hiding this comment

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

저도 이부분에 대해서 고민이 많았습니다. values()가 순서를 보장하지만, enum의 순서를 개발자가 조금이라도 변경하면- 사이드 이펙트가 발견될 수 있을 확률이 높은 것 같아, 위와같이 수정했습니다.

조금 더 고민해보겠습니다!


private int getSameNumberCount(LottoNumbers numbers) {
this.numbers.removeAll(numbers.getValues());
return SIZE_OF_TICKET - this.numbers.size();
}

private boolean isBonusMatchCount(int bonus, LotteryMatchType matchType) {
return matchType == LotteryMatchType.FIVE_MATCH && this.numbers.contains(bonus);
}

private boolean isNotContainsBonus(int bonus, LotteryMatchType matchType) {
return matchType == LotteryMatchType.FIVE_MATCH && (this.numbers.contains(bonus) == false);
}

@Override
public boolean equals(Object o) {
if (this == o)
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/lottery/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ public static void main(String[] args) {
.collect(Collectors.toCollection(LinkedHashSet::new));
infoCenter.setLastWeekWinningTicket(Ticket.of(LottoNumbers.from(ints)));

inputView.askForBonusNumber();
int bonus = Integer.parseInt(sc.nextLine().trim());
infoCenter.setBonusNumber(bonus);

Result result = buyer.checkTicket(infoCenter);
float totalYield = result.getTotalYield(initialMoney);
resultView.printResult(result, totalYield);
Expand Down
14 changes: 10 additions & 4 deletions src/main/java/lottery/view/InputView.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,30 @@
public class InputView {

public void askForBuyerMoney() {
System.out.println("구입금액을 입력해 주세요");
print("구입금액을 입력해 주세요");
}

public void inputCountOfTickets(int count) {
System.out.println(count + "개를 구매했습니다.");
print(count + "개를 구매했습니다.");
}

public void printTickets(Tickets tickets) {
tickets.getValues().forEach(ticket -> {
Set<Integer> numbers = ticket.numbers().getValues();
System.out.println("[" + numbers.stream()
print("[" + numbers.stream()
.map(String::valueOf)
.collect(Collectors.joining(", ")) + "]");
});
}

public void askForLastWeekTickets() {
System.out.println("지난 주 당첨 번호를 입력해 주세요.");
print("지난 주 당첨 번호를 입력해 주세요.");
}
public void askForBonusNumber(){
print("보너스 볼을 입력해 주세요.");
}

private void print(String x) {
System.out.println(x);
}
}
43 changes: 36 additions & 7 deletions src/main/java/lottery/view/ResultView.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,45 @@
package lottery.view;

import java.util.Arrays;
import java.util.Map;

import lottery.LotteryMatchType;
import lottery.Result;

public class ResultView {

public void printResult(Result result, float yield) {
System.out.println("당첨 통계");
System.out.println("-----------");
result.getResultMap()
.forEach(
(matchType, count) -> System.out.printf("%d개 일치 (%d원) - %d개\n", matchType.matchCount(), matchType.money().amount(), count)
);
System.out.printf("총 수익률은 %.2f 입니다.(기준이 1이기 때문에 결과적으로 손해라는 의미임)\n", yield);

print("당첨 통계");
print("-----------");
final Map<LotteryMatchType, Integer> resultMap = result.getResultMap();
Arrays.stream(LotteryMatchType.values())
.filter(matchType -> matchType != LotteryMatchType.MISS_MATCH)
.forEachOrdered(
matchType -> {
if (resultMap.containsKey(matchType)) {
makeMatchStringTemplate(matchType, resultMap.get(matchType));
}
makeMatchStringTemplate(matchType, 0);
}
);
System.out.printf("총 수익률은 %.2f입니다.(기준이 1이기 때문에 결과적으로 손해라는 의미임)\n", yield);
}

private void makeMatchStringTemplate(LotteryMatchType matchType, Integer count) {
StringBuilder sb = new StringBuilder();
StringBuilder append = sb.append(matchType.matchCount())
.append("개 일치");
if (matchType == LotteryMatchType.FIVE_MATCH_WITH_BONUS) {
append.append(", 보너스 볼 일치");
}
append.append(" (")
.append(matchType.money().amount())
.append("원)- " + count + "개");
System.out.println(append);
}

private void print(String 당첨_통계) {
System.out.println(당첨_통계);
}
}
63 changes: 44 additions & 19 deletions src/test/java/lottery/InfoCenterTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static org.assertj.core.api.Assertions.*;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;

Expand Down Expand Up @@ -37,7 +38,7 @@ void setUp() {

Result result = sut.confirmTicket(Tickets.of(Lists.list(buyerTicket)));

assertEqualMatchCount(result, 1, 0, 0, 0);
assertEqualMatchCount(result, 1, 0, 0, 0, 0);
}

@Test
Expand All @@ -47,17 +48,17 @@ void setUp() {
sut.setLastWeekWinningTicket(lastWinningNumber);

Result result = sut.confirmTicket(Tickets.of(Lists.list(buyerTicket)));
assertEqualMatchCount(result, 0, 1, 0, 0);
assertEqualMatchCount(result, 0, 1, 0, 0, 0);
}

@Test
void 로또_5개자리_일치() {
Ticket buyerTicket = Ticket.of(LottoNumbers.from(Sets.newLinkedHashSet(1, 2, 3, 4, 5, 13)));
Ticket lastWinningNumber = Ticket.of(LottoNumbers.from(Sets.newLinkedHashSet(1, 2, 3, 4, 5, 6)));
sut.setLastWeekWinningTicket(lastWinningNumber);

sut.setBonusNumber(45);
Result result = sut.confirmTicket(Tickets.of(Lists.list(buyerTicket)));
assertEqualMatchCount(result, 0, 0, 1, 0);
assertEqualMatchCount(result, 0, 0, 1, 0, 0);
}

@Test
Expand All @@ -67,49 +68,73 @@ void setUp() {
sut.setLastWeekWinningTicket(lastWinningNumber);

Result result = sut.confirmTicket(Tickets.of(Lists.list(buyerTicket)));
assertEqualMatchCount(result, 0, 0, 0, 1);
assertEqualMatchCount(result, 0, 0, 0, 1, 0);
}

@Test
void 복수의_로또티켓을_체크할수_있다() {
List<Ticket> tickets = new ArrayList<>();
tickets.add(Ticket.of(LottoNumbers.from(Sets.newLinkedHashSet(1, 2, 3, 4, 5, 6))));
tickets.add(Ticket.of(LottoNumbers.from(Sets.newLinkedHashSet(1, 2, 3, 4, 5, 6))));
tickets.add(Ticket.of(LottoNumbers.from(Sets.newLinkedHashSet(1, 2, 3, 4, 5, 6))));

Tickets tickets = getTickets(Sets.newLinkedHashSet(1, 2, 3, 4, 5, 6));
Ticket lastWinningNumber = Ticket.of(LottoNumbers.from(Sets.newLinkedHashSet(1, 2, 3, 4, 5, 6)));
sut.setLastWeekWinningTicket(lastWinningNumber);

Result result = sut.confirmTicket(Tickets.of(tickets));
Result result = sut.confirmTicket(tickets);

assertEqualMatchCount(result, 0, 0, 0, 3);
assertEqualMatchCount(result, 0, 0, 0, 1, 0);
}

@Test
void 수익률을_계산한다() {
List<Ticket> tickets = new ArrayList<>();
tickets.add(Ticket.of(LottoNumbers.from(Sets.newLinkedHashSet(1, 2, 3, 9, 10, 11))));
tickets.add(Ticket.of(LottoNumbers.from(Sets.newLinkedHashSet(1, 2, 3, 9, 10, 11))));
tickets.add(Ticket.of(LottoNumbers.from(Sets.newLinkedHashSet(1, 2, 3, 9, 10, 11))));
Tickets tickets = getTickets(Sets.newLinkedHashSet(1, 2, 3, 9, 10, 11),
Sets.newLinkedHashSet(1, 2, 3, 9, 10, 11),
Sets.newLinkedHashSet(1, 2, 3, 9, 10, 11)
);

Money money = Money.won(3000L);
float expected = 5.0f;

Ticket lastWinningNumber = Ticket.of(LottoNumbers.from(Sets.newLinkedHashSet(1, 2, 3, 4, 5, 6)));
sut.setLastWeekWinningTicket(lastWinningNumber);

Result result = sut.confirmTicket(Tickets.of(tickets));
Result result = sut.confirmTicket(tickets);
float totalYield = result.getTotalYield(money);
assertThat(totalYield).isEqualTo(expected);
}

@Test
void 보너스점수로_당첨될_경우() {
Tickets tickets = getTickets(
Sets.newLinkedHashSet(1, 2, 3, 4, 5, 11),
Sets.newLinkedHashSet(1, 2, 3, 4, 5, 11),
Sets.newLinkedHashSet(1, 2, 3, 4, 5, 11)
);
Ticket lastWinningNumber = Ticket.of(LottoNumbers.from(Sets.newLinkedHashSet(1, 2, 3, 4, 5, 6)));
sut.setLastWeekWinningTicket(lastWinningNumber);
int bonus = 11;
sut.setBonusNumber(bonus);

Result result = sut.confirmTicket(tickets);

assertEqualMatchCount(result, 0, 0, 0, 0, 3);
}

private void assertEqualMatchCount(Result result, int threeMatchNumber, int fourMatchNumber, int fiveMatchNumber,
int sixMatchNumber) {
int sixMatchNumber, int bonus) {

Map<LotteryMatchType, Integer> resultMap = result.getResultMap();

assertThat(resultMap.getOrDefault(LotteryMatchType.THREE_MATCH, 0)).isEqualTo(threeMatchNumber);
assertThat(resultMap.getOrDefault(LotteryMatchType.FOUR_MATCH, 0)).isEqualTo(fourMatchNumber);
assertThat(resultMap.getOrDefault(LotteryMatchType.FIVE_MATCH,0)).isEqualTo(fiveMatchNumber);
assertThat(resultMap.getOrDefault(LotteryMatchType.SIX_MATCH,0)).isEqualTo(sixMatchNumber);
assertThat(resultMap.getOrDefault(LotteryMatchType.FIVE_MATCH, 0)).isEqualTo(fiveMatchNumber);
assertThat(resultMap.getOrDefault(LotteryMatchType.FIVE_MATCH_WITH_BONUS, 0)).isEqualTo(bonus);
assertThat(resultMap.getOrDefault(LotteryMatchType.SIX_MATCH, 0)).isEqualTo(sixMatchNumber);
}

private Tickets getTickets(LinkedHashSet<Integer>... numbers) {
List<Ticket> tickets = new ArrayList<>();
for (LinkedHashSet<Integer> number : numbers) {
tickets.add(Ticket.of(LottoNumbers.from(number)));
}
return Tickets.of(tickets);
}
}