본문 바로가기

강의/TDD, Clean Code with Java 12기

[로또] step3 - 로또(2등) - 27일차 (피드백 및 피드백 반영)

private HashMap<Rank, Integer> resultRankMap = new HashMap<>();
private Map<Rank, Integer> resultRankMap = new HashMap<>();
이펙티브 자바 아이템 64. 객체는 인터페이스를 사용해 참조하라를 참고해주세요~
다른 부분도(ex. ArrayList -> List)도 같이 확인해주세요 :)

 

수정완료!


 if (countOfMatch == 6) {
    return FIRST;
}
if (countOfMatch == 5 && matchBonus) {
    return SECOND;
}
if (countOfMatch == 5 && !matchBonus) {
    return THIRD;
}
if (countOfMatch == 4) {
    return FOURTH;
}
if (countOfMatch == 3) {
    return FIFTH;
}
하드코딩을 하지 않고 Rank의 값을 활용해보면 어떨까요!?

아래와 같이 수정.

public static Rank valueOf(int countOfMatch, boolean matchBonus) {
    if (countOfMatch == Rank.FIRST.getCountOfMatch()) {
        return FIRST;
    }
    if (countOfMatch == Rank.SECOND.getCountOfMatch() && matchBonus) {
        return SECOND;
    }
    if (countOfMatch == Rank.THIRD.getCountOfMatch() && !matchBonus) {
        return THIRD;
    }
    if (countOfMatch == Rank.FOURTH.getCountOfMatch()) {
        return FOURTH;
    }
    if (countOfMatch == Rank.FIFTH.getCountOfMatch()) {
        return FIFTH;
    }
    return MISS;
}

int numOfLotto = inputView.requestInput();
ArrayList<ArrayList<Integer>> issuedLottoList = customer.buyLotto(numOfLotto);
resultView.printIssuedLottoList(issuedLottoList);
ArrayList<Integer> winningLottoNumberList = winningLottoNumbers.checkValidInput(inputView.requestWinningLottoNumber());
int bonusBallNumber = inputView.requestBonusBall();
전체적으로 객체들을 원시값과 문제열로 포장하고 일급 콜렉션을 사용해보면 어떨까요!?
규칙 3: 모든 원시값과 문자열을 포장한다.
규칙 8: 일급 콜렉션을 쓴다.

 

아래와 같이 수정 완료

LottoInfo lottoInfo = new LottoInfo(inputView.requestInput());
LottoTicket lottoTicket = new LottoTicket(customer.buyLotto(lottoInfo.getCount()));
resultView.printNumOfLotto(lottoInfo.getCount());
resultView.printIssuedLottoList(lottoTicket.getBundle());
WinningLotto winningLotto = new WinningLotto(inputView.requestWinningLottoNumber());
BonusBall bonusBall = new BonusBall(inputView.requestBonusBall(), winningLotto.getNumbers());

 if (issuedLotto.contains(bonusNumber)) {
      return true;
  }
  return false;

 

아래와 같이 수정

 

return issuedLotto.contains(bonusNumber);

 

