들어가며
GitHub 저장소
구현 기능들
궁금한 것
코드 리뷰
후기
📆 기간 : 2023.03.14~ 2023.03.16
들어가며
이번 체스 미션은 페어 케로와 함께 진행하게 되었다. 처음 만나자 마자 말 놓고 하자 라는 말을 들었다. 그렇게 진행했더니 전보다 훨씬 더 효율적이였던 것 같다. 좀 더 편하게 대화를 할 수 있었다. 우리는 체스 미션을 큰 고민없이 잘 구현했던 것 같다. 조금 더 효율적으로 작성할 수 도 있었을 것 같으나, 아쉽게도 서로 생각의 한계에 부딪혀서 😂.. 그래도 만족스러웠던 것 같다.
이번에는 어렵고 어색하다고 안써본 것을 피했던 것을 타파하고자 하였다. 블랙잭 미션에서 배웠던 상태패턴도 적용하였다. 크게 구조적으로는 문제가 없었던 것 같다. 전 미션과는 다르게 급하게 하지도 않았다. 그게 제일 만족스러웠다.😀
GitHub 저장소
구현 기능 목록
Piece
Piece 인터페이스 를 만들었다. 클래스 상속이 아닌 인터페이스 상속을 사용했던 이유는 공통된 기능이 있으나, 이 기능들이 기물별로 다르다고 생각하였고 구현체에서 구현하도록 하였다. 그리고 체스 기물들이 모두 공통적으로 하는 기능(움직임 등)이 있는데 이를 구현을 강제하면서 piece들이 같은 동작을 할 수 있도록 하기 위해 사용하였다.
Bishop, King, Queen, Rook, Pawn, Night들을 구현하였다. 이들은 원래 이름과 팀을 가지고 있었다. 하지만 3-4단계에서 불필요하다고 생각되어 PieceType을 가지고 있도록 함으로써 생략하였다.
Point
Point는 좌표로 x좌표로는 file, y좌표로는 rank를 갖는다. 이 좌표는 캐싱을 통해 저장해 두었다.
public static Point of(File file, Rank rank) {
return cache.computeIfAbsent(toKey(file, rank), ignore -> new Point(file, rank));
}
Point는 수직, 수평, 대각, file거리, rank거리, 거리계산 등 piece들이 움직이는데 좌표 범위를 계산하기 위한 메소드들이 있다.
Rank, File enum 또한 캐싱을 사용하였다.
State
이번미션에서 상태패턴을 적용해보고자 했다.
상태는 Ready -> White -> Black -> End 와 같은 방식으로 흐를 수 있도록 상태를 설정하였다.
각 상태들은 isEnd를 판별 메소드와 run 메소드를 가지고 있다.
run메소드는 Command가 start, move, end에 따라 각 다른 행위를 할 수 있도록 진행하였다.
이 상태는 Game에서 setState를 통해 상태의 run메소드를 실행시킴으로써 Game의 State를 변경해가는 방식으로 진행하였다.
밑은 Black상태의 run메소드 이다.
@Override
public State run(Command command, Board board) {
if (command.isStart()) {
throw new IllegalArgumentException("start를 입력할 수 없습니다.");
}
if (command.isMove()) {
board.move(command.getSourcePoint(), command.getTargetPoint(), Team.BLACK);
return new White();
}
if (command.isEnd()) {
return new End();
}
return this;
}
Game
game은 게임의 상태를 가지고 있으며 board를 가지고 있는 역할을 한다. 초기 상태는 Ready상태이다.
Board
Board는 체스판 도메인이다. 좌표와 기물을 가지고 있는 Map을 가지고 있다.
move를 실질적으로 진행한다. 이를 하기 위해서는 여러 검증 단계를 거친다.
소스와 타겟 좌표가 같은지 검증하고, 해당 좌표에 기물이 존재하는지, 해당 팀 기물인지, 타겟좌표가 자기팀 기물인지를 검증 후 루트를 따라 이동한다.
우리의 이동은 예를 들면
비숍이 1,1 에서 3,3으로 간다고 하면 eachFileMove(3-1)/2 eachRankMove(3-1)/2 를 해서 file과 rank를 1씩 증가하면서 움직이도록 하였다.
룩은 1,1 에서 6,1 로 간다고 하면 eachFileMove(6-1)/5 eachRankMove(0)/5 를 해서 file 방향으로 1씩 rank방향으로 0씩 이동하며 움직일수 있도록 하였다.
Command
체스 미션은 Start, End, Move b1 b2와 같은 명령을 입력할 수 있었다. 이를 위해 Command객체를 따로 만들어 관리하였다. 먼저 Start,End를 검증하고 그 후 Move를 검증 하는 방식으로 하였다. Move의 이동 좌표는 따로 List에 저장하여 관리하였다.
궁금한 것
기물이 위치를 갖고 있는 것에 대해서?
기물은 온전한 기물이고, 보드가 위치에 대한 책임을 갖는것은 맞다고 생각한다. 이 부분은 예정되어 있는 요구사항인 DB에 저장에 대해 미리 고민해보고 판단해봐도 좋겠네요.
지금 코드에서 폰이 조금 복잡하게 되었는데 어떻게 하면 효율적으로 작성할 수 있을까 ?
알고계시듯 폰은 실제로 로직이 다른 기물에 비해 복잡해요. 그렇기에 코드도 복잡해질 수 밖에 없습니다. 제가 보기에는 충분히 이해가 잘 되고 테스트도 잘 작성되어 있다고 느껴져요.
InstanceOf 를 사용해서 기물을 판단하는 것과 isPawn, isKnight와 같은 방식으로 처리하는 건 어떤지 ?
사실 Piece가 구현체인 Pawn, Knight의 존재를 알고 있음이 메서드 이름을 통해서 표현되죠. 이는 좋은 방식은 아니라 생각해요. 하지만 그렇지 않을 경우 인스턴스 타입을 체크하는 로직이 필요하구요. PieceType 과 같은 이넘 객체를 만들고 이를 Piece의 필드로 둔 후 이를 이용할 수도 있겠네요. 결국은 PieceType 게터로 노출해야하지만말이죠..😭
코드 리뷰
패키지 분리를 이름이나 형식보다 본질에 집중하면 좋겠다. Factory여도 도메인 로직을 담고있다면 도메인.
예외 메세지 일관된 어조로 수정하면 좋을 것 같다.
상태에 대한 테스트도 필히 테스트 해야한다.
테스트 패키지도 프로덕션 코드와 동일하게 위치하자.
@Nested를 이용하여 DCI패턴을 사용해보자
IllegalArgumentException, IllegalStateException, UnsupportedOperationException 예외를 잘 구별해서 던지자
나가며
이번 체스 미션은 어렵지만 재미있었다. 블랙잭 보다 도메인 난이도가 어렵진 않았던 것 같지만 구현을 어떻게 할지를 고민을 많이 했던 것 같았다. 페어 프로그래밍도 재미있었다. 블랙잭 미션 회고를 보니 정적 팩토리 메서드와, 엔티티와 값객체를 잘 분리해서 작성하고 싶다고 하였는데 이번에 많이 적용해서 구현해봤던 것 같다. 더 열심히 공부해야겠다는 생각밖에 들지 않는다 ㅎㅎ
2단계 체스가 끝나면 방학이다 좀만 더 화이팅하자 !
'우아한테크코스' 카테고리의 다른 글
[우아한테크코스 5기] 레벨1 레벨인터뷰 회고 (0) | 2023.04.06 |
---|---|
[우아한테크코스 5기] 체스 2단계 학습 로그 (0) | 2023.04.02 |
[우아한 테크코스 5기] 블랙잭 2단계 학습 로그 (1) | 2023.03.18 |
[우아한 테크코스 5기] 블랙잭 1단계 학습 로그 (0) | 2023.03.12 |
[우아한 테크코스 5기] 사다리 타기 2단계 학습 로그 (8) | 2023.02.26 |