[우아한테크코스 6기] 프리코스 3주차 회고

Daon (HyeongIk Jo)·2023년 11월 15일
0

우테코 6기

목록 보기
3/7
post-thumbnail

❗우아한프리코스 3주차 미션 종료

3주차 미션이 종료되었다.
회고에 앞서 지난 2주차 때 세웠던 Try와 코드리뷰를 먼저 확인해보자!

지난 Try

  • 구현부터 리팩토링은 나중에!
  • 이번엔 객체지향적 설계에 맞춰보자!
  • 코드리뷰 스터디를 통해 구현의도를 설명하자
  • README.md를 통해 프로젝트 소개를 야무지게 해보자

코드리뷰 종합

  • 서비스 레이어의 역할을 고려하며 설계하기 by @Heon0208
  • 정적 팩토리 메서드를 무작정 도입은 말자 by @wns312

👍🏻좋았던 점

🗨️ 스토리텔링식 설계를 적용해보았다.

미션 초기 프리코스 커뮤니티 함께-나누기에 올라온 게시글 하나

바로 스토리텔링식으로 이야기를 짜보고 협력/책임/역할로 나눠보는 것이었다. Thanks to 작성자님🙏🏻
이런걸 그냥 넘어갈 내가 아니지. 바로 실천해보았다.

1️⃣ 이야기 : 로또를 구매하러 옴
- 게임장엔 구매자, 로또 매니저, 진행자, 게임 매니저, 회계사가 있었음
- 진행자가 "로또를 구매하라"함
  - 구매자가 “이 금액만큼 자동이요”라고 외침
  - 구매자가 정렬된 로또 번호들을 소유함
- 진행자가 "당첨번호랑 보너스 번호 적어놔" 함
  - 게임 매니저가 6개 당첨번호와 1개 보너스번호를 적어둠
- 진행자가 로또 매니저에게 "로또번호들 말하라" 외침
- 진행자가 "1등부터 5등별로 총 몇 개 됐는지 확인해봐" 함
  - 심판이 결과를 말함
- 진행자가 "수익률을 말하라" 외침
  - 회계사가 수익률을 계산함

2️⃣ 협력 : 요청과 응답
- 누군가가 진행자에게 로또 게임을 요청함으로써 게임이 시작됨
- 진행자는 구매자에게 로또번호들을 구매할 것을 요청함
  - 구매자는 금액만큼의 자동으로 생성되는 로또번호들을 요청함
  - 로또 매니저는 정렬된 6자리 로또 번호들을 응답함
- 진행자가 게임 매니저에게 6자리 당첨번호와 1개의 보너스 번호를 요청함
  - 게임 매니저는 6자리 당첨번호와 1개의 보너스번호를 응답함
- 진행자가 서기에게 로또번호들 목록들을 적어라 요청함
  - 서기가 로또 매니저에게 로또번호들 요청함
  	- 로또 매니저가 로또번호들을 응답함
  - 서기가 로또번호들 목록을 응답함
- 진행자가 게임 매니저에게 1등부터 5등별 게임 결과를 요청함
  - 게임 매니저가 로또 매니저에게 번호들을 요청함
  - 로또 매니저가 로또번호들을 응답함
  - 게임 매니저가 게임 결과를 응답함
- 진행자가 회계사에게 수익률을 요청함
  - 회계사가 로또 구매자에게 초기 자본과 게임 매니저에게 게임 결과를 요청함
  - 로또 구매자가 초기 자본을 응답함
  - 게임 매니저가 게임 결과를 응답함
  - 회계사가 수익률 계산 결과를 응답함
3️⃣ 책임 : 하는 것과 아는 것
- 진행자
	- 하는 것 
    - 구매자에게 로또 구매를 요청, 구매자에게 로또 번호 목록들을 요청
    - 게임 매니저에게 게임 결과를 답하도록 요청, 회계사에게 총 수익률을 요청 
   
- 구매자
  - 하는 것 : 로또 번호 목록을 요청
  - 아는 것 : 초기 금액

로또 매니저
  - 하는 것: 로또 번호 목록을 생성
  - 아는 것:
    - 로또 번호 최대 최소
    - 로또 규칙
  
게임 매니저
  - 하는 것 : 게임 결과 산출
  - 아는 것 : 당첨번호와 보너스 번호
 
회계사
  - 하는 것: 총 수익률 계산
  - 아는 것: 계산 방법

마지막 역할은 고민을 해보았지만 해결되지 않았다.
완벽하진 않았지만 이 글을 토대로 설계를 시작하니 상당히 체계적으로 구현이 되었다.


🗨️ 정규표현식을 상수로 정의하였다.

.matches() 로직을 사용할때 Pattern 객체를 캐싱해야 성능 이슈를 줄일 수 있는 것을 알게 되었다. - Thanks to 현지님
역시나 나는 뭐다? 스펀지가 되어보기로 했으니 바로 적용해본다.

