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단계 - 체스] 기론(김규철) 미션 제출합니다. #295

Merged
merged 53 commits into from Apr 1, 2022

Conversation

Gyuchool
Copy link

안녕하세요 토니!
기론이라고 합니다!! 😎 이번 체스미션은 블랙잭보다 난이도가 많~이 뛰어서 체감상 어려웠습니다.😂😂
아직 아쉬운 코드들이 있는데 어떻게 해야 더 클린한 코드가 될지 고민이네요.
이번 체스 미션 리뷰 잘 부탁 드립니다!

감사합니다.

@Gyuchool Gyuchool changed the title [1, 2, 3단계 - 체스] 루키(최지훈) 미션 제출합니다. [1, 2, 3단계 - 체스] 기론(김규철) 미션 제출합니다. Mar 28, 2022
Copy link

@toneyparky toneyparky left a comment

Choose a reason for hiding this comment

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

안녕하세요 기론!

체스 게임의 리뷰어 토니에요.
작성해주신 코드 잘 확인했어요. 꾸준하게 리팩터링하여 발전하려는 모습이 눈에 보였습니다!

몇가지 리뷰를 남겼으니 확인하고 수정하여 더 이해하기 좋은 코드로 발전시켜보죠!
질문에 대해 대댓글 남겨주시구 DM 은 언제든 환영이에요!

화이팅입니다!

@@ -0,0 +1,18 @@
version: "3.9"

Choose a reason for hiding this comment

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

docker-compose는 어떤 의도에서 추가하셨는지 궁금해요~!

Copy link
Author

Choose a reason for hiding this comment

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

아 docker-compose는 오늘 수업 중에 실습 했던 내용입니다!
깃에 추가하고 싶은 사람은 추가해도 된다고 해서 추가했었습니다!

Choose a reason for hiding this comment

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

오호 그런 배경이 있었군요!

해당 기술을 잘 익혀두시면 추후에 프로젝트할 때나 현업에 와서도 잘 활용하실 수 있을거에요!

Comment on lines 33 to 37
if (canMove(targetPosition, sourcePiece, targetPiece)) {
move(sourcePosition, targetPosition, sourcePiece, targetPiece);
return;
}
throw new IllegalArgumentException("움직일수 없습니다.");

Choose a reason for hiding this comment

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

if문 내에서 예외를 던지를 처리를하고 그렇지 않을 경우에 그냥 리턴을 하도록 바꾼다면 지금과 어떤 차이가 있을까요?

Copy link
Author

Choose a reason for hiding this comment

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

if문 내에서 예외를 던지도록 하면 validate와 같이 메서드로 뺄수 있겠군요!
또한 if문에 걸려서 예외가 발생하면 어떤 상황에서의 예외인지 알 수 있어서 디버깅할때도 좋을 것 같습니다!

Choose a reason for hiding this comment

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

좋네요! 코드도 확인했는데 훨씬 더 깔끔해졌어요!

Comment on lines 11 to 21
if (input.contains("move")) {
throw new IllegalArgumentException("시작 명령어에는 move가 들어갈수 없습니다.");
}
if ("start".equals(input)) {
return new Start(input);
}
if ("end".equals(input)) {
return new End(input);
}
throw new IllegalArgumentException("command has only move or end ");
}

Choose a reason for hiding this comment

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

예외 메시지에 영어와 한글을 섞어서 사용하신 이유가 궁금해요 😁

Copy link
Author

Choose a reason for hiding this comment

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

큰 이유는 없어요...ㅎㅎ😅
페어와 진행하면서 여기에 예외 던지면 의견이 나왔었고 에러 메시지로는 생각나는대로 적어둔 것이었습니다!
통일성을 위해 한글로 수정할게요!

Choose a reason for hiding this comment

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

깔끔하게 수정해주셨네요! 굳!


