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

[로또 main2] 조윤호 제출합니다 #4

Open
wants to merge 15 commits into
base: main2
Choose a base branch
from
40 changes: 40 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
## 기능 리스트

- [ ] 가격 -> 개수 반환
- [ ] 로또 발행
- [ ] 총 일치 수, 금액, 수익률 계산
- [ ] 정답로또 입력받기
- validation
- 보너스점수
- [ ] ㅇ


PurchaseService
- (금액) -> 개수 // thr

Enum Prize
- 출력문구 matches bonus reward

LottoService


LottoBundle
- 로또 수
- LottoStatus list
-
- Lotto 정답로또
- 보너스번호
-

LottoStatus
- Lotto
- 일치 수
- 보너스 여부
- (정답로또, 보너스) -> 일치 수 , 보너스 여부
-

Lotto
- 랜덤로또 발행
- 정답로또 생성
-

5 changes: 4 additions & 1 deletion src/main/java/lotto/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package lotto;

import lotto.lotto.LottoService;

public class Application {
public static void main(String[] args) {
// TODO: 프로그램 구현
LottoService lottoService = new LottoService();
lottoService.run();
}
}
18 changes: 18 additions & 0 deletions src/main/java/lotto/lotto/Bonus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package lotto.lotto;

public class Bonus {
private final int bonusNumber;

private Bonus(int bonusNumber) {
this.bonusNumber = bonusNumber;
}

public static Bonus from(int num, Lotto lotto) {
lotto.validateBonusNumber(num);
return new Bonus(num);
}

public int getBonusNumber() {
return bonusNumber;
}
}
82 changes: 82 additions & 0 deletions src/main/java/lotto/lotto/Lotto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package lotto.lotto;


import lotto.lotto.lottoCreator.LottoCreator;
import lotto.lotto.lottoCreator.RandomLottoCreator;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;

public class Lotto {
private final static int LOTTO_NUM_LENGTH = 6;
private final static int LOTTO_MIN_NUM = 1;
private final static int LOTTO_MAX_NUM = 45;
private final List<Integer> numbers;

public Lotto(List<Integer> numbers) {
this.numbers = numbers;
}

public static Lotto from(LottoCreator lottoCreator) {
return lottoCreator.getLotto(
LOTTO_MIN_NUM,
LOTTO_MAX_NUM,
LOTTO_NUM_LENGTH
);
}

public Prize comparePrize(Lotto winningLotto, int bonus) {
List<Integer> list = new ArrayList<>();
list.addAll(numbers);
list.addAll(winningLotto.numbers);
int matches = list.size() - (new HashSet<>(list)).size();
boolean isBonus = numbers.contains(bonus);

Prize prize = Prize.NONE;
for (Prize p : Prize.values()) {
if (
p.isPrized() &&
p.getMatchNum() <= matches &&
p.getGrade() <= prize.getGrade()
) {
Copy link
Member

Choose a reason for hiding this comment

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

41~43 라인을 하나의 메소드로 뽑으면 가독성도 좋아지고, 메소드 이름으로 어떤 조건인지 표현 가능할 것 같습니다!

if (p.isBonus() && !isBonus) // 보너스 true인 경우 isBonus 확인한다.
continue;
prize = p;
}
}
return prize;
}

public void validateBonusNumber(int bonus) {
validateBonusDuplicateWithLotto(bonus);
validateBonusRange(bonus);
}

public boolean contains(int num) {
return numbers.contains(num);
}

@Override
public String toString() {
return numbers.toString();
}

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


private void validateBonusDuplicateWithLotto(int bonus) {
if (numbers.contains(bonus))
throw new IllegalArgumentException("[ERROR] 보너스 번호와 당첨 번호에 중복이 있습니다.");

}

private void validateBonusRange(int bonus) {
if (bonus < LOTTO_MIN_NUM || bonus > LOTTO_MAX_NUM)
throw new IllegalArgumentException("[ERROR] 보너스 번호와 당첨 번호에 중복이 있습니다.");
}
}
125 changes: 125 additions & 0 deletions src/main/java/lotto/lotto/LottoBundle.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package lotto.lotto;

