CODE SQUAD/FeedBack 정리

[Review] 로또 3단계 - 수동구매 기능 추가 (2022/02/25)

샤아이인 2022. 2. 25.

 

코드 리뷰

이번주는 호눅스가 리뷰를 해주셨다!

 

1. 일급 컬렉션의 데이터 출력하기

1급 컬렉션의 데이터는 어디서 출력해야 할까? 이에 대한 의문으로 부터 시작한 질문이였다.

 

 

호눅스 께서는 선택의 문제라 알려주셨다. 따라서 우리팀은 1급 객체의 데이터 불변성을 보장하기 위해 필드의 list인 lottoNumbers를 반환하는 getter를 만들지 않기로 하였다.

 

대신 LottoTicket에게 showLottoNumbers() 메시지를 수신할 수 있도록 만들었다.

 

해당 Class의 코드는 다음과 같다.

public class LottoTicket {
    private static final int NORMAL_TICKET_SIZE = 6;
    private static final String LOTTO_NUMBER_ERROR_MESSAGE = "로또 숫자는 6개가 필요합니다.";
    private final ArrayList<LottoNumber> lottoNumbers;

    public LottoTicket(ArrayList<LottoNumber> lottoNumbers) {
        if (!isValidTicketSize(lottoNumbers)) {
            throw new IllegalArgumentException(LOTTO_NUMBER_ERROR_MESSAGE);
        }
        this.lottoNumbers = lottoNumbers;
        Collections.sort(this.lottoNumbers);
    }
    
    private boolean isValidTicketSize(ArrayList<LottoNumber> lottoNumbers) {
        return lottoNumbers.size() == NORMAL_TICKET_SIZE;
    }
    
    public void showLottoNumbers() {System.out.println(lottoNumbers);}

    public int size() {return lottoNumbers.size();}

    public int countWinningNumber(WinningNumbers winningNumbers) {
        int count = 0;
        for (LottoNumber lottoNumber : lottoNumbers) {
            count += checkWinningNumber(winningNumbers, lottoNumber);
        }
        return count;
    }

    // 생략...
}

showLottoNumbers() 메서드를 통해 1급 객체 스스로 자신의 정보를 표현하도록 하였다.

 

외부로 list인 lottoNumbers 를 직접 반환하는 것 이 아니라, 배열의 복사본을 반환하도록 만든 후, 이를 OuputView 까지 전달했으면 됬을까?

 

lottoNumber는 ArrayList<LottoNumber> 이기 때문에 해당 컬렉션을 숫자 배열의 복사본으로 변경하기가 까다롭다.

숫자배열로 만드려면 일단 LottoNumber에서 값을 꺼내와야 하는데, getter를 만들어 주기가 싫었다...

 

2. User의 Seller에 대한 의존성 없에기

 

변경 전

기존의 코드에서 user는 seller에 대한 정보를 생성될 당시에 받게된다.

Person testUser = new Person("testUser", userMoney, seller);

Person 내부에서는 다음과 같이 사용되고 있다.

public void buyRandomLottoTicket(int money) {
    isValidInputMoney(money);
    myLottoTicketList = lottoTicketSeller.exchangeTicket(money);
    this.money -= money;
}

티켓을 구매할 때, seller에게 돈을 전달하면 반환값으로 TicketList를 받게된다.

 

요약해보면, Controller -> InputView -> Person -> Seller 의 흐름으로 money가 전달되고 있다.

 

변경 후

Person에게 Seller에 대한 의존성을 없에기 위해 다음과 같이 컨트롤러에서 값을 전달받게 된다.

public void run() {
    Person customer = new Person();
    int userMoney = InputView.requestMoney();
    int customTicketCount = InputView.requestCustomTicketCount();

    validateEnoughMoney(userMoney, customTicketCount);

    buyManualLottery(customer, customTicketCount);
    buyRandomLottoTicket(customer, discharge(userMoney, customTicketCount));

    showLottoInfo(customer);

    showResult(makeResult(customer));
    InputView.close();
}
private void buyRandomLottoTicket(Person customer, int money) {
    LottoTicketSeller lottoTicketSeller = new LottoTicketSeller(new RandomTicketFactory());

    int ticketCount = money / TICKET_PRICE;

    for (int i = 0; i < ticketCount; i++) {
        customer.buyRandomLottoTicket(lottoTicketSeller.exchangeTicket(new ArrayList<>()));
    }
}

Person은 더이상 Seller에게 의존하면서 돈을 보내지 않는다.

Controller -> InputView -> Person -> Controller -> Seller의 흐름으로 변경되었다.

 

3. Seller에 전략패턴 적용하기 (구성 활용)

우선 티켓을 생성하는 Factory는 다음과 같다.

이를 Seller는 is-a 관계로, Seller가 가지고 있게 된다. Seller의 코드는 다음과 같다.

public class LottoTicketSeller {
    private TicketFactory ticketFactory;

    public LottoTicketSeller(TicketFactory ticketFactory) {
        this.ticketFactory = ticketFactory;
    }

    public LottoTicket exchangeTicket(ArrayList<LottoNumber> numbers) {
        return ticketFactory.generateTicket(numbers);
    }
}

Seller는 생성될 때 Dependency Injection(의존성 주입)을 통해 알맞은 Factory를 전달받게 된다.

 

LottoTicketSeller는 다음과 같이 생성될 때 TicketFactory를 전달받게 된다.

LottoTicketSeller lottoTicketSeller = new LottoTicketSeller(new CustomTicketFactory());
LottoTicketSeller lottoTicketSeller = new LottoTicketSeller(new RandomTicketFactory());

 

댓글