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

[1단계 - 로또 구현] 케빈(박진홍) 미션 제출합니다. #228

Merged
merged 58 commits into from
Feb 21, 2021

Conversation

xlffm3
Copy link

@xlffm3 xlffm3 commented Feb 18, 2021

안녕하세요.

스트림, 람다 등을 연습하고자 반복문 사용을 최대한 지양했는데 다소 억지스럽거나 가독성이 떨어진다면 지적해주세요!
리뷰잘부탁드리겠습니다. :D

ohjoohyung and others added 30 commits February 16, 2021 15:01
필드변수명 변경에 따라 getter 또한 변경한다
등수에 따른 개수라는 의미를 직관적으로 살리기 위해 변경한다
자동으로 티켓을 발권한다는 역할을 메서드명에 드러나도록 변경
- 각 상수들의 접두로 PRIZE를 추가한다.
- 상금 상수의 가독성을 위해 언더바를 추가한다.
- 각 메서드가 어떤 내용을 출력하는지를 명시해준다.
- 또한 반복문을 삭제하고 람다 스트림을 이용한다.
@xlffm3
Copy link
Author

xlffm3 commented Feb 18, 2021

학습 로그


[JDK] Stream API - 4

내용

  • Stream API의 collect(Collectors.joininig(delimiter, prefix, suffix)를 통해 CharSequence 스트림을 파싱.
  • groupingBy()로 요소들의 빈도수(counting())를 Map으로 수집함.
  • 상금을 총합할 때 반복문을 돌지 않고 Stream의 sum() 메서드를 사용.
  • forEach 종단 연산을 통해 일반적인 for-each 반복문의 코드를 짧게 축약할 수 있었음.

링크

[JDK] Map API - 2

내용

  • putIfAbsent(), computeIfAbsent() 등 Map의 특정 Key가 없을 때 수행할 작업을 기존에는 여러 줄로 작성.
  • Map 인터페이스의 API를 통해 1줄로 축약함.

[Design Pattern] Singleton - 3

내용

  • LottoNumber는 내부 비즈니스 로직이 있다기 보다는 ValueObject 성격이 큼.
    • 중복해서 만들 필요가 있을까 하는 의문이 들었음.
  • 간단하게는 어플리케이션 상에서 1개만 존재함을 보장할 수 있는 패턴이다.
  • 단점으로 OCP 위배 가능성이 커질 수 있으며, 적절한 동기화 처리가 필요함.
    • 가장 간단한 싱글턴 보장은 Enum.

링크

[OOP] 표준 vs Custom Exception - 3

내용

  • domain 객체에서 발생하는 예외들을 표준 예외로 사용할지, 커스텀 예외를 사용할지 고민.
  • IllegalArgumentException이라는 표준 예외 네임으로도 충분히 메시지 전달이 가능하다고 판단.

링크

[OOP] 상속보단 컴포지션을 - 5

내용

  • LottoTicket을 상속하는 WinningLottoTicket을 구현하는데 다음과 같은 문제점이 존재.
    • 기존 LottoTicket의 생성자가 private이라 상속 불가하여 상속을 위해 어쩔 수 없이 기존 생성자를 public 변환.
    • LottoTicket의 생성 유효성 검증 로직이 정적 팩토리 메서드에 위치하고 있음.
    • 자식 클래스에서도 별도로 유효성 검증 로직을 추가 작성하는 코드 중복이 발생.
  • Effective Java 참고하였으며, 상속보단 조합을 활용하여 조금 더 유연성 있는 코드를 작성할 수 있었음.
    • WinnigTicket이 인스턴스로 보너스 번호와 로또 티켓을 가짐.

링크

[JDK] EnumMap - 2

내용

  • groupingBy()로 요소 수집시, MapFactory로 EnumMap을 사용함.
  • 열거 타입을 키로 사용하도록 설계된 Map 구현체인데, 조사해본 결과 HashMap보다 성능이 좋다고 한다.
    • Hash는 해싱 작업이 필요하지만 enum은 단일 객체임을 보장되어 작업이 필요 없기 때문이다.
  • TreeMap의 성격도 가지고 있다.
    • Enum에 명시된 순서를 기억하고 있다.

링크

[TDD] 테스트 주도 개발 - 3

내용

  • 초반에 적당히 설계하고 개발에 착수하다 보니, 중간 중간 변경사항이 많아져서 TDD에 어려움을 겪었음.
  • 기존 메서드들이 변경됨에 따라 기존에 작성했던 테스트 또한 깨짐.
  • 초반에 완벽한 설계는 불가능하다고 생각되는데, 이런 경우 TDD를 어떻게 적용해야 할지 페어와 어려움을 겪었음.
  • 미션 수행 전에 페어와 적당히 시간을 가지고 설계를 먼저 선행할 필요를 느낌.

[MVC] DTO - 3

내용

  • 지난 자동차 경주 게임을 수행하면서 DTO 사용의 장단점, DTO 변환 위치 및 사용 범위 등을 학습함.
  • 이번 프로젝트에서도 DTO를 사용하려고 했으나, 프로젝트 규모 등을 고려했을 때 굳이 필요하지 않다고 생각함.
  • 코드가 다소 복잡해지더라도, 도메인 모델을 외부로 노출시키지 않고 싶은 경우 사용하면 좋을 듯 하다.

링크

Copy link

@hyunssooo hyunssooo left a comment

Choose a reason for hiding this comment

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

안녕하세요 케빈! 지노입니다!
우선 리뷰가 늦어져서 죄송해요😭
빠르게 구현 잘 해주셨어요! 👍
몇 가지 코멘트 남겼습니다!
읽어보시고 궁금하시거나 다르게 생각하는 부분이 있다면 함께 이야기 나눠보면 좋을 거 같아요 :)
언제든 편하게 dm 주세요! 감사합니다!