public ArrayList<Integer> getRandomNumberLotto() {
해당 부분에 랜덤한 요소가 존재한다면 해당 메서드는 어떻게 테스트할 수 있을까요?

랜덤한 값에 대한 테스트는 불가능하다고 생각하였습니다.
그래서 LottoMachineTest 부분에서 랜덤한 값 자체를 테스트 하기 보다는
값의 중복이 존재하지 않는가에 초점을 맞추어 테스트를 진행해보았습니다!

 


전체 코드

package step3.domain;

import java.util.List;

public class BonusBall {
    private int number;

    public BonusBall(int number,List<Integer> winningLottoNumberList) {
        compareWithWinningLottoNumbers(number, winningLottoNumberList);
        this.number = number;
    }

    public int getNumber() {
        return number;
    }

    public void compareWithWinningLottoNumbers(int bonusBallNumber, List<Integer> winningLottoNumberList) {
        if (winningLottoNumberList.contains(bonusBallNumber)) {
            throw new IllegalArgumentException("보너스 볼의 번호는 당첨 번호와 일치할 수 없습니다.");
        }
    }
}
package step3.domain;

import step3.util.Rank;

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

public class Customer {
    private static int resultPrize = 0;

    private static final LottoMachine lottoMachine = new LottoMachine();
    private HashMap<Rank, Integer> resultRankMap = new HashMap<>();

    public Customer() {
        preareRankMap();
    }

    private void preareRankMap() {
        for (Rank rank: Rank.values()) {
            resultRankMap.put(rank,0);
        }
    }

    public List<List<Integer>> buyLotto(int numOfLotto) {
        return lottoMachine.issue(numOfLotto);
    }

    public void compareWinningLottoNumbersAndIssuedLottoList(List<Integer> winningLottoNumberList, List<List<Integer>> issuedLottoList, int bonusNumber) {
        int numOfLotto = issuedLottoList.size();
        for (int i = 0; i < numOfLotto; i++) {
            System.out.println(issuedLottoList.get(i));
            getMatchedNumber(issuedLottoList.get(i), winningLottoNumberList, bonusNumber);
        }
    }

    public int getResultPrize() {
        return resultPrize;
    }

    public Map<Rank, Integer> getResultRankMap() {
        return resultRankMap;
    }



    public void getMatchedNumber(List<Integer> issuedLotto, List<Integer> winningLottoNumberList, int bonusNumber) {
        boolean isMatched = false;
        isMatched = compareWithBonusBall(issuedLotto, bonusNumber);

        issuedLotto.retainAll(winningLottoNumberList);
        int matchedNumber = issuedLotto.size();

        Rank rank = Rank.valueOf(matchedNumber, isMatched);
        resultRankMap.put(rank,resultRankMap.get(rank)+1);
        resultPrize += rank.getWinningMoney();
    }

    private boolean compareWithBonusBall(List<Integer> issuedLotto, int bonusNumber) {
        return issuedLotto.contains(bonusNumber);
    }


}
package step3.domain;

public class Lotto {
    private static final String LOTTO_NUMBER_RANGE_ERROR_MESSAGE = "로또 당첨번호는 1과 45 사이의 정수이어야 합니다.";
    private static final int MIN_OF_LOTTO_NUMBER = 1;
    private static final int MAX_OF_LOTTO_NUMBER = 45;

    private final int number;

    public Lotto(int number) {
        checkRange(number);
        this.number = number;
    }

    private void checkRange(int number) {
        if (number > MAX_OF_LOTTO_NUMBER || number < MIN_OF_LOTTO_NUMBER) {
            throw new IllegalArgumentException(LOTTO_NUMBER_RANGE_ERROR_MESSAGE);
        }
    }

    public int getNumber() {
        return number;
    }
}
package step3.domain;

public class LottoInfo {
    private static final int chargeOfLotto = 1000;
    private int charge;
    private int count;

    public LottoInfo(int charge) {
        calculateNumOfLotto(charge);
        this.charge = charge;
    }

    private void calculateNumOfLotto(int charge) {
        this.count = charge/chargeOfLotto;
    }

    public int getCharge() {
        return charge;
    }

    public int getCount() {
        return count;
    }
}
package step3.domain;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class LottoMachine {
    private List<List<Integer>> lottoList = new ArrayList<>();
    static final private List<Integer> lottoNumberCandidateList = new ArrayList<>();

    public LottoMachine() {
        getLottoNumberCandidateArray();
    }

    public List<List<Integer>> issue(int numOfLotto) {
        for (int i = 0; i < numOfLotto; i++) {
            lottoList.add(getRandomNumberLotto());
        }
        return lottoList;
    }

    public List<Integer> getRandomNumberLotto() {
        List<Integer> lotto = new ArrayList<>();
        Collections.shuffle(lottoNumberCandidateList);
        for (int i = 0; i < 6; i++) {
            lotto.add(new Lotto(lottoNumberCandidateList.get(i)).getNumber());
        }
        return lotto;
    }

    private void getLottoNumberCandidateArray() {
        for (int i = 1; i <= 45; i++) {
            lottoNumberCandidateList.add(i);
        }
    }
}
package step3.domain;

import java.util.List;

public class LottoTicket {
    private List<List<Integer>> bundle;

    public LottoTicket(List<List<Integer>> bundle) {
        this.bundle = bundle;
    }

    public List<List<Integer>> getBundle() {
        return bundle;
    }
}
package step3.domain;

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

public class WinningLotto {
    private static final int numOfWinningLottoNumber = 6;
    private static final String winningLottoNumberErrorMessage = "당첨 번호는 6개로 구성되어야 하며, ', ' 을 이용하여 구분하고 있습니다.";

    private List<Integer> numbers;

    public WinningLotto(String stringNumbers) {
        this.numbers = checkValidInput(stringNumbers);
    }

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

    public List<Integer> checkValidInput(String s) {
        String[] winningLottoNumberStringList = s.split(", ");
        if (winningLottoNumberStringList.length != numOfWinningLottoNumber) {
            throw new IllegalArgumentException(winningLottoNumberErrorMessage);
        }
        return makeIntList(winningLottoNumberStringList);

    }
    private List<Integer> makeIntList(String[] winningLottoNumberStringList) {
        List<Integer> winningLottoNumberIntList = new ArrayList<>();
        for (String stringLottoNumber : winningLottoNumberStringList) {
            winningLottoNumberIntList.add(Integer.parseInt(stringLottoNumber));
        }
        return winningLottoNumberIntList;
    }

}
package step3.util;

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),
    MISS(0, 0);

    private int countOfMatch;
    private int winningMoney;

    private Rank(int countOfMatch, int winningMoney) {
        this.countOfMatch = countOfMatch;
        this.winningMoney = winningMoney;
    }

    public int getCountOfMatch() {
        return countOfMatch;
    }

    public int getWinningMoney() {
        return winningMoney;
    }

    public static Rank valueOf(int countOfMatch, boolean matchBonus) {
        if (countOfMatch == Rank.FIRST.getCountOfMatch()) {
            return FIRST;
        }
        if (countOfMatch == Rank.SECOND.getCountOfMatch() && matchBonus) {
            return SECOND;
        }
        if (countOfMatch == Rank.THIRD.getCountOfMatch() && !matchBonus) {
            return THIRD;
        }
        if (countOfMatch == Rank.FOURTH.getCountOfMatch()) {
            return FOURTH;
        }
        if (countOfMatch == Rank.FIFTH.getCountOfMatch()) {
            return FIFTH;
        }
        return MISS;
    }
}
package step3.view;