import lotto.lotto.lottoCreator.LottoCreator;
import lotto.lotto.model.StatusOutputModel;
import lotto.purchase.PurchaseService;

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

public class LottoBundle {
private final List<Lotto> lottos;
private final Lotto winningLotto;
private final Bonus bonus;

private LottoBundle(Builder builder) {
this.lottos = builder.lottos;
this.winningLotto = builder.winningLotto;
this.bonus = builder.bonus;
}

public static class Builder {
Copy link
Member

Choose a reason for hiding this comment

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

빌더 패턴을 사용하신 이유가 궁금해요!

private List<Lotto> lottos = new ArrayList<>();
private Lotto winningLotto;
private Integer bonusNumber;
private Bonus bonus;

public Builder() {}

/**
* lottoCreators만큼 새로운 로또 생성하기
*/
public Builder createLottos(List<LottoCreator> lottoCreators) {
for (LottoCreator creator : lottoCreators) {
Lotto lotto = Lotto.from(creator);
lottos.add(lotto);
}
return this;
}

/**
* 당첨번호 저장하기
*/
public Builder winningLotto(LottoCreator creator) {
Lotto lotto = Lotto.from(creator);
this.winningLotto = lotto;
if (bonusNumber != null)
bonus = Bonus.from(bonusNumber, winningLotto);
return this;
}

/**
* 보너스번호 저장하기
*/
public Builder bonus(int bonusNumber) {
this.bonusNumber = bonusNumber;
if (winningLotto != null)
this.bonus = Bonus.from(bonusNumber, winningLotto);
return this;
}

private void validateBonusNumber(List<Integer> numbers, int num){
if (numbers.contains(num))
throw new IllegalArgumentException("[ERROR] 보너스 번호와 당첨 번호에 중복이 있습니다.");
}
Comment on lines +62 to +65

Choose a reason for hiding this comment

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

사용되지 않는 메소드는 삭제해주세요!


public LottoBundle build() {
return new LottoBundle(this);
}
}

/**
* 로또 출력하기
*/
public List<List<Integer>> getLottosNumList() {
List<List<Integer>> lottoNumbers = new ArrayList<>();
for (Lotto lotto : lottos)
lottoNumbers.add(lotto.getNumbers());
return lottoNumbers;
}

/**
* 당첨 통계 출력하기
*/
public StatusOutputModel getResultStatus() {
Map<Prize, Integer> prizeMap = getPrizeStat();
int totalreward = getTotalReward(prizeMap);
double rewardPercent = PurchaseService.getRewardPercent(lottos.size(), totalreward);

StringBuilder sb = new StringBuilder();
for (Prize prize : Prize.values()) {
if (!prize.isPrized()) continue;
sb.append(prize.getToString());
sb.append(
String.format(" (%,d원) - %d개",
prize.getReward(),
prizeMap.get(prize)
));
sb.append("\n");
}
sb.append(String.format("총 수익률은 %.1f%%입니다.", rewardPercent));

return new StatusOutputModel(sb.toString());
}

private Map<Prize, Integer> getPrizeStat() {
Map<Prize, Integer> prizeMap = Prize.getInitializedMap();
for (Lotto lotto : lottos) {
Prize prize = lotto.comparePrize(winningLotto, bonus.getBonusNumber());
if (prize.isPrized())
prizeMap.put(prize, prizeMap.get(prize) + 1);
}
return prizeMap;
}

private int getTotalReward(Map<Prize, Integer> prizeMap) {
int totalReward = 0;
for (Prize prize : Prize.values()) {
if (prizeMap.get(prize) != 0)
totalReward += prize.getReward() * prizeMap.get(prize);
}
return totalReward;
}

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

import lotto.lotto.lottoCreator.LottoCreator;
import lotto.lotto.lottoCreator.ManualLottoCreator;
import lotto.lotto.lottoCreator.RandomLottoCreator;
import lotto.lotto.model.LottoOutputModel;
import lotto.purchase.PurchaseService;
import lotto.view.UserInput;
import lotto.view.UserOutput;

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

public class LottoService {

public void run() {
LottoBundle.Builder lottoBundleBuilder = new LottoBundle.Builder();

// 구입금액 입력받기
// todo : 구입금액 객체 관리
int lottoNum = PurchaseService.getLottoNum(getNum(UserInput.getCostInput()));
int manualNum = getNum(UserInput.getManualNumInput());
if (lottoNum < manualNum)
throw new IllegalArgumentException("[ERROR] 구입 가능한 로또 수를 초과하였습니다.");

// 자동 로또
List<LottoCreator> creators = Stream.generate(RandomLottoCreator::from)
.limit(lottoNum - manualNum)
.collect(Collectors.toList());
Comment on lines +30 to +32

Choose a reason for hiding this comment

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

RandomLottoCreator가 2개 이상 생성되어야 할까요?
한 번 생성된 RandomLottoCreator에서 getLotto()만 새롭게 호출해도 랜덤으로 생성된 로또를 발급할 수 있을 것 같아요.

// 수동 로또
if (manualNum != 0) {
List<String> strList = UserInput.getManualLottoInput(manualNum);
for (String input : strList)
creators.add(ManualLottoCreator.from(getNumList(input)));
}

lottoBundleBuilder.createLottos(creators);
LottoBundle bundle = lottoBundleBuilder.build();

// 로또 출력하기
UserOutput.printLottos(
new LottoOutputModel(
lottoNum - manualNum, manualNum, bundle.getLottosNumList()
));
Comment on lines +44 to +47

Choose a reason for hiding this comment

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

출력을 위한 값들을 담고 있는 객체가 정말 필요할까요?
로또 게임을 여러번 진행한다고 하면, LottoOutputModel이 계속 생성될텐데 객체 생성시 초기화된 값들을 그대로 꺼내서 사용하는 행위는 불필요해보입니다!


// 당첨번호 입력받기
lottoBundleBuilder.winningLotto(
ManualLottoCreator.from(getNumList(UserInput.getLottoInput())));

// 보너스번호 입력받기
lottoBundleBuilder.bonus(getNum(UserInput.getBonusInput()));
bundle = lottoBundleBuilder.build();

// 당첨 통계 출력하기
UserOutput.printResult(bundle.getResultStatus());
}


public int getNum(String input) {
try {
return Integer.parseInt(input);
} catch (NumberFormatException ne) {
throw new IllegalArgumentException("[ERROR] 정수를 입력해주세요.");
}
}

public List<Integer> getNumList(String input) {
Comment on lines +62 to +70

Choose a reason for hiding this comment

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

두 메소드는 LottoService 내부에서만 사용되는 것 같습니다. private로 선언해주세요!

Choose a reason for hiding this comment

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

해당 메소드에 책임이 너무 많은 것 같아요. 메소드의 책임이 최소화되도록 하면 좋을 것 같아요!

List<String> strList = new ArrayList<>(
Arrays.asList(
input.split(",")
)
);

// "," 개수를 이용해서 배열의 길이가 올바르게 입력되었는지 확인한다.
int commas = input.length() - input.replace(String.valueOf(","), "").length();

Choose a reason for hiding this comment

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

String.valueOf()는 불필요해 보이네요. 삭제해도 괜찮을 것 같아요!

if (commas + 1 != strList.size())
throw new IllegalArgumentException("[ERROR] 정수와 ,로 이루어진 배열을 입력해주세요.");
Comment on lines +78 to +80

Choose a reason for hiding this comment

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

구분자에 대한 검증같은데, 예외 메시지가 어색한 것 같습니다.
그리고 구분자로 사용되는 쉼표(,)는 상수화하면 구분자가 바뀌었을 때 예외 메시지와 replace()에서 사용되는 구분자를 실수없이 수정할 수 있을 것 같아요!


List<Integer> numList = strList.stream()
.map(s -> {
try {
return Integer.parseInt(s);
} catch (NumberFormatException ne) {
throw new IllegalArgumentException("[ERROR] 정수와 ,로 이루어진 배열을 입력해주세요.");
}
Comment on lines +84 to +88

Choose a reason for hiding this comment

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

문자열을 정수로 바꿔주는 getNum(String)이란 함수가 있는데, 이를 재사용하면 어떨까요?

}).collect(Collectors.toList());

return numList;
}

}
Loading