Comment on lines +19 to +21
public static LottoNumber from(int number) {
return CACHE.computeIfAbsent(number, LottoNumber::new);
}

Choose a reason for hiding this comment

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

👍

Comment on lines 5 to 7
public interface LottoNumberGenerator {
public List<Integer> generate();
}

Choose a reason for hiding this comment

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

인터페이스의 메서드는 접근제어자가 없어도 public 입니다!

Comment on lines 25 to 31
public LottoStatistics getStatistics(WinningLottoTicket winningLottoTicket) {
Map<LottoRank, Long> statistics = lottoTickets.stream()
.map(winningLottoTicket::compareNumbers)
.collect(Collectors.groupingBy(lottoRank -> lottoRank, () -> new EnumMap<>(LottoRank.class),
Collectors.counting()));
return new LottoStatistics(statistics);
}

Choose a reason for hiding this comment

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

EnumMap 👍

Comment on lines 19 to 23
public double calculateYield(PurchasingPrice purchasingPrice) {
long prizeMoneyTotal = calculatePrizeMoneyTotal();
int denominatorCost = purchasingPrice.getPrice();
return (((double) prizeMoneyTotal) / denominatorCost);
}

Choose a reason for hiding this comment

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

뷰에서 사용되는 로직이네요. 뷰에서 도메인 객체에게 메세지를 보내는 것에 대해서 고민해보면 좋을 거 같아요 :)
뷰가 웹으로 바뀐다면 어떻게 될까요?

Comment on lines 17 to 21
private LottoRank(int matchCounts, boolean isBonusBall, int prizeMoney) {
this.matchCounts = matchCounts;
this.isBonusBall = isBonusBall;
this.prizeMoney = prizeMoney;
}

Choose a reason for hiding this comment

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

enum은 생성자를 디폴트로 사용합니다! private일 필요는 없어요!

Suggested change
private LottoRank(int matchCounts, boolean isBonusBall, int prizeMoney) {
this.matchCounts = matchCounts;
this.isBonusBall = isBonusBall;
this.prizeMoney = prizeMoney;
}
LottoRank(int matchCounts, boolean isBonusBall, int prizeMoney) {
this.matchCounts = matchCounts;
this.isBonusBall = isBonusBall;
this.prizeMoney = prizeMoney;
}

Comment on lines 15 to 17
public long getTicketCountsByRank(LottoRank lottoRank) {
return statisticsMap.computeIfAbsent(lottoRank, key -> ZERO);
}

Choose a reason for hiding this comment

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

이 메서드도 마찬가지로 뷰에서 사용하는 메서드네요!

Comment on lines +34 to +41
@DisplayName("로또 번호는 싱글톤 객체이다")
@Test
void isSingleton() {
LottoNumber lottoNumber = LottoNumber.from(1);
LottoNumber target = LottoNumber.from(1);

assertThat(lottoNumber).isEqualTo(target);
}

Choose a reason for hiding this comment

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

LottoNumber는 value object로 만들 수도 있겠네요!
https://woowacourse.github.io/javable/post/2020-06-11-value-object/

Comment on lines 12 to 13
private final List<LottoNumber> lottoNumbers;

Choose a reason for hiding this comment

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

중복을 허용하지 않는 자료구조는 없을까요??

Copy link
Author

Choose a reason for hiding this comment

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

