우아한 프리코스가 끝난지 꽤 지난 상황에서 3주차 리뷰를 하고 있는게 조금 웃기긴 하지만 그 동안 코딩 테스트가 3번이나 있었기에 조금 바빠서 늦게 되었습니다..
사용자로부터 로또를 구매하기 위한 금액, 당첨 번호, 보너스 번호를 입력 받은 뒤, 결과를 출력하는 프로그램이었습니다.
3주차 미션에서 고려했던 사항은 다음과 같습니다.
https://github.com/chrkb1569/java-lotto-6/tree/chrkb1569
3주차 미션에서는 피드백을 받지 못하였습니다.
그래서 다른 분들 리뷰를 남기면서 배웠던 점, 다른 분들과 비교하였을 때, 제 코드에서 아쉬웠던 점을 위주로 서술하려고합니다.
3주차에는 최대한 2주차 피드백 내용을 코드에 담을 수 있도록 노력하였습니다.
ublic class GameConfiguration {
public GameController gameController() {
return new GameController(outputView(), inputView(), gameService());
}
private OutputView outputView() {
return new ConsoleOutputView();
}
private InputView inputView() {
return new InputView(input(), inputValueChecker());
}
private Input input() {
return new ConsoleInput();
}
private InputValueChecker inputValueChecker() {
return new InputValueChecker();
}
private GameService gameService() {
return new GameService(numberFactory());
}
private NumberFactory numberFactory() {
return new LottoNumberFactory();
}
}
다음처럼 GameConfiguration 이라는 클래스를 통하여 의존 관계인 클래스들을 주입할 수 있었으며,
다음처럼 main 함수를 깔끔하게 정리할 수 있었습니다.
다음은 게임 정보를 저장하는 Game 클래스입니다.
Game 클래스 내부에는 로또를 통한 수익을 관리하기 위한 Profit 클래스, 사용자가 구매한 로또와 당첨 여부를 관리하기 위한 Player 클래스를 선언하였으며, Game 클래스는 필드를 참조하여 결과만 반환하도록 구현하였습니다.
기존에는 Service에서 결과 문자열을 생성한 뒤, 이를 Controller에서 출력하도록 만들었다면, 3주차에서는 View를 통하여 값의 입력과 출력이 이루어지도록 설계하였습니다.
게임 과정에서 자주 사용되는 상수는 하드 코딩하는 방식 대신 별도의 열거형 클래스를 생성하여 변수의 형태로 활용하도록 설계하였습니다.
다른 분들 코드를 리뷰하면서 배웠던 점, 프리코스를 진행하면서 배웠던 점들을 위주로 기술하였습니다.
3주차 미션에서는 1, 2주차 미션과 다르게 사용자가 유효하지 않은 값을 입력한 경우에는 오류 메세지를 출력하며 다시 입력받으라는 요구 사항이 있었습니다.
이 부분을 어떻게 구현할까 공부해보니, 2가지 방법이 존재하였습니다.
1) while + try catch
2) 재귀
일단 가장 먼저 2번을 사용하는 경우에는 구현하기가 쉽고, 가독성이 뛰어나며 프로그램 요구 사항에 해당하는 indent 제한 사항을 위배하지 않는다는 장점이 존재하지만, 재귀라는 특성상 너무 많이 호출되는 경우에는 StackOverFlow가 발생할 수 있다는 문제점을 안고 있었습니다.
그러나 1번의 경우에는 indent 제약을 위반하는 문제점이 있었습니다.
이 부분을 어떻게 구현할지 고민도 많이 했고, 다른 사람들의 코드들도 많이 찾아보았으나, 마땅한 답을 찾지 못했습니다.
그래서 제가 생각한 방식은 바로 재귀였습니다.
다음은 사용자로부터 로또 구매 금액을 입력받는 코드입니다.
사용자로부터 값을 입력받은 뒤, 유효성 검사를 진행합니다.
유효하지 않은 값을 입력받았을 경우에는 NotValidInputException을 발생시키면서 오류 메세지와 함께 다시 값을 입력받습니다.
여기서 depth를 통하여 사용자가 일정 횟수 이상 유효하지 않은 값을 입력하는 경우, 프로그램이 종료되도록하여 StackOverFlow가 발생할 수 있다는 문제를 해결하였습니다.
사실 프로그램을 종료시킨다는 요구 사항이 없었기에 이 방식이 맞는지 많이 고민했었지만, 저에게는 이게 최선이었습니다.
추후에 다른 분들의 코드를 보니 while문을 활용하신 분들이 많았으나, 저는 그렇게 구현못할 것 같습니다...ㅠ 잘하시는 분들이 엄청 많으시더라구요..
다음은 사용자의 로또 구매 내역과 결과 정보를 저장하기 위한 Player 클래스입니다.
Player 클래스는 Game 클래스 내부에 존재하는 일급 컬렉션으로 설계하였고, 다음처럼 필요한 method들을 구현하였습니다.
초기 설계 목적은 동일한 패키지에 위치한 Game 클래스만이 Player 클래스의 method를 사용할 수 있도록 protected 접근자를 사용하였습니다.
다른 분들의 코드를 리뷰하면서 protected 접근자를 사용하는 것에 대해서 질문했었는데, 자식 클래스가 생성될 가능성으로 인하여 제 생각이 잘못되었음을 깨닫게 되었습니다.
다음은 로또 등수와 상금 정보를 저장하는 Enum 클래스입니다.
저는 Enum 클래스 내부에 등수를 통하여 상금을 반환하는 getPrize() method를 생성하였습니다.
처음 다른 분들의 코드를 리뷰했을때, Enum 클래스를 단순 값 조회 목적으로 사용하시는 분들도 있었기에, Enum 클래스 내부에 로직을 넣는 것은 본래 목적과 다르게 사용하고 있는 것이 아닌가라는 생각이 들었습니다.
그래서 저처럼 Enum 클래스 내부에 로직을 넣으신 분께 질문해봤는데, Enum 클래스가 저장하고 있는 정보에서 너무 많이 벗어난 역할만 아니면 충분히 고려해서 사용하면 괜찮을 것 같다는 답변을 받았습니다.
이 부분은 항상 부족한 것 같습니다.
3주차 미션의 경우에는 일급 컬렉션 활용을 목적으로 프로그램을 설계하였는데, 만들다보니 프로그램이 특정 객체에 의존하고 있는 형태가 되었습니다.
Service 로직도 Game에 의존하고 있으며,
Controller 로직 역시 Game에 의존하고 있다는 것을 확인할 수 있었습니다.
아직 이 부분이 미숙하다는 점을 보여주는 코드인 것 같습니다..