- 로또 요구사항을 파악한다.
- 요구사항에 대한 구현을 완료한 후 자신의 github 아이디에 해당하는 브랜치에 Pull Request(이하 PR)를 통해 코드 리뷰 요청을 한다.
- 코드 리뷰 피드백에 대한 개선 작업을 하고 다시 PUSH한다.
- 모든 피드백을 완료하면 다음 단계를 도전하고 앞의 과정을 반복한다.
- 문자열 사칙 연산 계산기 구현
- 사용자가 입력한
문자열 값에 따라 사칙연산을 수행할 수 있는 계산기를 구현해야 한다. 입력 문자열의 숫자와사칙 연산 사이에는 반드시빈 공백 문자열이 있다고 가정한다.나눗셈의 경우결과 값을 정수로 떨어지는 값으로 한정한다.문자열 계산기는 사칙연산의 계산 우선순위가 아닌입력 값에 따라 계산 순서가 결정된다. (즉, 수학에서는 곱셈, 나눗셈이 덧셈, 뺄셈 보다 먼저 계산해야 하지만 이를 무시한다.)- 예를 들어
2 + 3 * 4 / 2와 같은 문자열을 입력할 경우2 + 3 * 4 / 2실행 결과인10을 출력
- 사용자가 문자열을 입력한다.
- 사칙 연산을 모두 포함하는 기능 구현
- 입력 값이 null이거나 빈 공백 문자일 경우
IllegalArgumentException throw - 사칙연산 기호가 아닌 경우
IllegalArgumentException throw
- 입력받은 문자열을 처리한다
- 공백 문자열을 빈 공백 문자로 분리하려면 String 클래스의
split(" ")메소드
- 공백 문자열을 빈 공백 문자로 분리하려면 String 클래스의
- 반복적인 패턴을 찾아 반복문으로 구현
- 덧셈
- 뺄셈
- 곱셈
- 나눗셈
- : 로또 금액을 입력한다.
- : 입력받은 금액으로 로또 n개 생성
- : 1장의 가격은 1000원
- : 로또 생성 시
Collections.shuffle()로 생성 - :
Collections.sort()로 로또 내부 번호 6자리 정렬
- : 지난 주 당첨번호를 입력한다
- :
ArrayList의 contains()로 어떤 값이 존재하는지 유무를 판단가능- : 3개, 4개, 5개, 6개 일치를 찾는다
- : 총 가격대비 얼마 이득인지 수익률을 계산한다
- :
Process: 로또 진행 흐름을 담당 (입력받은 금액을 갯수로 바꾼다.)Lottos: 입력받은 갯수에 따라Lotto객체 생성Lotto: 로또 역할, 입력받은 당첨번호로 당첨인지 확인WinnerCount: 당첨번호 입력 및 당첨 통계 추리기
WinnerCount를 Process 객체의 멤버로 넣어야 할 것인가?- 그러나
Process내부 다른Lottos필드와 와 생명주기가 다름 Lottos는 초반에 생기지만,WinnerCount후반에 생김WinnerCount에 있는calculateTotalReturn()메서드에 인자로 pay를 넣는게 바람직한가 고민이 듬- 왜냐하면
Process필드로 pay가 있는데 이것을 활용하지 못하고, controller에서 입력받은 pay를 활용해서 넣기 때문임
- 왜냐하면
- 그러나
- 3,4,5,6개 일치관련 enum 처리
- switch 대신에 대안은 -> enum으로 행동넣기
2등을 위해 추가 번호를 하나 더 추첨한다. 당첨 통계에 2등도 추가해야 한다.
- 2등을 위해 추가 번호를 하나 더 추첨한다.
- 당첨 통계에 2등도 추가해야 한다.
- : 2등 당첨을 위해 로또 입력(보너스 볼)을 한번 더 받는다
- : 당첨 방식에서 2, 3 등 차이를 같은 5개일치 + 보너스볼 일치로 차이를 둔다.
- : enum
- : 상수 명을 일치 갯수에서 등수로 변경한다
- :
MISS추가한다
- : 출력문 변경
-
기존
LottoGame: 로또 진행 흐름을 담당 (입력받은 금액을 갯수로 바꾼다.)Lottos: 입력받은 갯수에 따라Lotto객체 생성Lotto: 로또 역할, 입력받은 당첨번호로 당첨인지 확인WinningResult: 당첨번호 입력 및 당첨 통계 추리기Pay: 입력받은 지불 금액에 대한 객체Rank: 당첨 수와 금액의 정보를 가지고, 당첨에 필요한 매치넘버를 관리하는 객체
-
추가
Bonus: 추가 수를 받아서 추가로 부합하는지 검증하는 객체
- : Bonus 에 필드 불변으로 변경
- : Lotto 와 Bonus 간에 메시지를 주는 객체간 관계 재조정 및 테스트 제거
- : findMatchCount 접근제어자 private 로 변경하면서 테스트 제거
- :
rankDecideByBonusNumber메서드의 주체 변경- 이유 : 메서드 시그니쳐가 모두 Rank 가 있는데,,, 이정도면 충분히 Rank 에게 주어야하는 역할임
- : Array 를 List 로 변경(Ai 검색 도움)
- : 주요 메서드 명 변경 (Ai 도움)
- 이유 : 의도를 명확히 드러내기 위해서
- :
Lotto문자열도 입력 받을 수있는 생성자 만들기 - : Lotto 에서 WinningLotto로 당첨 판단에 대한 지식을 옮긴다.
- : Lotto 에서 일치여부만 판단하는 것을 남긴다.
- : Rank 와 Lotto 간 순환구조를 끊는다.
- : 이를 위해서 Rank 내부에 매치 카운트에 2,3 등 부분까지 포함한다
- : 생성자 리팩터링
- : 반복문 stream API 로 변경
Lotto : 기본적인 로또 번호 역할 + 로또 번호가 일치하는지
WinningLotto : Lotto 에서 일치갯수와 보너스 맞는지 받아와 Rank 에 전달하여 당첨 결과를 WinningResult 에 전달
Rank : 규칙 정의 및 등수 판단
Rank와Lotto순환참조 없애기- indent 가 2여서 그러는건데 이 경우에 스트림 쓰기 추천
bonus가 모든 번호 검증 -> 필요한거만- 쓰지않는 패키지(ctl + opt + O)와, 포매팅 정리 (opt + cmd + L)
- 난수생성에 대한 테스트 통제 (보류)
- 현재 난수생성 방식은 static 메서드로 구현하여서 테스트 코드 내에서 Override가 불가능하다.
- :
LottoNumber는 매번 인스턴스가 생성되기 때문에 인스턴스의 갯수가 너무 많아져 성능이 떨어질 수 있다. LottoNumber 인스턴스를 생성한 후 '재사용'할 수 있도록 구현한다. Map과 같은 곳에 인스턴스를 생성한 후 재사용하는 방법을 찾으면 됨 -> 방식 : 캐싱 구현
Pay → LottoGame
↘
LottoTickets → List<Lotto> → List<LottoNumber> ← LottoNumberCache
↘
WinningLotto (Lotto + bonus LottoNumber)
↓
Rank → WinningResult (Map<Rank, Integer>)
- 최종
Pay: 입력받은 지불 금액에 대한 객체LottoGame: 로또 진행 흐름을 담당 (입력받은 금액을 갯수로 바꾼다.)LottoTickets: 입력받은 갯수에 따라Lotto객체 생성Lotto: 로또 역할, 입력받은 번호가 몇개 일치하는지 확인LottoNumber: 로또의 각 수에 대한 포장 객체LottoNumberCache: 로또 번호를 캐싱하여 로또번호 재생산 막는 역할WinningLotto: 당첨번호 관리 (Lotto + LottoNumber)Rank: 당첨 수와 금액의 정보를 가지고, 일치 갯수를 가지고 등수를 결정WinningResult: 당첨 당첨 통계 추리기
- 현재 로또 생성기는 자동 생성 기능만 제공한다. 사용자가 수동으로 추첨 번호를 입력할 수 있도록 해야 한다.
- 입력한 금액, 자동 생성 숫자, 수동 생성 번호를 입력하도록 해야 한다.
- 구매 로또 시 수동으로 추첨하는 기능 추가
- 자동 생성 숫자, 수동 생성 번호 추가
- : 수동으로 구매 할 로또 수 입력
- : 수동으로 구매하는 로또가 총 구매가능 수 보다 적은지
- :로또 곧바로 자동 생성이 아닌 총 구매 가능 수 제시
- : 넘었을 경우 다시 입력 유도
- :로또 곧바로 자동 생성이 아닌 총 구매 가능 수 제시
- : 수동으로 구매하는 로또가 총 구매가능 수 보다 적은지
- : 수동으로 구매할 로또 번호 입력
- : 수동 로또 수만큼 반복입력한다
- : 중복 입력, 1 ~ 45 범위 넘겨서 입력 시 다시 입력 유도
- 고민 : 입력 한번 할 때 마다 제시 or 모아서 넘기기
- : 모아서 넘기기
- : 수동 로또 수만큼 반복입력한다
- : 로또 발행 결과 수동, 자동 갯수 카운트
- : 수동부터 먼저 제시
Pay: 입력받은 지불 금액에 대한 객체LottoGame: 로또 진행 흐름을 담당 (입력받은 금액을 갯수로 바꾼다.)LottoTickets: 입력받은 갯수에 따라Lotto객체 생성Lotto: 로또 역할, 입력받은 번호가 몇개 일치하는지 확인LottoNumber: 로또의 각 수에 대한 포장 객체LottoNumberCache: 로또 번호를 캐싱하여 로또번호 재생산 막는 역할WinningLotto: 당첨번호 관리 (Lotto + LottoNumber)Rank: 당첨 수와 금액의 정보를 가지고, 일치 갯수를 가지고 등수를 결정WinningResult: 당첨 당첨 통계 추리기
BuyLotto: 로또 구매(자동 + 수동) 에 대한 관리manualLotto: 수동구매하는 로또와 그 갯수 관리
- : 로또를 생성하는 부분 인터페이스로 분리해 보는 연습
- : of 메서드를 활용하면 LottoNumber 객체가 재사용되는 것을 보장하지 못함
LottoNumber객체가 캐싱을 통해 재사용하는 것을 강제할 수 있도록 리팩터링LottoNumber와LottoNumberCache를 통합
- : 오히려 복잡해진 인터페이스 분리를 단순화하여서 구현
-
1. 이전에는 인터페이스 구현체들은 필드가 모두 같아야한다는 것으로 잘못된 이해를 한점 - 그 이유는 인터페이스 이용을 스프링 bean 생성 때만 주로 해봤기 때문-
- 필드가 같아야한다는 잘못된 이해로 메서드 인자는 모두 같게, 그 내부 로직을 달리해서 구현한 점
-
- 이러한 이유 때문에 오히려 인터페이스가 더 복잡해짐
-
- : Controller 에서 분기 처리에 따라 인터페이스 구현체 갈아끼우는 것 제거하기
- 조건문으로 같은 일(모드, 도메인)의 규칙 갈아끼우기에 불과함
- 이렇게 인터페이스 분리하면 장점
- 클라이언트 코드가 당장 필요한 구현체로 쉽게 갈아끼울수 있게 변경
- : LottoGenerator 가 LottoBuy 가 아닌 LottoTickets 을 생성하게 하기
- : LottoGenerator 구현체에서 바로 LottoTickets를 생성하도록 접근하기
- : LottoManulGenerator 에서 불필요한 pay 제거
- 자동, 수동, 혼합 간에 필드는 비슷하게 pay, pay + lotto, lotto 각각 상황에 따라 인자가 달라짐
- 즉 충분히 필드가 다를 수있다.
- 자동, 수동, 혼합 간에 필드는 비슷하게 pay, pay + lotto, lotto 각각 상황에 따라 인자가 달라짐
- : 이미
LottoAutoGenerator,LottoManualGenerator각각의 구현체가 있는데 굳이 수동 자동관련Auto,Manual객체가 필요한지 고민할 것- 고민 : 기존에는 Auto, Manual 이 필요할 수있었다. 그러나 인터페이스 구현체가 그 역할을 대체한다
- : composite 패턴을 이용해 part 인 구현체
LottoAutoGenerator,LottoManualGenerator를 합친 composite 인LottoCombineGenerator를 이용한다- 그러면 필드도 두개 구현체를 합친 인터페이스의 List로 한다 그리고 기존 생성도 두개를 각각 넣어주는 방식으로 구현한다
- 즉 여러 LottoGenerator 를 묶어서 두개를 하나처럼 쓸 수 있게 됨