더 쉬운 방법이 있었네요.... 😅😅

Comment on lines +40 to +42
public boolean contains(LottoNumber lottoNumber) {
return lottoNumbers.contains(lottoNumber);
}

Choose a reason for hiding this comment

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

LottoNumber 객체에게 같은 숫자인지 물어보는 방법은 없을까요??

Copy link
Author

@xlffm3 xlffm3 Feb 20, 2021

Choose a reason for hiding this comment

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

public boolean contains(LottoNumber lottoNumber) {
      return lottoNumbers.stream()
                          .anyMatch(lottoNumberOfTicket -> lottoNumberOfTicket.hasSameNumber(lottoNumber);
}

같은 형태의 코드를 말씀하시는 것일까요? 해당 부분의 피드백이 잘 이해가 되지 않네요!

Choose a reason for hiding this comment

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

public class LottoNumber {
  ..
  public boolean match(LottoNumber LottoNumber) {
    return this.value == value;
  }
}

이런 형태로 LottoNumber에게 메세지를 보낼 수도 있을거 같아서 남긴 피드백이였습니다!

Comment on lines 34 to 38
public int getMatchCounts(LottoTicket lottoTicket) {
return (int) lottoNumbers.stream()
.filter(lottoTicket::contains)
.count();
}

Choose a reason for hiding this comment

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

get보다 더 좋은 이름은 없을까요??

- 열거형 생성자는 기본 default를 사용한다.
- 인터페이스 추상 메서드의 접근 제어자의 public은 생략한다.
- getStatistics 등의 메서드 명 또한 checkResult로 직관되게 변경
- 구입가격 객체는 양의 정수만을 허용하도록 변경한다.
도메인 객체로 메시지를 보내던 기존 방식을
외부에서 주입받은 수익률을 바로 출력하도록 변경한다.
LottoTicket은 Set이기 때문에 정렬을 View단으로 넘긴다
@xlffm3
Copy link
Author

xlffm3 commented Feb 20, 2021

안녕하세요.

피드백을 반영하는 과정에서 코드의 구조가 다소 많이 변경되었네요.

  • 사용자가 입력하는 금액에 해당하는 PurchasingPrice 클래스는 더이상 로또 1장의 가격을 알지 못하도록 변경. ce4b8e1
  • 로또 티켓 가격은 관련 클래스(LottoTickets)에서 관리하도록 변경. 9b457ca

중복을 허용하지 않는 자료구조 피드백에 대해서 Set을 적용하도록 코드를 변경했습니다. 그리고 지적해주신 메서드 네이밍에 대해 고민해보면서, 기타 직관적이지 않은 클래스 및 메서드 네임들에 대해 전부 리팩토링해보았습니다. (예 : LottoStatistics -> LottoResult 등)

또한 뷰에서 도메인 객체에게 메시지를 보내는 것에 고민해보았는데요. 말씀하신대로 요구사항이 웹으로 바뀌면... 기껏 도메인에서 뷰에 필요한 정보들(DTO, JSON 등)을 만들어 보내줬는데 뷰가 도메인쪽으로 추가 정보를 달라고 다시 요청하는(?) 이상한 형태가 되네요. View에서 도메인 객체에게 메시지를 보내 그 때 그때 필요한 정보를 받기보다는, 도메인 객체가 제공하는 DTO 성격의 데이터를 파싱하여 View를 보여주는 방식으로 변경해보았습니다!

Copy link

@hyunssooo hyunssooo left a comment

Choose a reason for hiding this comment

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

안녕하세요 케빈!
주말까지 step1 진행하신다고 수고 많으셨어요! 👍
step2에서 만나요! 감사합니다!

Comment on lines +44 to +51
public int getPurchasingPrice() {
return lottoTickets.size() * LOTTO_TICKET_PRICE;
}

public int getTicketCounts() {
return lottoTickets.size();
}

Choose a reason for hiding this comment

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

get 보다 더 좋은 이름은 없을까요??

Comment on lines +44 to +46
public List<LottoNumber> getLottoNumbers() {
return Collections.unmodifiableList(new ArrayList<>(lottoNumbers));
}

Choose a reason for hiding this comment

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

getter 메서드가 내부와 다른 자료구조를 던져주네요!

Suggested change
public List<LottoNumber> getLottoNumbers() {
return Collections.unmodifiableList(new ArrayList<>(lottoNumbers));
}
public Set<LottoNumber> getLottoNumbers() {
return Collections.unmodifiableSet(lottoNumbers);
}

@hyunssooo hyunssooo merged commit 918f6f8 into woowacourse:xlffm3 Feb 21, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants