[로또] 코드
3주차 과제를 진행한 repo 주소이다.
https://github.com/Dreaming-J/java-lotto-6/tree/Dreaming-J
3주차 회고
지난 과제와 다르게 이번에는 여러 키워드가 나를 더욱 고민시킨 주차였다.
"검색 & 문서 탐색", "객체 분리 & 메시지", "단위 테스트", "함수형 인터페이스", "상속" 해당 키워드들이 이번 주 내내 나를 괴롭혔다.
검색 & 문서 탐색
이 키워드는 나를 괴롭히진 않았고 많은 깨달음을 줬다.
과제마다 새로운 지식을 습득하고 있어 인터넷에 검색하는 것이 얼마나 유용한지 충분히 알고 있었지만,
이번 주차를 통해 검색은 최고의 학습 도구임을 "수익률 계산" 과정에서 다시 한 번 깨달았다.
세자리마다 쉼표를 추가하는 기능을 구현하는 것이 어려워 며칠간 머리를 앓고 있었다.
단순히 기능을 수행하는 코드를 구현하는 것은 쉬웠으나 그 코드를 바라보니 흔히 말하는 "똥 싼 코드"로 보였다.
처음에는 BigDecimal 클래스를 활용해서 구현했으나, 너무 큰 수가 되어버리면 지수로 표현되는 것이 골머리였다.
그래서 다른 방식을 고민하던 찰나 DecimalFormat 클래스를 알게 되었다.
해당 클래스를 통해서 반올림, 쉼표 기능, 지수 표현까지 싸그리 해결할 수 있었다.
기존에 수십줄이던 코드가 해당 클래스 활용으로 인해 단 2줄만에 해결이 된 것이다!
나에게 DecimalFormat은 미개척지였고, 해당 클래스를 알게 됐을 때 신대륙을 발견한 것처럼 너무 기뻤다.
이 경험은 앞으로도 새로운 지식을 향한 동력이 되어주리라 생각이 든다.
객체 분리 & 메시지
이번 과제에 코치가 제시한 목표이다.
개인적으로는 "이정도면 잘했다!"라고 개인적으로 평가를 내리고 싶다.
물론 찾지 못한 문제점이나 더 좋은 방안이 있겠지만 과거와 비교하면 더욱 잘 분리했고, 객체의 역할에 벗어나지 않도록 메서드를 작성했다고 생각이 들었다.
이번에 객체를 분리하면서 가장 신경을 쓴 부분은 메시지이다.
객체에 메시지를 보내 스스로 행동하도록 객체의 내용을 다른 객체가 함부로 접근하고 수정할 수 없도록 노력했다.
더욱 성장할 여지가 있는 부분이라 느껴 누군가에게 피드백을 받고 더욱 좋은 방안을 찾고 싶다는 생각이 들었다.
단위 테스트
이번 과제에 제시된 두번째 목표이다.
꾸준히 단위 테스트를 하고자 노력했지만 부족한 부분을 스스로도 느끼고 있었다.
제시된 목표이기도 하기에 더욱 신경써서 단위 테스트를 했다.
예전에는 TDD를 시도하다 구조가 복잡해질수록 단위 테스트를 하기 어려워 테스트 작성을 못한 기능들이 존재했다.
이번에는 지난 주차 다짐이었던 "기능 테스트를 위한 구조 고민하기"로 혼자만의 목표가 있었기에 기능 목록 작성 시점부터 고민을 많이 했다.
디스코드 토론방을 구경하며 일급컬렉션을 알게 되고 적용하고자 노력했으며, 기능을 테스트하기 위해 메시지를 보내는 고민을 하게 됐다.
이 덕에 객체에 메시지를 보내는 것도 더욱 잘하게 된 것 같다는 생각이 들었다.
실제로 이번 과제를 하며 구현한 기능은 모두 테스트에 성공했다.
하지만 입력과 관련된 테스트는 성공하지 못했다. 그래서 이는 어쩔 수 없이 직접 애플리케이션을 구동하며 예외 처리가 잘 되는지 일일이 확인했다.
함수형 인터페이스
"검색"을 가장 많이 하게 만든 키워드이다.
이번 주차부터 예외 입력 시 에러 문구를 출력하고 해당 부분부터 다시 시작해야 하는 조건이 생겨났다.
가장 유명한 방식인 try-catch문을 이용하려 했지만 예전에 봤던 손쉽게 재입력을 받는 방식이 생각이 났다.
try-catch문을 메서드로 생성하고, 재입력받을 메서드를 매개변수로 받아 문제가 생겼을 때, try-catch를 하는 코드이다.
이전에는 그저 해당 코드를 가져와 이용하기만 했지 이해하고 내가 필요한 상태에 맞춰 수정하지는 못했었다.
이번에는 해당 코드를 이해하고자 노력해서 함수형 인터페이스가 무엇인지, <T>는 제너릭을 활용한 것을 알게 되면서 이해도가 높아졌다.
그래서 이번에는 직접 수정하여 내 입맛에 맞게 사용할 수 있게 되었다.
//기존 코드
protected <T> T repeat(Supplier<T> inputReader) {
try {
return inputReader.get();
} catch (IllegalArgumentException e) {
System.out.println(ERROR_PREFIX + e.getMessage() + LINE_BREAK);
return repeat(inputReader);
}
}
//수정 코드
protected <T> void repeat(Supplier<T> inputReader, Consumer<T> setMethod) {
try {
setMethod.accept(inputReader.get());
} catch (IllegalArgumentException e) {
System.out.println(ERROR_PREFIX + e.getMessage());
repeat(inputReader, setMethod);
}
}
상속
당첨 로또를 만들기 위해 많은 고민을 했었다.
똑같이 Lotto 클래스로 만들게 되면 주어진 프로그래밍 요구 사항을 지키기 어렵다고 판단했다.
보너스 번호도 입력을 받아야 하는데 이는 당첨 로또와 함께 보관하는게 좋다고 판단했기 때문이다.
고민하던 중 상속을 이용해 AnswerLotto 클래스를 만들자고 생각이 들었다.
이번 프리코스를 진행하며 다름 사람의 코드를 조금씩 읽어본 덕에 예전이었다면 생각도 못해봤을 접근을 할 수 있게 됐다.
이번 키워드는 나에게 피어리뷰를 통한 성장을 느끼게 해주었다.
피어리뷰를 통해 다른 사람의 코드를 보며 평소의 내가 생각하지 못하는 부분을 접하게 되면서 자연스레 시야가 넓어졌다.
3주차 아쉬웠던 점
코드를 제출하고 회고록을 작성하는 시점에서 다시 보니, 상속에 얽매이지 말고 그냥 AnswerLotto 클래스의 필드로 Lotto 객체를 보관하는 것이 어땠을까 하는 생각이 든다.
이전 코드는 당첨 번호와 보너스 번호 유효성 검사를 동시에 진행하기 때문에 보너스 번호에 에외가 발생해도 당첨 번호부터 입력받아야 하는 문제가 발생했었다.
이를 해결하기 위해 생성자에서 보너스 번호를 받지 않고 AnswerLotto 클래스 내부에 registerBonusNo 메서드를 생성하여 보너스번호를 세팅해주었다.
이는 setter와 다름없고 수정이 가능하기에 좋지 못한 구조라고 생각이 들었지만 다른 방안이 생각나지 않아 이대로 만족하였다.
하지만 AnswerLotto가 Lotto를 상속받지 않고 생성자로 Lotto 객체를 받고 필드로 보관하는 방식이 적용 된다면 더욱 좋았을 것이다.
당첨 번호가 보관될 Lotto 객체를 미리 생성하며 당첨 번호 유효성 검사를 하고, 그 후에 보너스 번호 유효성 검사를 AnswerLotto 클래스의 생성자에서 진행했다면 보너스 번호를 외부에서 set해주지 않는 더욱 안전한 구조였을텐데하는 생각이 든다.
4주차 다짐
- 피어리뷰하기!
- 입력 로직까지 생각하여 클래스 생성하기