이번에 우아한 테크코스 6기 프리코스에서 숫자 야구 게임을 구현해 보았다.
공통 피드백도 나왔기에 회고의 느낌으로 작성할 예정이다.
평소에도 객체지향에 관심이 많았고, 이를 충분히 적용하려고 노력하였다.
하지만 역시 코드에 정답은 없기에 미션을 수행하면서도 많은 고민을 하였다.
우선 구현에 사용하도록 외부 라이브러리가 포함되어 있었다.
이를 분석하고, 사용하기 위해 클래스 내부를 뜯어보았다.
내부에서 ThreadLocalRandom를 사용하여 랜덤 값을 생성하는데, ThreadLocalRandom를 찾아보니 동시성 문제에도 성능 저하 없이 사용할 수 있다는 특징이 있었다.
Console의 경우에는 scanner를 싱클톤으로 사용하여 효율적으로 리소스를 활용하는 모습이었다.
살아있는 문서
가장 처음 목표로 삼았던 것은 살아있는 문서였다.
사실 처음부터 완벽한 설계가 없다는 것은 미션을 진행하면서 느끼게 되었다.
나 역시 중간중간 수정으로 인해 문서가 계속해서 수정되었다.
의식적으로 문서를 중요시 생각하며 살아있는 문서를 만들려고 노력했다.
고민거리
고민을 정말 많이 했었다.
앞서 말했지만, 코드에는 정답이 없다. 따라서 내가 코드를 작성할 때는 그 이유가 분명히 있어야 하고 타당해야 한다고 생각한다.
내가 고민했던 몇 가지를 기록할 생각이다.
첫 번째는 컴퓨터를 생성할 때 밖에서 랜덤 값을 꺼내어 List를 넘길지, 아니면 랜덤 생성기를 넘겨서 실제 내부에서 랜덤 한 값을 꺼낼지를 고민했다. 우선 전자의 경우 랜덤 생성기를 의존하지 않기에 유지보수성 및 여러 측면에서 장점이 있다고 생각했다. 후자의 경우는 인터페이스로 구현하면 어느 정도의 유연성이 있지만, 단순한 List에 비해서는 떨어진다고 생각했다. 다만 랜덤이라는 의미가 더 잘 나타난다고 생각했다. 이에 대한 정답은 없지만 내가 내린 결론은 매개변수로 랜덤 생성기를 넘기는 것이었다. 컴퓨터는 랜덤으로 값을 생성할 역할도 있고 생성기 자체가 인터페이스이기에 충분히 유연성이 있다고 생각해서 이러한 결론을 내렸다.
이에 대한 의견은 분명 나뉠 것이다.
잘 적용한 점
랜덤 생성기를 인터페이스의 구현체로 만들어서 NumberGenerator라는 인터페이스를 매개변수로 넘기도록 설계하였다.
이렇게 설계한 이유는 확장성 + 테스트 코드에서의 이점이 있기 때문이다.
테스트 코드에서의 어떤 이점이 있을까?
랜덤 값을 생성하기에 테스트를 돌리면 어떤 값이 나올지 예측할 수 없다.
그래서 임의의 로 확정적으로 원하는 값이 나오도록 구현체를 만들어서 테스트에서 사용할 수 있었다.
고민거리
네이밍이 역시 너무나 어려웠다. 특히 정적 팩토리 메서드를 작성할 때 이러한 문제를 직면했다.
위의 코드를 다시 예시로 들면 아래와 같은 두 가지 네이밍을 후보로 둘 수 있다.
전자의 경우 매개변수의 NumberGenerator까지 한 번에 보면 말이 이어지기에 읽기가 자연스럽다. 하지만 코드를 작성하는 입장에서 createFrom만 보고는 어떤 매개변수를 넣어야 하는지 예측하기 어렵기 때문에 Computer내부까지 들어가야 한다.
후자의 경우 createFromGenerateNumbers라는 정확한 네이밍이 있기에 코드를 작성할 때 매개변수로 어떤 값이 들어갈지 쉽게 예측할 수 있다. 다만 코드가 작성되었다면 네이밍이 중복된다는 느낌이 확실히 들게 된다.
우선 나는 후자를 선택하여 사용했다. 이유는 네이밍이 중복되더라도 확실한 이름을 주고 싶었고, 실제 개발 시 좀 더 유리하다는 느낌을 받았기 때문이다.
이에 대한 토론도 진행해 보았다. 토론에서는 다른 예시를 사용하였지만 본질은 동일하다.
확실히 여러 사람들의 의견을 들으니까 계속해서 생각하게 되고 어떤 방향으로 선택하는 게 나을지도 정해지는 것 같다.
나만의 컨벤션을 정하는 과정이라는 생각도 들었다.
배운 점
원래 구조는 List <Integer>를 사용하여 입력값들을 관리하였다. 그러다 보니 자연스럽게 내부의 모든 숫자를 탐색하면서 1~9인지를 판단해야 했다. 이러한 책임이 어색하다는 느낌을 받았다. 그래서 객체 지향에 대한 여러 자료를 찾아보았고, 이를 원시값 포장을 통해 해결했다. Integer 대신 Number라는 이름을 붙여 주니 1~9 인지에 대한 검증의 책임을 분리할 수 있었다. 처음에는 원시값 포장에 대한 장점을 이해하지 못했지만, 직접 적용해 보니 몸소 느낄 수 있었다.
확실히 단일 값에 대한 검증을 따로 분리하니 책임이 분리되었고, 이후 확장성에도 유리할 것 같다는 느낌을 받았다.
잘 적용한 점
테스트를 진행할 때 반복되는 로직을 처리하기 위해 fixture를 만들어서 사용했다.
자주 사용되는 로직을 빼로 분리하여 재사용성 있게 사용하여 가독성을 높였다.
또한 ParamterizedTest를 사용하여 테스트를 간단하게 여러 경우를 테스트할 수도 있었다.
마무리
이외에도 많은 부분을 고민하고, 최적의 코드를 작성하려고 노력했다. 또한 나만의 컨벤션을 정하려고 노력했다.
아직도 많이 부족하지만 나의 지식이나 생각을 여러 사람들에게 전하고 싶어서 코드리뷰도 활발하게 진행했었다.
진행하다 보니 너무 재미있어서 조금은 과하게 많은 사람들에게 리뷰를 진행해 주었다.
근데 너무 많은 사람들에게 리뷰를 달아주니 답변이 오게 된다면 너무 많아져서 전부 답장해 주기 힘들기도 하였고, 까먹을 때도 있었다.
그래도 너무 재미있는 것 같다.
남의 코드를 보면서 배울 점도 많았고, 나 또한 리뷰를 통해 새로운 의견을 받을 수 있었다.
남은 미션도 후회없이 최선을 다하자.
실제 작성한 코드이다.
https://github.com/kimtaesoo99/java-baseball-6/tree/kimtaesoo99
'JAVA' 카테고리의 다른 글
[JAVA] 로또를 구현하면서 배운점 (2) | 2023.11.10 |
---|---|
[JAVA] 자동차 경주를 구현하면서 배운점 (3) | 2023.11.02 |
[JAVA] 체스를 구현하면서 배운점 (0) | 2023.07.06 |
[JAVA] 블랙잭을 구현하면서 배운 점 (0) | 2023.06.20 |
[JAVA] 리플렉션(Reflection) (0) | 2023.05.23 |