저번 블랙잭을 이후로 이번에는 체스를 구현해 보았다.
우선 설계를 조금 디테일하게 잡아서 진행하였다.
물론 첫 설계를 끝까지 가져가지는 못했다.. 중간에 추가된 부분이 조금은 있다.
그래도 처음보단 설계하는 실력도 늘었다고 생각한다.
진행하면서 고민하고 배운 것을 정리하였다.
고민했던 것
우선 첫 번째 고민은 각각의 기물들의 위치와 기물을 Map으로 관리하였다.
이때 위치를 단순하게 String으로 관리하려 했다.
하지만 이럴 경우 예외처리를 하는 경계도 애매해지고, 관리하기가 힘들 것 같다고 판단하였다.
그래서 위치 즉 Location도 객체로 관리하여 예외를 처리하도록 하였다.
위치도 더욱 나눌 수 있기 때문에 행과 열을 체스의 용어에 맞게 분리하여 코드를 작성했다.
또 다른 고민으로는 기물이 없는 곳을 표현하는 것이었다.
Map <Location, Pieces>로 관리를 하는데 기물이 없는 곳을 어떻게 표현할까에 대한 고민을 하였다. 처음에는 아무것도 없다는"."을 Pieces로 구현하여서 사용할까에 대해 생각했다. 하지만 아무것도 없다는 것이 기물을 상속받는 것이 이상하다는 생각을 하였다.
따라서 실제 기물이 있는 곳만 Map에 담아두었다.
배운 점
우선은 코드를 작성하며 이상한 점을 발견하고 배운 점이다.
next(), 다음에 nextLine()을 사용하면 안 된다.
- 이유 next() 다음에 nextLine()은 개행문자 때문에 인식되지 않는다.
올바른 입력에도 next이후에 nextLine을 사용하니 잘못인식되어 오류가 발생했다.
이후 둘 다 nextLine()을 사용하여 해결할 수 있었다.
다음은 char + int에 대한 것이다.
Char + int의 진실
기본적으로 char + int는 모두 알다시피 int를 반환한다.
따라서 만약 다시 char형으로 바꾸고 싶다면 캐스팅을 해줘야 한다.
위 코드에서 주목할 부분은 (char) (LOWER_CASE_A + rank)이다
이를 좀 더 보기 쉽게 바꾸면 (char) ('a' + 0~7)이다.
여기서 (char)를 제거하면 오류가 발생한다.
당연한 결과이다.
근데 이상한 것을 느꼈다.
char cc2 = 'c' + 1;
그럼 위의 코드는 어떻게 될까? 에러가 터질까?
정답은 아니다
위 코드는 정상작동 한다. 여기서 의문이 들었다. 둘 다 char + int인데 어떤 것은 캐스팅을 해줘야 하고 어떤 것은 괜찮은 이유가 뭘까?
이는 간단한 테스트를 통해 알 수 있었다.
위에 보이는 것을 통해 유추할 수 있다.
그렇다. 바로 char의 범위를 넘거나 범위를 넘을 가능성이 있다면 캐스팅을 해줘야 한다.
1을 직접 대입해 줘도 cc2는 char범위 안에 있다. 하지만 num이란 변수를 프로그램 입장에서는 바로 알 수 없다.
따라서 에러를 발생하는 것이다.
assertThatThrownBy()로 검증 시, 예외 메시지 검증을 고려하자.
처음에는 위의 코드의 예외테스트를 단순하게 예외가 던져지는 것을 검증하는 것으로 마무리지었다.
하지만 모두 같은 에러를 던지는데 이를 구별할 수 없다는 단점이 있었다.
즉 꼼꼼하지 못한 테스트를 작성했다.
이를 해결하는 방법으로 예외메시지를 검증하여 더욱 꼼꼼하게 테스트하였고, 이러한 중요성을 알게 되었다.
getter/setter 테스트에 대한 생각
이전에는 getter/setter를 그냥 메서드가 있으니까 테스트를 진행하였다.
그러다 getter/setter를 테스트할 이유가 있는지에 대한 피드백을 받았다.
단순하게 객체의 속성 값을 반환하는 로직도 테스트를 작성하였었다.
처음에는 당연히 모든 테스트를 진행하는 것이 맞다고 생각하기에 이상한 점을 느끼지 못하였으나
이를 좀 더 생각해 보니 충분히 고민할 가치가 있는 이야기였다.
왜냐하면 많은 테스트도 좋지만 테스트도 결국 유지보수의 대상이고, 비용이 들기 때문이다.
따라서 내가 내린 결론은 반환되는 값의 역할에 따라 테스트여부를 결정하기로 했다.
즉 반환되는 값이 중요한 정보를 가지고 있다면 테스트를 진행하는 방향으로 작성하자고 생각했다.
정리
내가 느끼기에 저번 블랙잭에 비해 훨씬 어렵다고 생각했다.
하지만 그건 단순한 난이도의 문제이고, 실제 구현하는데 막히거나 한 부분에 있어서는 더 할만했다.
아마 이러한 생각이 드는 이유는 그만큼 성장했기 때문이라고 생각한다.
또한 생각보다 당연하다고 생각했던 것들을 구현 도중에 자세히 알게 되는 부분이 있어서 좋았던 것 같다.
마무리 멘트로는 당연한 코드란 없다고 생각한다.
왜 이렇게 썼는지 이것이 정말 필요한지, 더 좋은 방법은 없는지에 대한 고민을 하며 성장해 나가자.
위의 내용 말고도 받은 피드백에 관환 링크는 아래에 걸어두었습니다.
https://github.com/kimtaesoo99/java-chess/pull/1
'JAVA' 카테고리의 다른 글
[JAVA] 자동차 경주를 구현하면서 배운점 (3) | 2023.11.02 |
---|---|
[JAVA] 숫자 야구 게임을 구현하면서 배운점 (4) | 2023.10.27 |
[JAVA] 블랙잭을 구현하면서 배운 점 (0) | 2023.06.20 |
[JAVA] 리플렉션(Reflection) (0) | 2023.05.23 |
[JAVA] 불변 객체를 사용해야 하는 이유 (2) | 2023.05.17 |