public class InputView {
    private static final String REGEXP_ONLY_NUMBER = "^\\d*$";
    private static final Pattern PATTERN_ONLY_NUMBER = Pattern.compile(REGEXP_ONLY_NUMBER);
    //...//
}

실제 구현까지 해보니 왜 해야하는지가 더 머릿속에 남게 되는 기분이다.


📖아쉬운 부분

🔥void 메서드 테스트를 작성할 수 없었다.

public class LottoResult {
    private final Map<LottoRanking, Integer> lottoResults;

    private LottoResult(Map<LottoRanking, Integer> lottoResults) {
        this.lottoResults = lottoResults;
    }

    public static LottoResult createLottoResult(Map<LottoRanking, Integer> lottoResults) {
        return new LottoResult(lottoResults);
    }

    public void addLottoResult(LottoRanking lottoRanking) {
        if (Objects.nonNull(lottoRanking)) {
            lottoResults.put(lottoRanking, lottoResults.get(lottoRanking) + 1);
        }
    }

당첨된 로또를 저장하기 위해 Map을 사용했다.
로또 1장당 발생하는 당첨 결과를 추가하는데 void 메서드를 사용하게 되었다. 문제는 테스트에서 발생하였다.
반환값이 없으면 테스트를 진행할 수 없었다.


물론 mock을 사용하면 테스트는 돌아갔지만 이번 미션의 마음가짐과 위반되는 행동이었다.

이번 미션 마음가짐

  1. getter 사용을 지양하자.
  2. 테스트 작성시 mock을 지양하자.

결국 설계 오류로 남기며 미션이 종료되었다.

🤓IllegalStateException 테스트

public class LottoGameManager {
    private final LottoOwner lottoOwner;
    private final WinningLotto winningLotto;
    private boolean isEnd = false;

	//...//

    public void matchLottosWithWinningLotto() {
        if (isEnd) {
            throw new IllegalStateException(OWNER_ALREADY_MATCH_WITH_WINNING_LOTTO.toString());
        }
        isEnd = lottoOwner.matchLottosWithWinningLotto(winningLotto);
    }

    public WinningStatistics getWinningStatistics() {
        if (!isEnd) {
            throw new IllegalStateException(NOT_MATCHING_WITH_WINNING_LOTTO.toString());
        }
        return lottoOwner.getWinningStatistics();
    }
	//...//
}

로또 게임에서 발생하면 안되는 상태를 생각해보았다.
그걸 염두하여 위와 같은 로또게임관리자 클래스를 설계하였다.
그러나 나의 프로그램 설계상 isEnd는 무조건 정상 상태를 유지하였다.
이 또한 테스트를 하지 못하여 아쉬운 부분으로 남게 되었다.


📚코드리뷰 피드백 - (23.11.15 최신화)

1. Enum을 key로 갖는 Map은 EnumMap을 사용해 보자 - by @kimwanyoung

EnumMap은 Enum을 키로 사용하는 Map이다.

  • 탐색에 상수의 시간복잡도를 사용한다고 한다.
  • 좋은걸 알았으면 정리하자!

2. 메서드를 가독성있게 분리하자 (⚠️약혐주의)

 public void run() {
        LottoOwner lottoOwner = getLottoOwner();
        outputView.printLottosInfo(lottoOwner.getLottosInfo());
        Lotto winningNumbers = getWinningNumbersUntilValid();
        outputView.printSpace();
        WinningLotto winningLotto = getWinningLottoUntilValidBonusNumber(winningNumbers);
        LottoGameManager lottoGameManager = LottoGameManager.createGameManager(lottoOwner, winningLotto);
        lottoGameManager.matchLottosWithWinningLotto();
        printWinningStatistics(lottoGameManager);
        outputView.printRateOfReturn(lottoGameManager.getRateOfReturn());
}
  • 메서드 15줄 규칙을 안넘긴 것을 확인하고 넘어간 것이 실수였다.
  • 나에겐 일주일 내내 보던 코드였으니 이상함을 느끼지 못한거 같다.
  • 지금 보니 다 뜯어고치고 싶은 욕구가 솓구친다

🫡Keep And Try

Keep

대망의 4주차도 클린 코드를 적용해보려한다. 지난 Try 목록은 자연스레 keep으로 쌓여가게 된다.

Try

  • void 메서드 설계를 피하자
  • README.md를 통해 프로젝트 소개를 야무지게 해보자
  • 메서드를 가독성있게 분류하자!
  • 구현이 리팩토링보다 먼저다

참고 게시글

[공부한 것] String.matches는 왜 성능에 안 좋을까? - dlguswl936

profile
To be a Backend Developer

0개의 댓글