@Override
public List<String> getCommandPosition() {
throw new IllegalArgumentException("명령어에서 위치를 얻을수 없습니다.");

Choose a reason for hiding this comment

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

IllegalArgumentException는 어떤 경우에 발생하는 예외일까요?

지금 상황을 보다 적절하게 표현할 수 있는 예외가 있을까요~!?

Copy link
Author

Choose a reason for hiding this comment

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

이펙티브 자바에 의하면

  • IllegalArgumentException은 허용하지 않는 인수가 들어오는 경우
  • IllegalStateException은 객체가 메서드를 수행하기에 적절하지 않은 상태인 경우
  • UnsupportedOpertionException 은 호출한 메서드를 지원하지 않을 때 사용한다고 하네요.

IllegalStateExceptionUnsupportedOpertionException 중 어느 것이 더 적합할지 고민이었는데 아래 stackoverflow글의 도움을 받아서 현재는 재정의된 상태이므로 IllegalStateException을 사용하도록 수정하겠습니다!

IllegalStateException vs UnsupportedOpertionException

Choose a reason for hiding this comment

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

좋아요 기론!

자바의 예외 체계는 잘 구성되어 있기에 적절한 예외를 선택해서 사용한다면 예외 상황을 마주했을 때에 좋은 힌트가 될거에요!

import chess.view.OutputView;

public class ChessController {
public void run() {

Choose a reason for hiding this comment

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

코드 리뷰를 하는 동안 해당 클래스의 길이가 줄어들었네요!

메서드 분리 적절하게 잘 하신거 같아요 👍

Comment on lines 29 to 33
private static void makeNewLine(Piece piece) {
if (piece.isLastFile()) {
System.out.println();
}
}

Choose a reason for hiding this comment

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

Piece의 메서드를 사용하시군요! OutputView와 도메인이 결합되어 있어보이네요~!

Copy link
Author

Choose a reason for hiding this comment

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

dto 사용으로 결합을 끊었습니다!

Choose a reason for hiding this comment

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

넵~! 좋아요.

콘솔이 아닌 웹뷰를 적용하실텐데 도메인과의 연결을 끊는 연습을 했다고 생각합니다!

.orElseThrow(() -> new IllegalArgumentException("해당 위치에 말이 없습니다."));
}

public long countOfKing() {

Choose a reason for hiding this comment

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

해당 메서드에 대한 테스트가 빠져있군요!
다른 클래스에도 기론이 생각하기에 필요한 테스트가 빠져 있다면 추가해보아요~!

}

@Test
@DisplayName("체스판에 남은 말들의 점수를 계산한다. - 폰이 전부 가로로 있을 때")

Choose a reason for hiding this comment

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

이름만 봐서는 폰 이외의 다른 말에 대해서도 테스트한다는 내용을 파악하기가 조금 힘들었어요~!

기론이 테스트하고 싶은 내용에 포커스를 맞춰서 수정해보면 더 명확한 테스트가 될 거 같아요!

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

public class CommandTest {

Choose a reason for hiding this comment

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

테스트 클래스는 프러덕션 코드의 클래스와 같은 패키지 선상에 위치해야합니다!

또한 내용은 Command보다는 Init과 End에 대해서 테스트하는 것으로 보여져요!

import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;

class KingTest {

Choose a reason for hiding this comment

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

테스트를 꼼꼼하게 잘 작성해주셨네요! 좋습니다 👍

@@ -12,6 +12,7 @@ dependencies {
implementation 'com.sparkjava:spark-core:2.9.3'
implementation 'com.sparkjava:spark-template-handlebars:2.7.1'
implementation 'ch.qos.logback:logback-classic:1.2.10'
runtimeOnly 'mysql:mysql-connector-java:8.0.28'

Choose a reason for hiding this comment

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

DB가 붙는군요! 설레네요(?)

여기서 한가지 퀴즈~!

runtimeOnly와 implementation 그리고 testImplementation는 각각 어떤 의미로 build.gradle에서 사용될까요? 댓글로 남겨주세요!

Copy link
Author

Choose a reason for hiding this comment

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

오 퀴즈는 언제나 설레네요 ㅎㅎㅎ
퀴즈 공부하면서 grdle의 의존성 옵션에 대해서 알아볼 수 있었습니다!
runtimeOnly

  • runtimeClassPath에만 추가된다.
  • 해당 클래스에서 코드 변경이 발생해도 런타임에만 불러오기 때문에 컴파일을 다시 할 필요가 없다는 장점이 있습니다.

implementation

  • A->B->C 모듈 순서로 의존성이 있을 때, A에서 B를 implementation으로 설정하면 B에 대한 모듈만 가져오고 C에 대해서는 가져오지 않습니다!
  • 따라서 C가 수정되었더라도 A를 다시 컴파일할 필요가 없습니다.

testImplementation

  • implementation이 테스트코드를 수행할 때만 적용한다.

와 같이 정리했습니다!
정리글

Choose a reason for hiding this comment

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

정리 잘 해주셨네요! 좋습니당 블로그 글도 확인했어요 기론은 글도 잘 작성하시는군요 👍


public final class Board {

private final Pieces pieces;

Choose a reason for hiding this comment

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

체스판을 구현하는 여러가지 방법이 있을텐데요. 기론은 자신의 위치에 해당하는 정보를 들고 있는 각 기물들의 모음을 체스판으로 봤어요.

다른 방식으로 구현한다면 8X8의 체스판 자체를 이중 리스트나 맵 형식으로 구조화할 수도 있겠죠!

기론이 기론의 방식으로 구현하며 느낀 장점이나 단점이 있을까요? 다른 방식으로 구현한다면 어떤 장단점이 있을지도 상상해보면 재미있겠네요!

Copy link
Author

@Gyuchool Gyuchool Mar 30, 2022

Choose a reason for hiding this comment

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

처음엔 Map<Position, Piece>를 갖는 board를 만들었습니다. 그런데 구현을 진행하면서 페어와 board가 가지고 있는 상태에 대해서 이야기를 했는데, board에서 위치와 말을 모두 알아야하나? 라는 생각을 먼저 했습니다.

boardpiece를 움직여서 게임하는 것이므로 piece들만 관리하는 게 더 맞지 않을까 라고 생각하였고 boardpiece만 알고 piece가 자신의 위치를 가지고 있도록 구현하였습니다.

또한 boardpieceposition모두를 관리하지 않고 piece들만 관리하면 되기 때문에 결합도가 더 낮아지지않을까? 라고 생각했습니다.

장점으로는 boardpieces를 가지면서 board클래스 내에서 하는 일이 많이 줄어들었습니다. 대부분 piece들이 움직이므로 책임의 대부분이 pieces 또는 piece로 갔어요!
단점으로는 체스판을 출력해줄 때, piece의 위치를 매번 확인해서 마지막 file에 있는 자리인지 확인해야 한다는 단점이 있었습니다.

아마 기존의 Map방식으로 사용한다면
Map의 자료구조 특성상 요소의 추가 삭제가 List보다 성능이 나을 때가 많고 검색 성능은 더 좋아서 성능상에선 더 좋을 것 같네요🤔
대신 제 생각에는 객체의 행위에 집중하면 boardposition까지 알 필요가 있나 라고 생각되어서 이게 단점인 것 같습니다.

생각하면 생각할수록 어렵네요🤔🤔 각각 트레이드오프가 있는 것 같습니다.
토니였다면 어떤 선택을 하셨을까요?

또한 토니는 제 생각에 대해서 어떻게 생각하시나요?

객체의 행위에 집중하면 boardposition까지 알 필요가 있나?

boardpiece들을 가지고 게임하므로 position은 알 필요가 없다고 생각해서 나온 이 생각이 정말 객체의 행위에 집중한 게 맞을까요?
오브젝트 책의 내용에 나온 행동을 바탕으로 설계하는 것을 적용해보려고 하는데 읽는 것과 실제 적용하기에는 너무 어렵네요😂😂

Choose a reason for hiding this comment

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

크... 많은 고민을 하셨군요 기론! 상세하게 적어주셔서 이해하는 데에 도움이 많이 되었어요 감사합니다! 페어와 함께 고민하셨다는 부분 너무 좋네요.

항상 개발을 하다보면 트레이드 오프가 있죠! 체스 게임이 엄청 큰 서비스가 아니기에 성능적인 고려보다는 코드를 이해하기 좋은 방식으로 구성할거 같아요. 지금 기론의 방식은 좋은 접근이라 생각해요. 각 piece를 자신의 위치도 관리하는 주체적인 객체라고 정의한다!


public class ConsoleApplication {
public static void main(String[] args) {
ChessController chessController = new ChessController();

Choose a reason for hiding this comment

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

현재의 구조에서는 게임을 진행하다가 실수할 경우에 체스 게임이 바로 종료되어요 😂

요구사항에 명시되지 않았지만 프러덕트를 개발하는 개발자로서 사용자가 실수하더라도 적절한 메시지를 노출한 후에 이어서 진행할 수 있도록 수정해보는건 어떨까요?

image

Copy link
Author

Choose a reason for hiding this comment

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

명짤(?)이네요.. 저장해둬야겠습니다😁수정했습니다!

@@ -0,0 +1,18 @@
version: "3.9"

Choose a reason for hiding this comment

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

오호 그런 배경이 있었군요!

해당 기술을 잘 익혀두시면 추후에 프로젝트할 때나 현업에 와서도 잘 활용하실 수 있을거에요!

Copy link

@toneyparky toneyparky left a comment

Choose a reason for hiding this comment

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

리뷰 반영 잘 해주셨어요 기론!

한가지 퀴즈(?)와 코멘트를 남겼으니 확인 후 반영 부탁드려요!

이야기 나누고 싶은 부분이 있다면 DM주셔도 좋아요~!

@@ -15,8 +20,11 @@ private Position(File file, Rank rank) {
public static Position of(char... position) {
File file = File.valueOf(position[0] - ASCII_TO_INT);
Rank rank = Rank.valueOf(Character.getNumericValue(position[1]));
return CACHE.computeIfAbsent(createKey(file, rank), absent -> new Position(file, rank));

Choose a reason for hiding this comment

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

요 메서드는 저도 배워가네요! 굳굳

command = command.turnState(input);
return command;
} catch (IllegalArgumentException e) {
System.err.println(e.getMessage());

Choose a reason for hiding this comment

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

에러에 대한 출력도 출력을 담당하는 객체가 수행하도록 수정해볼까요?

@@ -12,6 +12,7 @@ dependencies {
implementation 'com.sparkjava:spark-core:2.9.3'
implementation 'com.sparkjava:spark-template-handlebars:2.7.1'
implementation 'ch.qos.logback:logback-classic:1.2.10'
runtimeOnly 'mysql:mysql-connector-java:8.0.28'

Choose a reason for hiding this comment

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

정리 잘 해주셨네요! 좋습니당 블로그 글도 확인했어요 기론은 글도 잘 작성하시는군요 👍

Copy link

@toneyparky toneyparky left a comment

Choose a reason for hiding this comment

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

안녕하세요 기론 😊

제가 남긴 리뷰에 대해 답변을 잘 해주셨네요! 더 나아가 캐싱까지 적용하신 점도 인상깊었어요.
이번 단계는 머지해도 충분할거 같아요. 다음 단계로 나아가보죠!

고생 많으셨습니다.

@toneyparky toneyparky merged commit 5c860cb into woowacourse:gyuchool 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.

None yet

2 participants