Skip to content

Conversation

@pageprologue
Copy link

@jjj0611 jjj0611 self-requested a review July 17, 2021 11:29
Copy link

@jjj0611 jjj0611 left a comment

Choose a reason for hiding this comment

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

희진 선생님 안녕하세요 2단계 구현해주신 내용 잘 봤습니다.
생각보다 코드리뷰가 많이 늦어져서 죄송합니다 😢

간단하게 고민해볼만한 부분들 코멘트 남겼으니 확인 부탁드려요! 🙏

public class LottoController {
public void run() {

private static final LottoTicketVendingMachine lottoTicketVendingMachine = new LottoTicketVendingMachine();
Copy link

Choose a reason for hiding this comment

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

클래스 내부에서 new로 선언하며 의존하는 것과 주입받아서 사용하는 것에 대해서 고민해보면 좋을 것 같아요.
벤딩머신은 변경점이 그렇게 많지는 않겠지만, 주입해주는 방향에 대해 고민해보면 좋을 것 같아요.

Copy link
Author

Choose a reason for hiding this comment

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

넵~! LottoService 객체를 새로 만들었고, 여기에서 LottoTicketVendingMachine을 생성자로 주입받도록 변경하였습니다.

public static void main(String[] args) {
LottoController lottoController = new LottoController();
lottoController.run();
LottoController.run();
Copy link

Choose a reason for hiding this comment

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

LottoController에서 run하는 것도 instance 생성에서 static으로 변경한 이유가 있을까요?
이전 리뷰에서 제가 질문을 드린건 무조건 변경하라는 것이 아니라 어떤 차이가 있을까에 대해 고민해보시면 좋을 것 같다는 부분이었어요!
저는 둘 다 괜찮다고 생각하는데 '왜 그렇게 선택했는가'에 대한 기준은 중요하다고 생각합니다!

Copy link
Author

@pageprologue pageprologue Aug 3, 2021

Choose a reason for hiding this comment

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

이 부분에 대해서 static 키워드와 정적 팩터리 메서드 부분을 다시 살펴보았습니다.



static 영역에 할당된 메모리는 모든 객체가 공유하고, 프로그램의 종료시까지 메모리가 할당된 채로 존재하게 됩니다. 모든 객체가 공유한다는 점에서 사이드 이펙트 영향이 커 질 수 있습니다. 또, 프로그램이 종료되기 전에 항상 메모리에 상주하고 있어서 자주 사용하지 않는 메서드가 누적된다면 GC에 수거되지 못하므로 메모리 낭비가 발생할 수 있습니다.







인스턴스 생성과 비교하여 정적 팩터리 메서드의 장점에 대해 정리해 보았습니다.



1. 생성자는 의미있는 명명을 하기 어렵지만, 정적 팩토리 메소드는 의미있는 명명이 가능하다.



2. 정적 팩토리 메소드는 메소드 리턴 타입 범위 내에서 다양한 리턴 타입의 정의가 가능하다.



3. 정적 팩토리 메소드는 인스턴스를 생성하기 위한 로직을 캡슐화 할 수 있다.









여기에서는 LottoController 에서 메소드를 작게 분리하고, 의미 있는 명명을 하기 위해 인스턴스를 생성하여 메서드를 호출하는 방식으로 변경하였습니다~!


public class LottoGenerator {

private static final List<LottoNumber> lottoNumberContainer = create();
private static final List<LottoNumber> LOTTO_NUMBER_CONTAINER = create();
Copy link

Choose a reason for hiding this comment

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

LottoNumber에 대한 cache가 이곳에 위치해야 하는지 고민해보면 좋을 것 같아요.
지금 LottoGenerator를 통해서 Lotto를 생성하고 있는데, 코드 곳곳에 보면 new LottoNumber()로 생성하는 곳도 있고,
LottoGenerator를 통해서 caching된 객체를 받는 곳도 있어요.
이 부분에 대한 일관성(모두 new 를하여 사용할지, 어짜피 lottoNumber는 1~45만 필요하니 어디서든 caching된 것을 사용할지)을 유지하면 좋지 않을까 싶습니다!

Copy link
Author

@pageprologue pageprologue Aug 3, 2021

Choose a reason for hiding this comment

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

LottoGenerator 의 역할을 LottoTicketVendingMachine 로 옮겼습니다~!
 그리고 로또 번호 인스턴스를 생성하는 부분은 로또 티켓이 자동 생성인지, 수동 생성인지에 따라 차이가 생기게 되었습니다.

  • 자동 생성에서는 LottoGenerator를 통해 전체 로또 번호를 미리 생성하여 중복되지 않은 로또 번호를 반환해 주고 있습니다.
  • 수동 생성인 경우에는 수동 번호를 입력 받기 때문에 유효성 검증과 일관된 자료형을 사용할 목적으로 LottoNumber로 생성하고 있습니다.


Copy link

Choose a reason for hiding this comment

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

수동 생성의 경우도 고민이 되는 부분이 있네요.
중복되지 않은 번호여야 한다는 것은 로또 티켓이 가져야 하는 role이지 않을까요?
수동으로 생성한다하더라도 티켓을 만들 때 유효성 검증에서 걸릴텐데
한 애플리케이션 안에서 어떤 부분은 new를 통해 새롭게 생성하고, 어떤 부분은 caching을 사용하는 구조보다는
일관성을 가져가는게 어떨까 싶네요 :)

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 23 to 24
Collections.shuffle(LOTTO_NUMBER_CONTAINER);
return LOTTO_NUMBER_CONTAINER.subList(0, 6).stream()
Copy link

Choose a reason for hiding this comment

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

0과 6이 의미하는 바가 무엇일까요?

Comment on lines 16 to 18
public LottoNumber(String lottoNumber) {
this(Integer.parseInt(lottoNumber));
}
Copy link

Choose a reason for hiding this comment

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

Integer.parseInt(lottoNumber)를 했는데 lottoNumber가 숫자가 아닌 문자열이면 어떤 일이 벌어질까요?
무엇이 잘못되었는지 사용자가 잘 알 수 있게 하려면 어떻게 하는 것이 좋을까요?

Copy link
Author

@pageprologue pageprologue Aug 3, 2021

Choose a reason for hiding this comment

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

LOTTO_NUMBER_CONTAINER 에서 6개의 볼을 꺼내기 위해 ArrayList.subList 를 사용하였습니다. 파라미터로 받는 fromIndex와 toIndex를 따로 상수화 하지 않았었는데, 6이라는 숫자는 LOTTO_NUMBER_SIZE 상수로 대체 하였습니다. 0도 상수로 빼는 것이 좋을까요?

또한 ArrayList.subList 사용시 메모리 누수 위험성이 있다고 하여 수정하였습니다.
(
ArrayList.subList 주의할 점)

Copy link

Choose a reason for hiding this comment

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

엇 제가 남긴 코멘트는 IntegerParseInt에 대해서 valid하지 않은 String이 들어오면 어떻게 되는지에 대한 물음이었는데,
혹시 그에 대한 답변이 맞을까요??

Copy link
Author

Choose a reason for hiding this comment

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

아 이 부분 바로 위에 남긴 코멘트였는데 잘못 달았네요..ㅎ
IntegerParseInt 하는 부분 처리 했습니다.

Comment on lines +25 to +32
if (buyingPrice < TICKET_PRICE) {
throw new IllegalArgumentException("구입 금액은 " + TICKET_PRICE + "원 이상이여야 합니다.");
}
}

private void validateFactor(int buyingPrice) {
if ((buyingPrice % LottoTicketVendingMachine.TICKET_PRICE) != 0) {
throw new IllegalArgumentException("구입 금액은 1,000원 단위여야 합니다.");
private void validateDivisible(int buyingPrice) {
if ((buyingPrice % TICKET_PRICE) != 0) {
throw new IllegalArgumentException("구입 금액은 " + TICKET_PRICE + "원 단위여야 합니다.");
Copy link

Choose a reason for hiding this comment

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

BuyingPrice가 여전히 TICKET_PRICE가 있는 TicketAmount에 의존하고 있고,
TicketAmount도 BuyingPrice를 받는 메서드가 존재해서 양방향 의존이 되고 있네요.
예전 리뷰에서도 말씀드렸다시피 '구매금액'이라는 용어가 가지는 방향에서 저는 티켓 금액인 1,000원에 대한 고민이 자꾸 드는 것 같아요.
정의한 용어와 그 용어의 역할에 대해 같이 고민해보면 좋을 것 같아요.
더불어 양방향 의존에 대한 고민도 해보시면 좋을 것 같습니다!

Copy link
Author

Choose a reason for hiding this comment

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

이 부분은 TicketAmount 생성자에서 BuyingPrice 객체를 받지 않고 totalTicketAmount 를 받도록 수정하였습니다. TICKET_PRICE 상수도 BuyingPrice 로 옮겼습니다. 양방향 의존에 대해서는 더 학습해 보도록 하겠습니다~!


public class LottoTicketVendingMachine {

private static final LottoGenerator LOTTO_GENERATOR = new LottoGenerator();
Copy link

Choose a reason for hiding this comment

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

여기서도 static하게 필드 선언을 하는 것과 주입받아 사용하는 것은 어떤 차이가 있을까요?

사실 로또 게임에서 LottoGenerator도 크게 변하지 않을 도메인이긴 하지만 주입 받을 때와 필드로 선언했을 때의 차이에 대해서는 고민해보시면 좋을 것 같아요

Copy link
Author

@pageprologue pageprologue Aug 3, 2021

Choose a reason for hiding this comment

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

넵! 이번 미션에서 static 키워드에 대해 많이 생각해 보게 되네요 :)
 생성자에서 로또 번호를 generate 해주고, shuffle 해주도록 수정하였습니다.


@@ -0,0 +1,36 @@
package lotto.domain.vending;

public class TicketAmount {
Copy link

Choose a reason for hiding this comment

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

위에 BuyingPrice에서도 말씀드렸지만, BuyingPrice와 TicketAmount의 관계에 대해 고민해보시면 좋을 것 같아요!


import java.util.*;

public class WinningLottoRank {
Copy link

Choose a reason for hiding this comment

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

WinningLottoRank가 하고 있는 일이 너무 많은 것 같아요.
처음에 LottoTikcet들과 당첨 번호를 받아서 랭크가 생성이 되고,
lottoTicket 한개를 받아서 일치하는 숫자가 몇개인지도 확인할 수 있고(이건 WinningLottoRank가 아닌 WinningNumbers의 역할이죠)
LottoRank의 Key들을 반환하는 역할도 하고 있네요.

이 WinningLottoRank를 만든 목적은 어떤거였을까요?

Copy link
Author

@pageprologue pageprologue Aug 3, 2021

Choose a reason for hiding this comment

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

WinningLottoRank는 당첨 번호와 일치하는 개수에 따른 순위를 계산해 주는 역할을 하기 위해서 만들었습니다. 로또 티켓에서 당첨번호와 일치하는 개수를 반환하는 메서드는 WinningNumbers 로 옮겼습니다!

private static final String BONUS_NUMBER_MESSAGE = "보너스 볼을 입력해 주세요.";
private static final String DELIMITER = ",";

private InputView() {
Copy link

Choose a reason for hiding this comment

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

static으로 사용하는 것들 중 이것은 생성자를 막아두고, 어떤 것들은 생성자를 열어두었네요!

Copy link
Author

Choose a reason for hiding this comment

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

넵 수정했습니다 :)

@jjj0611 jjj0611 self-requested a review August 10, 2021 11:06
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.

2 participants