본문 바로가기
교육 및 책/TDD, 클린 코드 with Java

TDD, 클린 코드 - 로또(자동)

by oneny 2023. 4. 23.

로또 자동

 

LottoNumber

public class LottoNumber {

  public static final int LOTTO_MINIMUM_NUMBER = 1;
  public static final int LOTTO_MAXIMUM_NUMBER = 45;
  private static final String ILLEGAL_LOTTO_NUMBER_MESSAGE = "올바른 로또 번호를 입력해주세요. 입력된 번호 : ";
  private final int number;

  public LottoNumber(int number) {
    this.number = validatedLottoNumber(number);
  }

  public int number() {
    return number;
  }

  private int validatedLottoNumber(int number) {
    if (!isLottoNumber(number)) {
      throw new IllegalArgumentException(ILLEGAL_LOTTO_NUMBER_MESSAGE + number);
    }

    return number;
  }

  private boolean isLottoNumber(int number) {
    return number >= LOTTO_MINIMUM_NUMBER && number <= LOTTO_MAXIMUM_NUMBER;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    LottoNumber that = (LottoNumber) o;

    return number == that.number;
  }

  @Override
  public int hashCode() {
    return number;
  }
}

로또넘버에 대한 원시값을 Wrapper Class로 만들었고, 상수 선언한 부분에 대해 👍 받았다!ㅎㅎ

 

LottoTicketTest

public class LottoTicketTest {
  @BeforeEach
  public void setUp() {
    winningLottoTicket = new LottoTicket(() -> List.of(
            new LottoNumber(1),
            new LottoNumber(2),
            new LottoNumber(5),
            new LottoNumber(7),
            new LottoNumber(9),
            new LottoNumber(11)
    ));
  }

  @Test
  @DisplayName("로또 티켓 생성하여 당첨 번호와 매치되는 개수 반환 테스트")
  public void match_로또_번호() {
    LottoTicket lottoTicket = new LottoTicket(() -> List.of(
            new LottoNumber(1),
            new LottoNumber(3),
            new LottoNumber(5),
            new LottoNumber(7),
            new LottoNumber(13),
            new LottoNumber(11)
    ));

    assertThat(lottoTicket.matchLottoCount(winningLottoTicket))
            .isEqualTo(4);
  }
}

메인 로직인 matchLottoCount에 대한 테스트 검정에 대해 👍 받았다ㅎㅎ

 

LottoTicket

public class LottoTicket {

  public static final int TICKET_NUMBER_COUNT = 6;
  private static final String ILLEGAL_COUNT_MESSAGE = "로또 번호는 6개 입력하셔야 합니다.";
  private final List<LottoNumber> lottoTicket;
  
 }

일급 컬렉션으로 구성한 것에 대해 👍 받았지만, 기능 요구사항에 티켓에 대해 정렬이 되어 출력이 되어야 하는데 해당 기능을 만들지 않았다. 그래서 리뷰어 분께서 Collections.sort() 또는 TreeSet 등의 컬렉션을 사용해보면 좋을 것 같다고 말씀해주셨다.

한 번 LottoNumber에 대해 equals 메서드도 있으니 중복되는 수가 들어오는 것도 방지하고, 자동으로 정렬이 되도록 TreeSet으로 구성해봐야 겠다!

 

LottoTickets

public class LottoTickets {
  private final List<LottoTicket> lottoTickets = new ArrayList<>();

  public List<LottoTicket> unmodifiedLottoTickets() {
    return Collections.unmodifiableList(lottoTickets);
  }

  public List<Integer> matchesLottoTickets(LottoTicket winningLottoNumbers) {
    return lottoTickets.stream()
            .map(t -> t.matchLottoCount(winningLottoNumbers)) // 규칙 5: 줄여쓰지 않는다(축약 금지). 를 지키는 것이 좋다는 피드백을 받았다.
            .collect(Collectors.toList());
  }

  public void addLottoTicket(LottoStrategy lottoStrategy) {
    lottoTickets.add(new LottoTicket(lottoStrategy));
  }
}

객체 지향 생활 체조 원칙

  1. 한 메서드에 오직 한 단계의 들여쓰기(indent)만 한다.
  2. else 예약어를 쓰지 않는다.
  3. 모든 원시값과 문자열을 포장한다.
  4. 한 줄에 점을 하나만 찍는다.
  5. 줄여쓰지 않는다(축약 금지).
  6. 모든 엔티티를 작게 유지한다.
  7. 3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다.
  8. 일급 콜렉션을 쓴다.
  9. 게터/세터/프로퍼티를 쓰지 않는다.

 

LottoRank

public enum LottoRank {
  FIRST(6, 2_000_000_000),
  SECOND(5, 1_500_000),
  THIRD(4, 50_000),
  FOURTH(3, 5_000),
  NOTMATCHED(0, 0);

  private static final int UNDER_RANK = 2;
  private static final Map<Integer, LottoRank> BY_MATCHEDCOUNT = new HashMap<>();

  static {
    for (LottoRank lottoRank : values()) {
      BY_MATCHEDCOUNT.put(lottoRank.matchedCount, lottoRank);
    }
  }

  private final int matchedCount;
  private final int prizeMoney;

  LottoRank(final int matchedCount, final int prizeMoney) {
    this.matchedCount = matchedCount;
    this.prizeMoney = prizeMoney;
  }

  public static List<Integer> lottoMatchedNumberList() {
    return BY_MATCHEDCOUNT.keySet().stream()
            .filter(v -> v != 0)
            .sorted()
            .collect(Collectors.toList());
  }

  public static LottoRank valueOfMatchedCount(int count) {
  	// HashMap은 정렬 처리가 되지 않는데 0번쨰는 무엇을 의미하는 걸까요? 🤔 라는 피드백을 받았다.
    // 0을 상수로 빼서 어떤 값을 가져오려는 것인지 의도를 나타내도록 리팩토링해야겠다.
    if (count <= UNDER_RANK) {
      return BY_MATCHEDCOUNT.get(0);
    }

    return BY_MATCHEDCOUNT.get(count);
  }

  public static int prizeMoney(LottoRank lottoRank) {
    return lottoRank.prizeMoney;
  }

  public boolean equalsMatchedCount(int matchedCount) {
    return this.matchedCount == matchedCount;
  }
}