Skip to content
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, 3단계 - 체스] 후디(조동현) 미션 제출합니다. #293

Merged
merged 45 commits into from
Apr 1, 2022

Conversation

devHudi
Copy link

@devHudi devHudi commented Mar 28, 2022

안녕하세요 에단! 만나서 반갑습니다.

당연하겠지만, 지금까지의 미션 중 가장 어려운 미션이었던 것 같아요.
그럼에도 불구하고 지금까지 학습한 여러 가지를 적용해볼 수 있어서 가장 재밌었던 미션이기도 했습니다.

여러 고민사항이나 질문은 별도의 코멘트로 남길게요. 감사합니다 에단 😄

아쉬운 점

코드

특히 게터를 사용하지 않기, 한줄에 점 하나만 찍는 디미터 법칙을 지키기, 메소드 라인 제한 세가지가 미흡한 상황인 것 같습니다.
추가로 체스 말이 움직이는 다양한 경우의 수에 대해 테스트가 다소 부족하다는 생각이 들어요. 이 부분들은 두번째 피드백 요청까지 깔끔하게 해결해보겠습니다. 😓

기능 구현 목록

더하여, 기능 구현에 집중하다보니 기능 구현 목록작성이 많이 미흡해서 아쉽습니다. 페어와 함께 3단계까지 구현하고나서야 기능 구현 목록을 작성하지 않은 것을 알아챘네요. 4단계와 5단계는 신경써서 작성해보겠습니다. 🥲

devHudi added 21 commits March 23, 2022 10:25
- 테스트 코드 작성
- XAxis, YAxis 추가
- 테스트 코드 작성
- 불필요한 생성 테스트 제거
- Night 를 Knight 로 변경
- Cell 클래스 제거
- Piece 인터페이스 제거
Comment on lines 16 to 21
public class Board {
private final Map<Position, AbstractPiece> value;

private Board(Map<Position, AbstractPiece> value) {
this.value = value;
}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

빈 칸을 표현하기 위한 '빈 말' 클래스를 piece 패키지에 구현할지, 혹은 그냥 빈칸에 대한 도메인은 구현하지 않을지 고민했어요.

위 코드와 같이 말과 말의 위치를 한쌍으로 묶기위해 Board 라는 클래스에서 Map 자료구조를 사용했는데, 현재는 '빈 말' 도메인을 구현하지 않고, 비어있는 칸은 아무 값도 넣어두지 않았습니다. 따라서 get 을 호출하면 null 이 반환되는데, 외부에서 NPE 가 발생하는 것을 방지하기 위해 게터에는 Optional 로 한번 감싸서 반환하였습니다.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

null보다는 Optional이 훨신 명시적으로 보이네요 👍

Copy link

@Laterality Laterality left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

안녕하세요 후디,

리뷰어 에단입니다 :)

구현하시느라 고생하셨어요.

몇가지 간단한 코멘트를 남겨드렸으니 확인 부탁드려요.

src/main/java/chess/controller/ChessController.java Outdated Show resolved Hide resolved
src/main/java/chess/controller/ChessController.java Outdated Show resolved Hide resolved
src/main/java/chess/domain/board/MoveResult.java Outdated Show resolved Hide resolved
src/main/java/chess/domain/game/gamestate/FinishedEnd.java Outdated Show resolved Hide resolved
src/test/java/chess/domain/piece/BishopTest.java Outdated Show resolved Hide resolved
src/test/java/chess/domain/piece/KingTest.java Outdated Show resolved Hide resolved
src/test/java/chess/domain/piece/NightTest.java Outdated Show resolved Hide resolved
src/main/java/chess/domain/piece/Bishop.java Outdated Show resolved Hide resolved
devHudi added 18 commits March 31, 2022 12:50
- Score에 add, subtract 메소드 추가
- ScoreResult의 점수 계산로직 리팩토링
- OutputView가 ScoreResult를 받아 출력하도록 변경
- BoardDto 패키지 이동 (dto -> dto.board)
- DTO 구현에 따른 컨트롤러, 뷰 코드 변경
- Position에 Position 두개를 전달하여 각도를 반환하는 메소드 구현
- Direction에 Position 두개를 전달하여 방향을 반환하는 메소드 구현
- DirectionStrategy 제거
- MovingStrategy에 Direction 적용
- 사용되지 않는 Position의 메소드 제거
- 폰 움직임 테스트 추가
- Board 리팩토링
- Direction에 isVertical, isHorizontal, isDiagonal 추가
- XAxis 테스트 코드 추가
- YAxis 테스트 코드 추가
@devHudi
Copy link
Author

devHudi commented Apr 1, 2022

안녕하세요, 에단! 두번째 리뷰요청이 많이 늦었죠 😓
늦게 드리는 리뷰요청인만큼 정말 많은 부분이 변경되었는데요, 간략하게 정리하면 아래와 같아요.

