-
Notifications
You must be signed in to change notification settings - Fork 411
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[1, 2단계 - 체스] 성하(김성훈) 미션 제출합니다. #517
Conversation
이동 기능 매개변수로 Rank,File 별 이동 횟수를 전달받도록 수정
이동 기능 매개변수로 Rank,File 별 이동 횟수를 전달받도록 수정
모든 기물이 Piece를 상속 받았었는데, Pawn/Knight : SpecialPiece 상속 King/Queen/Rook/Bishop : NormalPiece 상속
안녕하세요 제이온! 피드백 주신 부분들 리팩토링 진행했습니다! 리팩토링 진행 전, 진행 후 제이온이 달아놓으신 코멘트에 제 생각이 담긴 답변 및 질문들을 남겼어요!! 시간 나시면 답변 부탁드립니다! 다음은 이번 리팩토링 진행 시 주요 변화점과 질문할 점을 정리한 것을 나열해보겠습니다! 🎯 이번 리팩토링 주요 변화1. 테스트 케이스를 @MethodSource로 많이 추가했습니다!2.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
안녕하세요 성하!
1차 피드백을 잘 반영해 주시고 Piece의 계층을 나누어서 중복 코드를 제거하는 시도는 좋은 연습이 되셨을 것 같습니다 👍
몇 가지 피드백을 남겨뒀으니 반영해 보시고 리뷰 요청 주세요!
PR 본문에 달아두신 질문은 피드백 곳곳에 답변을 해뒀는데, Piece 계층에 관련된 부분만 여기에 별도로 답변드릴게요~
Q. Piece의 상속 구조 변경
기존 Piece의 계층은 2계층이며 한 눈에 코드를 알아보기가 좋았습니다. (중복 코드가 있더라도 말이죠!) 하지만 5계층으로 분리가 되면서 성하께서 사진도 첨부해 주시고 설명까지 달아주신 것을 정독하고 코드도 사진 및 설명과 비교해야 이해가 되었습니다 😥
여기서 알 수 있는 사실은 중복 코드가 있는 것은 불편하다정도지만, 복잡한 추상화를 해 놓으면 코드를 해석하기 어렵다는 것입니다! 한 번 기존 형태로 다시 돌아가 보시고 반드시 계층을 나눠야하는지 고민해 보시면 어떨까합니다.
또한, 지금 코드를 보면 "움직이는 방식"에 따라 Piece에 상속을 받은 체스말들이 있는데, 이렇게 설계가 된다면 Piece는 일반 클래스로 두고 "움직이는 방식"을 인터페이스로 하여 Piece가 필드로 들고 있는 방식이 낫지않을까하는 생각도 드네요 ㅎㅎ 요건 한 번 선택적으로 반영해보세요!
src/main/java/chess/piece/Piece.java
Outdated
return this.position.equals(position); | ||
} | ||
|
||
public Piece move(final Position positionToMove) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저는 비슷한 코드라고는 생각하나 각기 다른 인스턴스를 반환하므로 중복된 코드라고 생각하지는 않았습니다 ㅎㅎ
또한 지금은 move가 인스턴스만 반환하는 로직이지만, 상황에 따라 각 객체가 자유롭게 move()를 정의할 여지를 주기 때문에 더 객체지향적이라고 생각해요~
instanceof는 상위 클래스가 하위 클래스를 명확하게 알고 조작한다는 점도 문제가 되지만, 성능 상에서도 단점이 있습니다. 아래 아티클을 확인해 보세요!
https://tecoble.techcourse.co.kr/post/2021-04-26-instanceof/
public BoardDto(final Board board) { | ||
this.nameBoard = new ArrayList<>(); | ||
initBoardSpaces(); | ||
fillPieceNames(board); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
사실 static과 무관하게 DTO에 로직이 많이 들어가는 건 DTO가 아니라 OutputView나 Controller 단에서 처리할 수 있는 것이 아닌지 고민해 보아야 할 신호이기도 합니다 ㅎㅎ
우선 static을 붙이는 것 자체는 정답을 드리기 보다는 아래 아티클을 소개해 드릴게요!
https://tecoble.techcourse.co.kr/post/2020-07-16-static-method/
https://steady-coding.tistory.com/603
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
public class BoardDto { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DTO는 왜 필요한 걸까요?
누가 필요로하길래 이러한 객체로 따로 포장해서 전달하는지 생각해 보시면 해답이 나오지 않을까 합니다~
public enum Name { | ||
KING("k", "K"), | ||
QUEEN("q", "Q"), | ||
BISHOP("b", "B"), | ||
KNIGHT("n", "N"), | ||
ROOK("r", "R"), | ||
PAWN("p", "P"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아, 이건 성하 의견이 맞습니다. 제가 체스 요구 사항에 대해 조금 착각을 했네요 ㅎㅎ
출력을 위해 사용되는 로직은 도메인이 가지지 않는 것이 좋습니다.
안녕하세요! 제이온! 2차 리팩토링 진행했습니다 👍 제이온의 1차 피드백 코멘트대로 상속 구조를 변경한 후에 다이어그램을 보고 처음 든 생각은 '복잡하다..' 였어요! 계층이 2,3,4 계층 정도이고 묶이는 클래스들이 여러 개라면 정말 효과적으로 중복을 제거할 수 있다는 생각이 들었어요!
이 부분은 시간상 다음에 해보도록 하겠습니다!! 뭔가 할 수 있을 것 같은데 지금 당장은 안되네요 ㅠㅠ 이번 2차 리팩토링도 어떤 것을 리팩토링했는지 나열해보겠습니다!! 🎯 2차 리팩토링 변경점1. 상속 구조 원래대로 변경2.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
안녕하세요 성하!
잘 계층화된 구조에서 원래 구조로 돌아오면서 많은 것을 배우고 느끼셨을 것 같아요~
나머지는 소소한 피드백이거나 다음 단계에서 진행해보면서 반영해도 괜찮은 내용이라 이만 머지할게요 😁
public abstract boolean isMovable(Position targetPosition); | ||
|
||
public boolean isSamePosition(Position position) { | ||
return this.position.equals(position); | ||
} | ||
|
||
public abstract Piece move(final Position positionToMove); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isMovable과 move를 합쳐보는 건 어려울까요?
느낌상 move 안에 isMovable이 들어가는 것이 적절해 보여서요~!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isMovable()
메소드로 Board가 해당 기물이 갈 수 있는지 판단을 한 후에
해당 기물이 move()
로 이동하도록 Board에서 관리하기 때문에 두 가지 행위를 분리했던 것 같아요!!
public boolean isCorrectTurn(Side side) { | ||
return this.side == side; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
그렇긴한데, 저는 getter 정도는 유도리있게 열어주는 편이긴 합니다 ㅎㅎ
checkPieceMovable(targetPosition, sourcePiece); | ||
checkPath(targetPosition, sourcePiece); | ||
checkTargetPosition(targetPosition, sourcePiece); | ||
if (sourcePiece.isNeedToCheckWhenDiagonalMove()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isPawn()
을 썼을 때 계층이 드러나는 것은 맞으나, 체스 게임이라는 규칙상 예외적으로 Pawn이라고 명시했을 때 코드를 읽기 더 좋은 것 같습니다~
실제로 isNeedToCheckWhenDiagonalMove()
메서드를 봤을 때, "Diagonal이 대각선이니 대각선으로 움직이는 말인지 확인하는 건가? 그러면 퀸, 비숍, 폰 등이 있겠네?"라고 오해할 여지가 있더라고요 ㅎㅎ
public class NameBoard { | ||
|
||
private static final Map<Class<? extends Piece>, Name> nameByPiece = new HashMap<>(); | ||
private static final int BOARD_ROW_SIZE = 8; | ||
|
||
private final List<List<String>> nameBoard; | ||
|
||
static { | ||
nameByPiece.put(King.class, Name.KING); | ||
nameByPiece.put(Queen.class, Name.QUEEN); | ||
nameByPiece.put(Bishop.class, Name.BISHOP); | ||
nameByPiece.put(Knight.class, Name.KNIGHT); | ||
nameByPiece.put(Rook.class, Name.ROOK); | ||
nameByPiece.put(Pawn.class, Name.PAWN); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저도 이 친구는 controller 패키지가 적합해보이네요~
내부 로직 까보면 도메인을 살짝 이용하고 있고, static 블록으로 도메인을 쓰고 있으면서 전체적인 핵심 로직은 View를 위한거니까요!
@Override | ||
public List<Position> getPaths(final Position targetPosition) { | ||
final Direction direction = position.getDirectionTo(targetPosition); | ||
final int moveCount = position.getMoveCount(targetPosition, direction); | ||
|
||
if (isStartPosition() && moveCount == INITIAL_MOVE_COUNT) { | ||
return List.of(position.getNextPosition(direction)); | ||
} | ||
return Collections.emptyList(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여기서 발생하는 중복 코드는 다음 단계 진행해보면서 리팩터링해 보면 좋을 것 같습니다~
일단은 이대로 두고 가시죠!
안녕하세요! 제이온!
백엔드 5기 크루 성하입니다!!
이번 미션은 너무 설계 고민이 많아서 미션이 하루 늦춰졌음에도 만족스럽지 못한 코드가 나온 것 같아요.
그래서 급하게 설계 및 구현하느라 고민해야 할 점들을 많이 못 적어놔서 아쉬운 것 같습니다 ㅠㅠ 😭
원래 목표는 다이어그램 이미지도 만들고, 주요 설계점들을 적어 놓으려고 했는데
그런 부분은 추후에 리팩토링 시에 추가해보도록 하겠습니다.
질문할 부분은
Piece
부분에서 추상 클래스의 인스턴스화가 불가능해서 생긴 문제가 있었어서instanceof를 사용했는데, 해당 부분은 코드에 코멘트로 남겨두도록 하겠습니다!
설계 시 오랜 시간 고민했던 점이 있어서 아래에 질문 남기겠습니다!
🎯 Position을 어느 객체가 알게 할 것인가?
처음에 Position을 Board가 알게 할지, Piece가 알게 할지 고민이었습니다.
저는 객체가 자율성을 가져야 한다고 생각하여 Piece가 Position을 알고 이동하는 객체로 설계하였습니다.
Board가 Position을 알게 되면 Piece는 진영만 가지는 빈 껍데기 객체가 되는 것 같았습니다.
이렇게 하다보니 Board가 64칸의 실제 보드가 아닌,
List<Piece>
로 Piece들을 가지는 주머니 느낌이 되어이상하다는 느낌을 받았습니다.
이러한 근거를 가지고 설계한 저의 생각이 괜찮은 건지 궁금합니다!