-
Notifications
You must be signed in to change notification settings - Fork 0
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
[3, 4단계 - 체스] 커찬(이충안) 미션 제출합니다. #21
base: leegwichan
Are you sure you want to change the base?
[3, 4단계 - 체스] 커찬(이충안) 미션 제출합니다. #21
Conversation
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 동안 수고 많았어!!
방학동안 푹쉬고 레벨2도 같이 힘내자💪
turn VARCHAR(10) CHECK (turn IN ('BLACK', 'WHITE')) not null, | ||
PRIMARY KEY (turn) | ||
); | ||
|
||
CREATE TABLE pieces ( | ||
board_file VARCHAR(2) CHECK (board_file IN ('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H')) not null, | ||
board_rank VARCHAR(2) CHECK (board_rank IN ('1', '2', '3', '4', '5', '6', '7', '8')) not null, | ||
type VARCHAR(10) CHECK (type IN ('KING', 'QUEEN', 'ROOK', 'BISHOP', 'KNIGHT', 'PAWN', 'EMPTY')) not null, | ||
team VARCHAR(10) CHECK (team IN ('BLACK', 'WHITE', 'EMPTY')) not null, | ||
PRIMARY KEY (board_rank, board_file) |
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.
테이블 명이나 컬럼명은 소문자로 하고 예약어는 대문자 컨벤션을 유지한거 같은데 not null은 소문자인 이유 궁금하군요!👀
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.
어 그러게요 ㅎ;
대문자로 통일하는게 좋겠네요 ㅎㅎ
private static final InputView INPUT_VIEW = new InputView(); | ||
private static final OutputView OUTPUT_VIEW = new OutputView(); | ||
|
||
public static void main(String[] args) { |
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.
view 주입을 방식을 변경하게 된 이유가 궁금해요
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 static void main(String[] args) {
ConnectionManager connectionManager = new ConnectionManager();
ChessService chessService = createChessService(connectionManager);
InputView inputView = new InputView();
OutputView outputView = new OutputView();
ChessController chessController = new ChessController(chessService, inputView, outputView);
try {
chessController.run();
} finally {
connectionManager.closeConnection();
}
}
private static ChessService createChessService(ConnectionManager connectionManager) {
ChessGameRepository chessGameRepository = new MysqlChessGameRepository(connectionManager);
PieceRepository pieceRepository = new MysqlPieceRepository(connectionManager);
ChessDao chessDao = new ChessDao(chessGameRepository, pieceRepository);
return new ChessService(chessDao);
}
if (command.isEnd()) { | ||
System.exit(0); |
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.
System.exit(0) 를 사용하지 않은 건 좋군요!👍
if (board != null) { | ||
throw new IllegalStateException("보드가 이미 초기화 되었습니다."); | ||
} |
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.
board != null 대신 제일 초기에 빈 board를 넣어준다음 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.
-
그냥 아마도 Board를 그냥 처음부터 만들어버리는 것도 방법이 될 것 같아요.
정말 init() 메서드를 외부에서 실행 해주어야 하나 고민이네요;; -
그렇게 되면 빈 보드의 역할이 너무 모호해질 것 같아요. 다른 메서드는 다 되지 않고, 특정 메서드만 가능한 보드가 될 것 같아서 걱정이 되네요.
private static final String OPTION = "?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC"; | ||
|
||
private static final String URL = "jdbc:mysql://" + SERVER + "/" + DATABASE + OPTION; |
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.
사소하긴 하지만 여기의 공백은 필요한 공백이었을까요?
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.
사용해도 되고, 사용하지 않아도 된다고 생각해요.
다른 변수를 구성하기 위한 변수, 실질적으로 메서드에서 사용되는 변수를 나누어 놓은 것도 가시성이 올라가지 않을까요?
|
||
import java.util.Objects; | ||
|
||
public class Point { |
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.
Point 를 record가 아닌 class로 구현한 이유가 궁금해요
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.
도메인 객체니까? 타칸이 테코톡으로 말했던 Record를 사용하는 때를 적용해본 것 같아.
기능 상으로는 문제는 없을 것 같은데, 조금 더 도메인 객체라는 의도를 주고 싶었던 것 같아.
(물론 도메인 패키지 안에 있으니까 상관 없겠지만)
@@ -16,6 +17,9 @@ public final class Pawn extends Piece { | |||
new BlackPawnFirstMovement(), new BlackPawnDefaultMovement(), new BlackPawnDiagonalMovement()); | |||
private static final List<MovementRule> WHITE_MOVEMENT_RULES = List.of( | |||
new WhitePawnFirstMovement(), new WhitePawnDefaultMovement(), new WhitePawnDiagonalMovement()); | |||
private static final Point PIECE_BASIC_POINT = new Point(1.0); | |||
private static final Point PIECE_OVERLAPPED_POINT = new Point(0.5); // TODO 변수명 변경 |
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.
todo 주석이 남아있군요👀
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 final boolean isKing() { | ||
return this.getClass() == King.class; | ||
} | ||
|
||
public final boolean isPawn() { | ||
return this.getClass() == Pawn.class; | ||
} |
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.
Piece는 추상 클래스 즉 부모클래스인데 부모클래스가 자식 클래스를 알고 있어도 괜찮을까요?
괜찮은 의존 방향인가요
요런 관점에서 보면 sealed 클래스가 왜 만들어졌는지도 의문이군요😅
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()
과 같은 메서드를 하위 클래스 전부에서 구현했다면, sealed를 쓰지 않았을 것 같아.
구체 클래스가 무엇인지에 따라 정해지다보니, sealed를 사용했어.
어짜피 실제 인스턴스 클래스에 따라 구분되는 메서드이고, 추가적으로 계속적인 확장이 필수불가결하지 않다면 위와 같은 방법도 괜찮다고 생각해. 하지만 위와 같은 방법은 항상 쓸 수 있는 것은 아니고, 특정 상황에서만 쓸 수 있다고 생각해.
END_GAME(false), | ||
; | ||
|
||
private final boolean isContinue; |
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.
요 boolean 없이 아래에 isContinue 메서드에서
public boolean isContinue() {
return this == PROGRESS;
}
이렇게 구현하는건 어떤가요?
import java.util.List; | ||
import java.util.Map; | ||
|
||
public class MysqlPieceRepository implements PieceRepository { |
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.
나였다면! MySql
과 Mysql
을 고민하다가 MySql
을 사용했을 거 같아!
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.
일반적으로 JSON을 클래스 이름으로 쓸 때, JSONParser
보다는 JsonParser
가 더 가독성이 좋다고 하더라고.
그래서 하나의 프로그램 응용프로그램인 MySql은 MysqlPieceRepository
라고 쓰는게 가독성이 더 좋다고 생각했어
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.
리비! 일찍 리뷰 와있어서 먼저 답변 달아봤어!
내 생각을 적어봤는데, 궁금한 점 있다면 코멘트 달거나 직접 와서 물어봐 줘!
turn VARCHAR(10) CHECK (turn IN ('BLACK', 'WHITE')) not null, | ||
PRIMARY KEY (turn) | ||
); | ||
|
||
CREATE TABLE pieces ( | ||
board_file VARCHAR(2) CHECK (board_file IN ('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H')) not null, | ||
board_rank VARCHAR(2) CHECK (board_rank IN ('1', '2', '3', '4', '5', '6', '7', '8')) not null, | ||
type VARCHAR(10) CHECK (type IN ('KING', 'QUEEN', 'ROOK', 'BISHOP', 'KNIGHT', 'PAWN', 'EMPTY')) not null, | ||
team VARCHAR(10) CHECK (team IN ('BLACK', 'WHITE', 'EMPTY')) not null, | ||
PRIMARY KEY (board_rank, board_file) |
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.
어 그러게요 ㅎ;
대문자로 통일하는게 좋겠네요 ㅎㅎ
private static final InputView INPUT_VIEW = new InputView(); | ||
private static final OutputView OUTPUT_VIEW = new OutputView(); | ||
|
||
public static void main(String[] args) { |
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 static void main(String[] args) {
ConnectionManager connectionManager = new ConnectionManager();
ChessService chessService = createChessService(connectionManager);
InputView inputView = new InputView();
OutputView outputView = new OutputView();
ChessController chessController = new ChessController(chessService, inputView, outputView);
try {
chessController.run();
} finally {
connectionManager.closeConnection();
}
}
private static ChessService createChessService(ConnectionManager connectionManager) {
ChessGameRepository chessGameRepository = new MysqlChessGameRepository(connectionManager);
PieceRepository pieceRepository = new MysqlPieceRepository(connectionManager);
ChessDao chessDao = new ChessDao(chessGameRepository, pieceRepository);
return new ChessService(chessDao);
}
if (board != null) { | ||
throw new IllegalStateException("보드가 이미 초기화 되었습니다."); | ||
} |
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.
-
그냥 아마도 Board를 그냥 처음부터 만들어버리는 것도 방법이 될 것 같아요.
정말 init() 메서드를 외부에서 실행 해주어야 하나 고민이네요;; -
그렇게 되면 빈 보드의 역할이 너무 모호해질 것 같아요. 다른 메서드는 다 되지 않고, 특정 메서드만 가능한 보드가 될 것 같아서 걱정이 되네요.
private static final String OPTION = "?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC"; | ||
|
||
private static final String URL = "jdbc:mysql://" + SERVER + "/" + DATABASE + OPTION; |
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.
사용해도 되고, 사용하지 않아도 된다고 생각해요.
다른 변수를 구성하기 위한 변수, 실질적으로 메서드에서 사용되는 변수를 나누어 놓은 것도 가시성이 올라가지 않을까요?
import java.util.List; | ||
import java.util.Map; | ||
|
||
public class MysqlPieceRepository implements PieceRepository { |
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.
일반적으로 JSON을 클래스 이름으로 쓸 때, JSONParser
보다는 JsonParser
가 더 가독성이 좋다고 하더라고.
그래서 하나의 프로그램 응용프로그램인 MySql은 MysqlPieceRepository
라고 쓰는게 가독성이 더 좋다고 생각했어
|
||
import java.util.Objects; | ||
|
||
public class Point { |
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.
도메인 객체니까? 타칸이 테코톡으로 말했던 Record를 사용하는 때를 적용해본 것 같아.
기능 상으로는 문제는 없을 것 같은데, 조금 더 도메인 객체라는 의도를 주고 싶었던 것 같아.
(물론 도메인 패키지 안에 있으니까 상관 없겠지만)
@@ -16,6 +17,9 @@ public final class Pawn extends Piece { | |||
new BlackPawnFirstMovement(), new BlackPawnDefaultMovement(), new BlackPawnDiagonalMovement()); | |||
private static final List<MovementRule> WHITE_MOVEMENT_RULES = List.of( | |||
new WhitePawnFirstMovement(), new WhitePawnDefaultMovement(), new WhitePawnDiagonalMovement()); | |||
private static final Point PIECE_BASIC_POINT = new Point(1.0); | |||
private static final Point PIECE_OVERLAPPED_POINT = new Point(0.5); // TODO 변수명 변경 |
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 final boolean isKing() { | ||
return this.getClass() == King.class; | ||
} | ||
|
||
public final boolean isPawn() { | ||
return this.getClass() == Pawn.class; | ||
} |
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()
과 같은 메서드를 하위 클래스 전부에서 구현했다면, sealed를 쓰지 않았을 것 같아.
구체 클래스가 무엇인지에 따라 정해지다보니, sealed를 사용했어.
어짜피 실제 인스턴스 클래스에 따라 구분되는 메서드이고, 추가적으로 계속적인 확장이 필수불가결하지 않다면 위와 같은 방법도 괜찮다고 생각해. 하지만 위와 같은 방법은 항상 쓸 수 있는 것은 아니고, 특정 상황에서만 쓸 수 있다고 생각해.
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.
커찬, 안녕하세요! 망쵸입니다~
코드 잘 읽었어요. 항상 드는 생각이지만 커찬의 코드는 깔끔하고 담백하네요.
3, 4단계에 추가된 코드를 위주로 리뷰를 진행했어요. 평소와 같게 감탄하며 코멘트 달 부분이 많지 않았네요.
레벨1 고생하셨어요!! 레벨2도 파이팅 하죠 ✊✊
public class ChessDao { | ||
|
||
private final ChessGameRepository chessGameRepository; | ||
private final PieceRepository pieceRepository; |
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.
dao와 repository의 네이밍의 의도가 궁금해요 💭
repository는 DB에 직접 접근하는 주체, dao는 비즈니스 로직을 품고 있다고 파악되네요 😀
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.
일단 다른 크루들에게 물어봤을 때는 Repository
, Dao
는 크게 구분없이 통용되어 사용되고 있다고 이해했어. 그런데도 구분해서 사용한 이유는 Spring에서 Repository를 사용할 때 보통 한 테이블을 기준으로 사용하는 점을 차용했어. 그리고 Dao는 하나의 트랙젝션의 단위로 묶고 싶어서 이를 분리했어. (지금은 ChessGame
에서 필드를 2개만 사용하기 위해서 ChessGameRepository
, PieceRepository
를 묶어주었다고 생각해도 무방할 것 같아.)
이렇게 DAO를 구성하게 되면 비즈니스 로직을 일부 품고 있게 되는 것은 어쩔 수 없다고 생각해. 지금은 저장하는 로직과 도메인 로직이 완전히 분리되어 관리해주어야 하다 보니... 그래도 "DB에 정상적으로 저장하는 작업 단위"로 묶어주었다는 것에 의의를 둘 수 있지 않을까?
|
||
private static final String SERVER = "localhost:13306"; | ||
private static final String DATABASE = "chess"; | ||
private static final String OPTION = "?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC"; |
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.
DB 서버와 시간 형식을 맞춰보면 좋을 것 같아요! docker 설정 파일에는 ASIA/SEOUL로 되어 있더군요.
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.
이걸 캐치하다니 ㅎㄷㄷ;;
Docker 설정에 대해서도 알아봐야겠네;;
try (PreparedStatement preparedStatement = connection.prepareStatement(query)) { | ||
addBatch(pieces, preparedStatement); | ||
preparedStatement.executeBatch(); |
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.
배치 👍
저도 유사한 방식으로 구현했는데, 확실히 배치하고 싶더라고요!
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.
JDBC에 대해서는 잘 모르는 편이라 '구글신'께 물어봐서 만든 코드야;; 다른 크루 리뷰를 찾아봤더니 Batch를 사용하는 편이 성능이 좋다고 하더라고.
import chess.dto.PieceType; | ||
import chess.dto.TeamType; | ||
|
||
public class PieceEntity { |
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.
커찬이 작성한 entity, dto, domain의 차이가 궁금하네요!
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.
entity : DB에 저장하기 위한 객체 -> DB로 정보를 저장할 때, DB의 정보를 불러올 때 사용하는 객체
dto : 정보 전달을 위한 객체 -> Domain의 일부 정보를 Domain 밖으로 전달하기 위한 객체
domain : 프로그램의 요구사항들을 담고 있는 객체 -> Domain은 최대한 Domain 객체끼리 의존해야 한다.
이런 식으로 정리해서 생각하고 있어. 추가적으로 domain <-> dto, domain <-> entity의 매핑 하는 방법들을 최대한 dto, entity에게 할당하는 형식으로 구성하는 편이야.
private ProgressStatus processTurn() { | ||
GameCommand command = inputView.readCommand(); | ||
if (command.isStart()) { | ||
throw new IllegalArgumentException("이미 게임을 시작했습니다."); | ||
} | ||
if (command.isEnd()) { | ||
System.exit(0); | ||
if (command.isMove()) { | ||
return executeMove(); | ||
} | ||
if (command.isStatus()) { | ||
return executeStatus(); | ||
} | ||
executeMove(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.
if문으로 분기하니, 의도가 명확하게 보여서 좋은 것 같아요 👍
하지만 더 많은 커맨드가 추가된다면 어떨까요? 유지보수 관점에서 어떻게 생각하는지 궁금해요 💭
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~6개로 늘어난다면, 다른 대안을 생각해볼 것 같아.
지금과 같이 생각한 이유는 '특정 커멘드에 따라 행동하는 것은 하나의 파일로 같이 있어야 하지 않을까?'라는 생각이 있었어. 그런데 커멘드가 5~6개로 늘어난다면 지금과 같이 한 파일로 보는 것이 더 힘들것 같네...
private static final Map<TurnType, String> TURN_TO_STING = Map.of( | ||
TurnType.BLACK, "BLACK", TurnType.WHITE, "WHITE"); | ||
private static final Map<String, TurnType> STRING_TO_TURN = Map.of( | ||
"BLACK", TurnType.BLACK, "WHITE", TurnType.WHITE); |
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.
이런 부분을 보면, 커찬 코드는 담백하다는 생각이 들어요.
저는 enum 같은 클래스로 분리할 생각을 하는데 이게 더 명확해 보이네요 👍
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.
많은 사람들이 이 부분을 Enum으로 분리해서 관리하는데, 그것에 대해 의문점이 들었어.
DB에 어떤 텍스트로 저장되는지가 최대한 Repository에 붙어 있어야 하지 않을까 생각했어. 그래서 DB에 저장되는 텍스트가 변경되었을 때, 쉽게 변화에 대응할 수 있으니까.
private static final Map<File, String> FILE_TO_STRING = Map.of( | ||
File.A, "A", File.B, "B", File.C, "C", File.D, "D", | ||
File.E, "E", File.F, "F", File.G, "G", File.H, "H"); | ||
private static final Map<String, File> STRING_TO_FILE = Map.of( | ||
"A", File.A, "B", File.B, "C", File.C, "D", File.D, | ||
"E", File.E, "F", File.F, "G", File.G, "H", File.H); | ||
private static final Map<Rank, String> RANK_TO_STRING = Map.of( | ||
Rank.ONE, "1", Rank.TWO, "2", Rank.THREE, "3", Rank.FOUR, "4", | ||
Rank.FIVE, "5", Rank.SIX, "6", Rank.SEVEN, "7", Rank.EIGHT, "8"); | ||
private static final Map<String, Rank> STRING_TO_RANK = Map.of( | ||
"1", Rank.ONE, "2", Rank.TWO, "3", Rank.THREE, "4", Rank.FOUR, | ||
"5", Rank.FIVE, "6", Rank.SIX, "7", Rank.SEVEN, "8", Rank.EIGHT); | ||
private static final Map<PieceType, String> PIECE_TYPE_TO_STRING = Map.of( | ||
PieceType.KING, "KING", PieceType.QUEEN, "QUEEN", PieceType.ROOK, "ROOK", PieceType.BISHOP, "BISHOP", | ||
PieceType.KNIGHT, "KNIGHT", PieceType.PAWN, "PAWN", PieceType.EMPTY, "EMPTY"); | ||
private static final Map<String, PieceType> STRING_TO_PIECE_TYPE = Map.of( | ||
"KING", PieceType.KING, "QUEEN", PieceType.QUEEN, "ROOK", PieceType.ROOK, "BISHOP", PieceType.BISHOP, | ||
"KNIGHT", PieceType.KNIGHT, "PAWN", PieceType.PAWN, "EMPTY", PieceType.EMPTY); | ||
private static final Map<TeamType, String> TEAM_TYPE_TO_STRING = Map.of( | ||
TeamType.WHITE, "WHITE", TeamType.BLACK, "BLACK", TeamType.EMPTY, "EMPTY"); | ||
private static final Map<String, TeamType> STRING_TO_TEAM_TYPE = Map.of( | ||
"WHITE", TeamType.WHITE, "BLACK", TeamType.BLACK, "EMPTY", TeamType.EMPTY); |
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.
이렇게 길어진다면 분리를 고민해 볼 것 같은데, 커찬은 괜찮다고 판단해서 그대로 두셨겠죠?
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.
우선적으로는 앞의 ChessGameRepository
와 같은 이유로 DB에 저장하는 텍스트를 한 파일에 몰아두었어. 근데 많이 길어지는 건 어쩔 수 없더라고;; 지금까지는 턱걸이로 허용되는 정도라고 생각하고, 이것보다 조금 만 더 많아지면, 이 텍스트를 분리할 필요가 있을 것 같아.
지금의 PieceType
, TeamType
등이 OutputView
에서도 같이 사용하고 있어서 Enum으로 텍스트를 옮길 수 없었어. 아마 다음에는 View에서 사용하는 Enum과 Entity에서 사용하는 Enum을 분리한다면 해당 텍스트를 옮길 수 있을 것 같아.
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.
망쵸! 커찬이야~ 준 리뷰에는 최대한 코멘트를 다는 편인데, 미션하느라 너무 바빠서 방학 기간에 남기게 되었어. 혹시나 궁금한 점 있으면 추가 커멘트 남겨줘도 되!
꼼꼼히 리뷰해줘서 고마워!
private ProgressStatus processTurn() { | ||
GameCommand command = inputView.readCommand(); | ||
if (command.isStart()) { | ||
throw new IllegalArgumentException("이미 게임을 시작했습니다."); | ||
} | ||
if (command.isEnd()) { | ||
System.exit(0); | ||
if (command.isMove()) { | ||
return executeMove(); | ||
} | ||
if (command.isStatus()) { | ||
return executeStatus(); | ||
} | ||
executeMove(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.
커멘드가 5~6개로 늘어난다면, 다른 대안을 생각해볼 것 같아.
지금과 같이 생각한 이유는 '특정 커멘드에 따라 행동하는 것은 하나의 파일로 같이 있어야 하지 않을까?'라는 생각이 있었어. 그런데 커멘드가 5~6개로 늘어난다면 지금과 같이 한 파일로 보는 것이 더 힘들것 같네...
public class ChessDao { | ||
|
||
private final ChessGameRepository chessGameRepository; | ||
private final PieceRepository pieceRepository; |
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.
일단 다른 크루들에게 물어봤을 때는 Repository
, Dao
는 크게 구분없이 통용되어 사용되고 있다고 이해했어. 그런데도 구분해서 사용한 이유는 Spring에서 Repository를 사용할 때 보통 한 테이블을 기준으로 사용하는 점을 차용했어. 그리고 Dao는 하나의 트랙젝션의 단위로 묶고 싶어서 이를 분리했어. (지금은 ChessGame
에서 필드를 2개만 사용하기 위해서 ChessGameRepository
, PieceRepository
를 묶어주었다고 생각해도 무방할 것 같아.)
이렇게 DAO를 구성하게 되면 비즈니스 로직을 일부 품고 있게 되는 것은 어쩔 수 없다고 생각해. 지금은 저장하는 로직과 도메인 로직이 완전히 분리되어 관리해주어야 하다 보니... 그래도 "DB에 정상적으로 저장하는 작업 단위"로 묶어주었다는 것에 의의를 둘 수 있지 않을까?
|
||
private static final String SERVER = "localhost:13306"; | ||
private static final String DATABASE = "chess"; | ||
private static final String OPTION = "?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC"; |
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.
이걸 캐치하다니 ㅎㄷㄷ;;
Docker 설정에 대해서도 알아봐야겠네;;
private static final Map<TurnType, String> TURN_TO_STING = Map.of( | ||
TurnType.BLACK, "BLACK", TurnType.WHITE, "WHITE"); | ||
private static final Map<String, TurnType> STRING_TO_TURN = Map.of( | ||
"BLACK", TurnType.BLACK, "WHITE", TurnType.WHITE); |
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.
많은 사람들이 이 부분을 Enum으로 분리해서 관리하는데, 그것에 대해 의문점이 들었어.
DB에 어떤 텍스트로 저장되는지가 최대한 Repository에 붙어 있어야 하지 않을까 생각했어. 그래서 DB에 저장되는 텍스트가 변경되었을 때, 쉽게 변화에 대응할 수 있으니까.
private static final Map<File, String> FILE_TO_STRING = Map.of( | ||
File.A, "A", File.B, "B", File.C, "C", File.D, "D", | ||
File.E, "E", File.F, "F", File.G, "G", File.H, "H"); | ||
private static final Map<String, File> STRING_TO_FILE = Map.of( | ||
"A", File.A, "B", File.B, "C", File.C, "D", File.D, | ||
"E", File.E, "F", File.F, "G", File.G, "H", File.H); | ||
private static final Map<Rank, String> RANK_TO_STRING = Map.of( | ||
Rank.ONE, "1", Rank.TWO, "2", Rank.THREE, "3", Rank.FOUR, "4", | ||
Rank.FIVE, "5", Rank.SIX, "6", Rank.SEVEN, "7", Rank.EIGHT, "8"); | ||
private static final Map<String, Rank> STRING_TO_RANK = Map.of( | ||
"1", Rank.ONE, "2", Rank.TWO, "3", Rank.THREE, "4", Rank.FOUR, | ||
"5", Rank.FIVE, "6", Rank.SIX, "7", Rank.SEVEN, "8", Rank.EIGHT); | ||
private static final Map<PieceType, String> PIECE_TYPE_TO_STRING = Map.of( | ||
PieceType.KING, "KING", PieceType.QUEEN, "QUEEN", PieceType.ROOK, "ROOK", PieceType.BISHOP, "BISHOP", | ||
PieceType.KNIGHT, "KNIGHT", PieceType.PAWN, "PAWN", PieceType.EMPTY, "EMPTY"); | ||
private static final Map<String, PieceType> STRING_TO_PIECE_TYPE = Map.of( | ||
"KING", PieceType.KING, "QUEEN", PieceType.QUEEN, "ROOK", PieceType.ROOK, "BISHOP", PieceType.BISHOP, | ||
"KNIGHT", PieceType.KNIGHT, "PAWN", PieceType.PAWN, "EMPTY", PieceType.EMPTY); | ||
private static final Map<TeamType, String> TEAM_TYPE_TO_STRING = Map.of( | ||
TeamType.WHITE, "WHITE", TeamType.BLACK, "BLACK", TeamType.EMPTY, "EMPTY"); | ||
private static final Map<String, TeamType> STRING_TO_TEAM_TYPE = Map.of( | ||
"WHITE", TeamType.WHITE, "BLACK", TeamType.BLACK, "EMPTY", TeamType.EMPTY); |
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.
우선적으로는 앞의 ChessGameRepository
와 같은 이유로 DB에 저장하는 텍스트를 한 파일에 몰아두었어. 근데 많이 길어지는 건 어쩔 수 없더라고;; 지금까지는 턱걸이로 허용되는 정도라고 생각하고, 이것보다 조금 만 더 많아지면, 이 텍스트를 분리할 필요가 있을 것 같아.
지금의 PieceType
, TeamType
등이 OutputView
에서도 같이 사용하고 있어서 Enum으로 텍스트를 옮길 수 없었어. 아마 다음에는 View에서 사용하는 Enum과 Entity에서 사용하는 Enum을 분리한다면 해당 텍스트를 옮길 수 있을 것 같아.
try (PreparedStatement preparedStatement = connection.prepareStatement(query)) { | ||
addBatch(pieces, preparedStatement); | ||
preparedStatement.executeBatch(); |
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.
JDBC에 대해서는 잘 모르는 편이라 '구글신'께 물어봐서 만든 코드야;; 다른 크루 리뷰를 찾아봤더니 Batch를 사용하는 편이 성능이 좋다고 하더라고.
import chess.dto.PieceType; | ||
import chess.dto.TeamType; | ||
|
||
public class PieceEntity { |
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.
entity : DB에 저장하기 위한 객체 -> DB로 정보를 저장할 때, DB의 정보를 불러올 때 사용하는 객체
dto : 정보 전달을 위한 객체 -> Domain의 일부 정보를 Domain 밖으로 전달하기 위한 객체
domain : 프로그램의 요구사항들을 담고 있는 객체 -> Domain은 최대한 Domain 객체끼리 의존해야 한다.
이런 식으로 정리해서 생각하고 있어. 추가적으로 domain <-> dto, domain <-> entity의 매핑 하는 방법들을 최대한 dto, entity에게 할당하는 형식으로 구성하는 편이야.
프로그램 실행 방법
-> Docker(Mysql)가 실행되지 않거나 초기 테이블 설정이 되어있지 않다면, 프로그램이 정상적으로 작동하지 않습니다.
구현 시 고려한 점
요구 사항 만족
"돌아가는 쓰레기를 만들자"와 "주어진 프로그래밍 요구 사항을 지킬 수 있는 만큼 지키자"를 목표로 프로그램을 만들었습니다. 메서드 네이밍이 직관적이지 않거나 구조 측면이 과하게 복잡할 수 있습니다. 보이시는 만큼 리뷰 부탁드립니다. ( _ _ )
자동 저장 기능
말이 한 번 움직일 때마다 저장합니다. 입력 받는 중에 예기치 못하게 중단되거나 오류가 발생한다 하더라도 이전의 상황을 그대로 사용할 수 있습니다.
고려하지 못한 점
트랜젝션
따로 트랜젝션을 사용하지 않아서, 동작 중에 오류가 발생하면 DB가 정상적으로 저장되지 않을 수 있습니다.