변경 사항

  1. 각 기물이 움직이는 전략을 객체로 분리하였습니다. (movingstrategy)
  2. 점수를 나타내는 도메인인 ScoreScoreResult 를 만들었습니다.
  3. 게임 진행의 상태를 나타내는 도메인인 game.state 의 구조를 간단하게 변경했어요.
  4. 말의 이동방향을 Position에서 계산하지 않고, Direction이라는 도메인을 별도로 만들어 관리하도록 했습니다.
    • Position 에서는 두점이 주어졌을 때 각도만 계산하도록 변경했어요.
    • Direction 을 기물의 움직임 전략에 사용했어요.
  5. BoardDto 가 체스판에 대한 정보만 갖고, 출력로직을 갖지 않도록 변경했어요.
  6. 뷰와 컨트롤러에서만 사용되는 ConsoleCommandDto 를 만들었어요. 사용자가 콘솔에 명령을 입력할 때 사용되는 Dto 입니다.
  7. PieceDto 도 추가하였습니다.

개선해야할 점

더 개선하다보면 피드백 텀이 너무 길어질 것 같아서 아직 아래의 내용은 개선하지 못했어요.

  1. 디미터 법칙 지키기
  2. 메소드 라인 및 뎁스 제한 사항
  3. 매직넘버가 다수 존재

기타 더 궁금한 내용은 코멘트로 달아보겠습니다! 감사해요.

Comment on lines +36 to +47
private void executeCommandIfNotEnd(ChessGame chessGame, ConsoleCommandDto commandDto) {
if (commandDto.isStart()) {
executeStart(chessGame);
}
if (commandDto.isMove()) {
executeMove(chessGame, commandDto);
}
if (commandDto.isStatus()) {
executeStatus(chessGame);
}
requestCommand(chessGame);
}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

많은 if 문을 없애기 위해서, 컨트롤러 쪽에 ConsoleCommandDto 등을 전달받아 알맞은 명령을 실행하는 클래스를 별도로 만들어볼까 고민을 했었는데, 컨트롤러가 너무 많은 클래스를 갖게 될까봐 반영은 하지 않았어요.

아직 if 문의 반복을 줄이는 뾰족한 방법이 생각나지 않는데, 에단의 의견이 궁금합니다!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

생각하신 방법대로 한번 구현해보시면 어떨까요?

public class PieceDto {

private final PieceNotation pieceNotation;
private final PieceColor pieceColor;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PieceDto 에서 도메인인 PieceColorPieceType 을 알고있고, 사용하고 있어요. DTO 에서 도메인을 알고 있는 게 좋지 않은 것은 알고있지만, Enum 은 예외라고 생각합니다. 이 구조에 대한 에단의 의견이 궁금합니다.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

왜 enum은 예외라고 생각하시나요?

Copy link

@Laterality Laterality left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

안녕하세요 후디,

피드백이 많았는데 잘 수정해주셨어요 👍

몇가지 피드백을 추가로 남겨드렸는데, 시간 관계상 이 단계는 이만 머지하도록 할게요.

남겨드린 피드백은 다음 미션 진행하면서 참고해주세요.

고생 많으셨습니다 :)

public class PieceDto {

private final PieceNotation pieceNotation;
private final PieceColor pieceColor;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

왜 enum은 예외라고 생각하시나요?

Comment on lines +36 to +47
private void executeCommandIfNotEnd(ChessGame chessGame, ConsoleCommandDto commandDto) {
if (commandDto.isStart()) {
executeStart(chessGame);
}
if (commandDto.isMove()) {
executeMove(chessGame, commandDto);
}
if (commandDto.isStatus()) {
executeStatus(chessGame);
}
requestCommand(chessGame);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

생각하신 방법대로 한번 구현해보시면 어떨까요?

src/main/java/chess/domain/game/gamestate/State.java Outdated Show resolved Hide resolved
Comment on lines 16 to 21
public class Board {
private final Map<Position, AbstractPiece> value;

private Board(Map<Position, AbstractPiece> value) {
this.value = value;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

null보다는 Optional이 훨신 명시적으로 보이네요 👍

requestCommand(chessGame);
}

private void requestCommand(ChessGame chessGame) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

requestCommand ->executeCommandIfNotEnd -> requestCommand... 처럼 메서드가 재귀적으로 실행되는데요.

requestCommand가 재귀함수지만 재귀 호출부가 다른 메서드에 있어서 메서드의 호출 흐름을 파악하기 어렵게 만드는 걸로 보여요. requestCommand의 재귀 호출은 가급적 같은 메서드에서 처리하면 어떨까요?

return new ConsoleCommandDto(Command.from(command), arguments);
}

public boolean isStart() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

커맨드가 추가될 때마다 isXX 메서드가 계속 추가될까요?

@Laterality Laterality merged commit 9d82b82 into woowacourse:devhudi Apr 1, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants