[레벨1] 체스 1, 2단계 - 체스판 초기화, 말 이동
- 저장소: https://github.com/yoondgu/java-chess/tree/step1
- 코드리뷰 진행 PR: https://github.com/woowacourse/java-chess/pull/454
기능 요구사항
콘솔 UI에서 체스 게임을 할 수 있는 기능을 구현한다.
- 1단계는 체스 게임을 할 수 있는 체스판을 초기화한다.
- 체스판에서 말의 위치 값은 가로 위치는 왼쪽부터 a ~ h이고, 세로는 아래부터 위로 1 ~ 8로 구현한다.
- 체스판에서 각 진영은 검은색(대문자)과 흰색(소문자) 편으로 구분한다.
- 2단계는 콘솔 UI에서 체스 게임을 할 수 있는 기능을 구현한다.
- 체스 말의 이동 규칙을 찾아보고 체스 말이 이동할 수 있도록 구현한다.
- move source위치 target위치을 실행해 이동한다.
> 체스 게임을 시작합니다.
> 게임 시작 : start
> 게임 종료 : end
> 게임 이동 : move source위치 target위치 - 예. move b2 b3
start
RNBQKBNR
PPPPPPPP
........
........
........
........
pppppppp
rnbqkbnr
move b2 b3
RNBQKBNR
PPPPPPPP
........
........
........
.p......
p.pppppp
rnbqkbnr
구현하며 고민한 내용
지난 미션들을 교훈삼아, 이번 미션에서는 페어 프로그래밍 때부터 페어와 협의해 설계에 많은 시간을 투자했다.
"이렇게 설계하려면, 어떻게 구현해야 할까?" 구현을 상상하며 설계를 한 것이 도움이 많이 된 것 같다.
체스판이 말을 움직일 것인가, 말이 스스로 움직일 것인가?
말 간의 위치 관계가 게임의 핵심 로직에서 사용되므로
체스판이 각 말의 위치를 아는 구조가 구현하기도, 관리하기도 더 편할 것이라 판단했다.
실제로도 좋은 판단이었다고 생각했다.
다만, 이동 요청에 대한 검증 책임을 말과 체스판에서 나눠 가지게 되면서
불완전한 상태에서 실행될 수 있는 공개 메서드들이 있는 것은 여전히 고민이었다.
(이에 대해서는 3, 4단계에서 더 고민했어서 다음 포스트에서 작성하겠다.)
체스 말의 타입을 확인하는 방법
실제 구체적인 말의 종류를 표현하는 클래스들이 Piece 라는 추상클래스를 상속하도록 만들었다.
말의 종류를 알 필요 없이 수행 가능한 기능들도 있었지만,
말의 종류에 따라 출력 정보를 매핑시키거나, 말의 종류가 폰일 때만 어떤 로직을 수행해야 하는 경우도 있었다.
이 때 getClass, instanceOf를 지양해야 하는 이유에 대해 학습하였고
그 대신 PieceType이라는 이넘 클래스를 사용하거나, isXXX()와 같은 메서드로 객체에게 직접 물어보도록 했다.
(그런데 PieceType을 꺼내 쓰는 게 getClass의 단점과 뭐가 다른지는 아직 잘 모르겠다.)
참고 링크: instanceof의 사용을 지양하자
코드리뷰에서 배운 것들
커밋 메시지는 구체적이어도 좋다
불가피하게 하나의 커밋에 관련 변경 사항이 매우 많아졌다.
이전에 자동차 미션에서 받았던 피드백을 기억하며 대신 커밋의 바디부에 변경 사항을 구체적으로 작성해주었다.
이에 대해 이번 리뷰어님도 커밋 내용이 매우 친절하다고 피드백해주셨다!
그리고 현업에서도 커밋 바디에 얼마든지 구체적인 내용을 작성하는지,
컨벤션 및 가독성만 지킨다면 필요에 따라 얼마든지 구체적으로 적어도 되는지 궁금해서 질문을 드렸고,
아래와 같은 답변을 받았다.
코드리뷰 피드백:
구체적으로 적은것이 문제되는 상황은 경험하지 못했어요 😄커밋 메시지가 구체적이면 리뷰하는 입장에서는 많은 정보를 얻을 수 있어 좋다고 생각해요!! 💯
커밋 메시지의 Head(?) 부분에 작업을 표현하고
만약 Head 에 작업을 표현하기에 충분하지 않았다 라거나 항목이 여러가지다! 라면Body에 자세히 적어주는 편입니다!
디자인 패턴에 매몰되지 않기
학습 목적으로 상태 패턴을 적용해보았지만
리뷰어님의 질문 덕분에 내가 이 패턴에 대해 모르고 있었다는 것을 알게 되었고
장점이 무엇인지 뒤늦게 고민해볼 수 있었다.
그래서 패턴을 적용하지 않은 방식, 상태 패턴을 적용한 방식을 모두 다시 구현해보고 비교해보았다.
Move 커맨드에 의한 변경 외에는 오로지 running, ready 라는 상태의 교체만 단순하게 이루어지는 상황이라
상태 패턴이 효과적으로 사용되는 방식은 아니라 상태 패턴이 효과적으로 사용되는 방식은 아니라고 결론 내렸다.
고민이 생겼다면, 무언가 잘못되었는지 의심하기
이동 가능 여부를 반환하는 공개 메서드가 "출발지-도착지의 이동방향은 유효하다"는 가정을 포함하고 있는 문제가 있었다.
출발지-도착지의 이동방향이 해당 말이 움직일 수 없는 방향이더라도,
경로 사이에 장애물만 없다면 "이동 가능하다"고 판단하는 것이다.
그저 찜찜한 부분이라고만 생각하고 넘겼는데,
리뷰어님이 "해당 고민이 들었다면 무언가 잘못됐음을 의심해봐도 되겠다"고 말씀해주셨다.
생각해보니 검증 로직이 통합되어있지 않아서 생긴 문제였다.
기존 구현을 크게 바꾸지 않는 선에서 Piece가 이동에 대한 모든 검증을 담당하도록 수정했다.
그저 찜찜하다고만 생각하고 넘어갔었는데, 무엇이 잘못됐는지 제대로 의심하는 태도를 배울 수 있었다.