우테코 5기 3주차 미션을 다시 설계하고 객체 지향 프로그래밍을 연습을 초점에 맞추어 글을 써봅니다.
입출력의 요구사항 설계는 생략 하니 이점 양해 부탁 드립니다.
우선 프로그래밍 요구사항을 살펴보고 개념 또는 도메인을 정해 역할을 부여해보자.
🚀 기능 요구 사항
로또 게임 기능을 구현해야 한다. 로또 게임은 아래와 같은 규칙으로 진행된다.
- `로또 번호의 숫자 범위는 1~45까지이다.
- 1개의 로또를 발행할 때 중복되지 않는 6개의 숫자를 뽑는다.
- 당첨 번호 추첨 시 중복되지 않는 숫자 6개와 보너스 번호 1개를 뽑는다.
- 당첨은 1등부터 5등까지 있다. 당첨 기준과 금액은 아래와 같다. - 1등: 6개 번호 일치 / 2,000,000,000원 - 2등: 5개 번호 + 보너스 번호 일치 / 30,000,000원 - 3등: 5개 번호 일치 / 1,500,000원 - 4등: 4개 번호 일치 / 50,000원 - 5등: 3개 번호 일치 / 5,000원`
- 로또 구입 금액을 입력하면 구입 금액에 해당하는 만큼 로또를 발행해야 한다.
- 로또 1장의 가격은 1,000원이다.
- 당첨 번호와 보너스 번호를 입력받는다.
- 사용자가 구매한 로또 번호와 당첨 번호를 비교하여 당첨 내역 및 수익률을 출력하고 로또 게임을 종료한다.
- 사용자가 잘못된 값을 입력할 경우 IllegalArgumentException를 발생시키고, "[ERROR]"로 시작하는 에러 메시지를 출력 후 종료한다.
개념 or 도메인 정하기
LottoGame
- 게임을 시작하는 역할
LottoShop
- 로또를 판매 하는 역할
Customer
- 로또를 구매 하는 역할
WinningLotto
- 당첨 로또 번호를 생성 하는 역할
Lotto
- 로또 번호를 가지고 있다.
LottoRankAggregation
- 당첨을 집계하는 역할
도메인에 메시지를 부여하여 구성도를 그려보자
그려보기 전 협력은 2가지로 나뉜다.
첫번째 구매자가 구매금액에 대한 로또를 사는 것이다.
두번째 당첨내역을 조회하는 것이다.
구성도를 그려보았으니 이제 구현만 남았다.
첫번째 협력부터 시작해보자.
- LottoGame.class
public class LottoGame {
private LottoShop lottoShop;
public LottoGame(LottoShop lottoShop) {
this.lottoShop = lottoShop;
}
public void start(Customer customer, int amount) {
lottoShop.sellTo(customer, amount);
}
}
게임은 start(Customer) 로 게임을 시작한다.
그리고 lottoShop 에게 sellTo(customer, amount) 메시지를 요청한다.
- LottoShop.class
public class LottoShop {
private static final intLOTTO_PRICE= 1000;
private Lotto lotto;
public LottoShop(Lotto lotto) {
this.lotto = lotto;
}
public void sellTo(Customer customer, int amount) {
try {
customer.buy(lotto.createBuyLotto(calculateQuantity(amount)));
} catch (IllegalStateException e) {
System.out.println(e.getMessage());
}
}
private int calculateQuantity(int amount) {
if (amount %LOTTO_PRICE!= 0) {
throw new IllegalStateException(ErrorCode.ERROR.getMessage());
}
return amount /LOTTO_PRICE;
}
}
구매 금액에 대한 수량 계산은 LottoShop 에게 책임이 있고 LottoShop 내부에서만 처리 하니 private으로 은닉하였다.
이는 객체 지향의 캡슐화에 장점을 살렸다.
- Lotto.class
public class Lotto {
private final List<Integer> numbers;
public Lotto(List<Integer> numbers) {
this.numbers = numbers;
}
public List<Lotto> createBuyLotto(int quantity) {
List<Lotto> lottos = new ArrayList<>();
for (int count = 1; count <= quantity; count++) {
Lotto lotto = new Lotto(Randoms.pickUniqueNumbersInRange(1, 45, 6));
lottos.add(lotto);
}
return lottos;
}
public List<Integer> getNumbers() {
return numbers;
}
}
수량에 대한 로또 생성은 Lotto 자기 자신이 해야하는 책임이다.
그러므로 Lotto 클래스에 생성하는 로직을 가진다.
다음으로 Customer는 로또를 구매해야 하니 buy() 메서드를 구현해보자.
- Customer.class
public class Customer {
private List<Lotto> lottos = new ArrayList<>();
public void buy(List<Lotto> lottos) {
this.lottos.addAll(lottos);
}
public List<Lotto> getLottos() {
return lottos;
}
}
이로써 첫번째 협력에 대한 구현은 끝났다고 본다.
이제 두번째 협력을 구현해보자.
- LottoGame.class
public class LottoGame {
private LottoShop lottoShop;
private WinningLotto winningLotto;
public LottoGame(LottoShop lottoShop, WinningLotto winningLotto) {
this.lottoShop = lottoShop;
this.winningLotto = winningLotto;
}
public void start(Customer customer, int amount) {
lottoShop.sellTo(customer, amount);
}
public void winningHistory(Customer customer, int amount) {
OutPutView.showRankAggregation(winningLotto.compareTo(customer), amount);
}
}
winningHistory(customer, amount) 는 winningLotto에게 compareTo() 를 메시지 요청을 보낸다.
- WinningLotto.class
public class WinningLotto {
private LottoRankAggregation lottoRankAggregation;
public WinningLotto(LottoRankAggregation lottoRankAggregation) {
this.lottoRankAggregation = lottoRankAggregation;
}
public ResponseRankAggregationDto compareTo(Customer customer) {
return lottoRankAggregation.aggregation(customer, InputView.readWinningLotto(), InputView.readBonusNumber());
}
}
요청을 받은 winningLotto 는 당첨 번호화 보너스 번호를 입력 받고 lottoRankAggregation에게 aggregation() 메시지 요청을 한다.
- LottoRankAggregation.class
public ResponseRankAggregationDto aggregation(Customer customer, List<Integer> winningLotto, int bonusNumber) {
for (Lotto lotto : customer.getLottos()) {
Rank winningRank = Rank.valueOf(getWinningCount(winningLotto, lotto),isMatchBonusNumber(bonusNumber, lotto));
if (winningRank.equals(Rank.LOSE)) {
continue;
}
rankAggregationMap.put(winningRank, rankAggregationMap.computeIfPresent(winningRank, (rank, count) -> count += 1));
}
return ResponseRankAggregationDto.of(rankAggregationMap);
}
각각 순위별로 당첨 횟수를 저장 하여 결과를 반환해준다.
이로서 협력의 구현은 끝났습니다.
코드 구현은 쉬웠으나 객체 설계에는 시간을 많이 걸린 것 같습니다.
부족한 부분이 있으면 피드백 해주시면 감사드립니다.
'Laguage' 카테고리의 다른 글
[Java] 숫자 야구 게임 (객체 지향 편) (0) | 2022.11.27 |
---|---|
[Java] 지네릭 변성 (0) | 2022.08.13 |
[Java] Decorator Pattern (0) | 2022.07.16 |
[Java] Exception (0) | 2022.07.15 |
[Java] Stream (0) | 2022.07.14 |
[Java] Lambda expression (0) | 2022.07.14 |
[Java] Inner Class (0) | 2022.07.14 |
[Java] Generic Programing (0) | 2022.07.14 |