import java.util.Scanner;

public class InputView {
    static final String INPUT_MESSAGE = "구입 금액을 입력해주세요.";
    static final String INPUT_LAST_LOTTO_NUMBER = "지난 주 당첨 번호를 입력해 주세요.";

    static Scanner sc = new Scanner(System.in);

    public int requestInput() {
        System.out.println(INPUT_MESSAGE);
        int charge = sc.nextInt();
        sc.nextLine();
        return charge;
    }

    public String requestWinningLottoNumber() {
        System.out.println(INPUT_LAST_LOTTO_NUMBER);
        String lastWinningLottoNum = sc.nextLine();
        return lastWinningLottoNum;
    }

    public Integer requestBonusBall() {
        System.out.println("보너스 볼을 입력해 주세요.");
        return sc.nextInt();
    }
}
package step3.view;

import step3.util.Rank;

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

public class ResultView {
    private static final Double chargeOfLotto = 1000.0;
    static final String INPUT_INFO_MESSAGE = "개를 구매했습니다.";

    public void printMatchedLottoRecord(Map<Rank, Integer> matchedLottoRecordArray) {
        System.out.println("당첨 통계");
        System.out.println("-------");
        System.out.printf("%s개 일치 (%s)- %s개\n",Rank.FIFTH.getCountOfMatch(),Rank.FIFTH.getWinningMoney(),matchedLottoRecordArray.get(Rank.FIFTH));
        System.out.printf("%s개 일치 (%s)- %s개\n",Rank.FOURTH.getCountOfMatch(),Rank.FOURTH.getWinningMoney(),matchedLottoRecordArray.get(Rank.FOURTH));
        System.out.printf("%s개 일치 (%s)- %s개\n",Rank.THIRD.getCountOfMatch(),Rank.THIRD.getWinningMoney(),matchedLottoRecordArray.get(Rank.THIRD));
        System.out.printf("%s개 일치 (%s)- %s개\n",Rank.SECOND.getCountOfMatch(),Rank.SECOND.getWinningMoney(),matchedLottoRecordArray.get(Rank.SECOND));
        System.out.printf("%s개 일치 (%s)- %s개\n",Rank.FIRST.getCountOfMatch(),Rank.FIRST.getWinningMoney(),matchedLottoRecordArray.get(Rank.FIRST));
    }

    private void printResultMessage(double yield) {
        if (yield < 1) {
            System.out.printf("총 수익률은 %s입니다.(기준이 1이기 때문에 결과적으로 손해라는 의미임)",yield);
            return;
        }
        if (yield >= 1) {
            System.out.printf("총 수익률은 %s입니다.(기준이 1이기 때문에 결과적으로 이익이라는 의미임)",yield);
            return;
        }

    }

    public void printNumOfLotto(int numOfLotto) {
        System.out.println(numOfLotto+INPUT_INFO_MESSAGE);
    }

    public void printIssuedLottoList(List<List<Integer>> issuedLottolist) {
        issuedLottolist.stream()
                .forEach(issuedLotto -> System.out.println(issuedLotto));
    }

    public void printResultPrice(int resultPrice, int numOfLotto) {
        Double totalSpendMoney = numOfLotto * chargeOfLotto;
        Double yeild = resultPrice / totalSpendMoney;
        printResultMessage(yeild);
    }
}
package step3;

import step3.domain.*;
import step3.view.InputView;
import step3.view.ResultView;

import java.util.List;

public class LottoApp {
    public static void main(String[] args) {
        InputView inputView = new InputView();
        ResultView resultView = new ResultView();
        Customer customer = new Customer();

        LottoInfo lottoInfo = new LottoInfo(inputView.requestInput());
        LottoTicket lottoTicket = new LottoTicket(customer.buyLotto(lottoInfo.getCount()));
        resultView.printNumOfLotto(lottoInfo.getCount());
        resultView.printIssuedLottoList(lottoTicket.getBundle());
        WinningLotto winningLotto = new WinningLotto(inputView.requestWinningLottoNumber());
        BonusBall bonusBall = new BonusBall(inputView.requestBonusBall(), winningLotto.getNumbers());
        customer.compareWinningLottoNumbersAndIssuedLottoList(winningLotto.getNumbers(), lottoTicket.getBundle(), bonusBall.getNumber());

        resultView.printMatchedLottoRecord(customer.getResultRankMap());
        resultView.printResultPrice(customer.getResultPrize(), lottoInfo.getCount());


    }

}