-
Notifications
You must be signed in to change notification settings - Fork 415
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단계 - 체스] 망고(고재철) 미션 제출합니다. #437
Merged
+2,729
−58
Merged
Changes from all commits
Commits
Show all changes
47 commits
Select commit
Hold shift + click to select a range
f819e70
docs: 기능 명세 문서 작성
shin-mallang 8632943
feat: 체스 기물 생성
shin-mallang ebfe4a6
feat: 체스 기물에 색상 추가
shin-mallang efc16dd
feat: 체스 위치 추가
shin-mallang 490bd32
refactor: Piece에서 PiecePosition 사용
shin-mallang 8f786e1
refactor: Piece 의 역할을 enum으로 관리
shin-mallang d938d00
feat: ChessBoard 생성
shin-mallang b1b56a2
feat: 체스판 뷰 구현
shin-mallang d0674d0
refactor: Piece를 추상 클래스를 통해 구현하도록 변경
shin-mallang 78a2104
feat: 1단계 뷰 구현 완료
shin-mallang d6cfaa0
feat: 방위를 나타내는 Direction 구현
shin-mallang 03cea30
feat: PiecePosition에 기능 추가
shin-mallang 2ecd841
feat: Path 구현
shin-mallang 04a9fe7
feat: Piece 에 경로 탐색을 위한 템플릿 메서드 작성
shin-mallang ff4781a
feat: King 의 이동 조건 탐색 기능 구현
shin-mallang 4cc1eb6
feat: Bishop 의 이동 조건 탐색 기능 구현
shin-mallang 44335df
feat: Rook 의 이동 조건 탐색 기능 구현
shin-mallang c9e4051
feat: Queen 의 이동 조건 탐색 기능 구현
shin-mallang d50d83d
feat: Pawn 의 이동 조건 탐색 기능 구현
shin-mallang 585e209
feat: Knight 의 이동 조건 탐색 기능 구현
shin-mallang db7727e
refactor: path -> wayPoints 단어 변경
shin-mallang aba299f
fix: Pawn 움직일 수 없는데 움직여지는 버그 수정
shin-mallang 8d4c42a
feat: 체스 보드에서 말을 움직이는 기능 구현
shin-mallang a49ad60
refactor: IMPOSSIBLE Condition 제거
shin-mallang 2debf96
refactor: Condition 완전히 제거
shin-mallang 1e0b005
refactor: ChessBoard Polishing
shin-mallang 0628e86
feat: 체스 게임 구현
shin-mallang 60044eb
refactor: Command 분리
shin-mallang 21e9aba
fix: 예외 메세지 수정
shin-mallang 8f8675f
refactor: 매직 넘버 상수로 관리
shin-mallang f8e2753
refactor: ChessBoard 생성을 Factory에서 담당
shin-mallang f92e75f
docs: 2단계 기능 목록 정리
Go-Jaecheol b371786
refactor: 사용하지 않는 import문 삭제
Go-Jaecheol bc18b37
docs: 클래스 다이어그램 추가
Go-Jaecheol ad46fa0
mission: [미니 미션] Lambda, Stream 이해하기 - 미션 1 구현
Go-Jaecheol 2c52ec8
mission: [미니 미션] Lambda, Stream 이해하기 - 미션 2 구현
Go-Jaecheol b5c5790
mission: [미니 미션] Lambda, Stream 이해하기 - 미션 3 구현
Go-Jaecheol 6201740
refactor: createExcludePawn 메서드 명 변경
Go-Jaecheol 37b99b4
refactor: 생성자에서 방어적 복사를 하도록 변경
Go-Jaecheol fdf03d0
refactor: PiecePosition에서 File, Rank 순서 변경
Go-Jaecheol ed024b5
refactor: Turn을 확인하는 incorrect 메서드 명 변경 및 메서드 순서 변경
Go-Jaecheol c95316d
style: 한 줄에 점을 하나만 찍도록 수정
Go-Jaecheol b2f7bd6
refactor: ChessState에서 기물을 직접 움직이지 않도록 변경
Go-Jaecheol eccf422
refactor: isEnemy 메서드 부정 연산자 사용하지 않도록 변경
Go-Jaecheol 2a06db3
refactor: 필드와 파라미터가 같으면 정적 팩토리 메서드 대신 생성자를 사용하도록 수정
Go-Jaecheol 48ea72c
refactor: getter 사용 지양하도록 수정
Go-Jaecheol 571ba92
docs: 1, 2단계 리팩토링 요구 사항 정리
Go-Jaecheol File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
|
||
## 클래스 다이어그램 | ||
|
||
### 주요 클래스 구조 | ||
|
||
```mermaid | ||
classDiagram | ||
class ChessBoard { | ||
-List~Piece~ pieces | ||
} | ||
|
||
class Piece | ||
<<Abstract>> Piece | ||
|
||
class Color | ||
<<Enumeration>> Color | ||
|
||
class Direction | ||
<<Enumeration>> Direction | ||
|
||
ChessController ..> ChessBoard | ||
ChessController ..> ChessState | ||
|
||
ChessBoard --> Piece | ||
ChessBoard ..> PiecePosition | ||
ChessBoard ..> WayPoints | ||
ChessBoard ..> Turn | ||
|
||
Piece --> Color | ||
Piece --> PiecePosition | ||
Piece ..> Path | ||
는 | ||
|
||
Path --> PiecePosition | ||
WayPoints --> PiecePosition | ||
|
||
PiecePosition --> Rank | ||
PiecePosition --> File | ||
PiecePosition ..> Direction | ||
``` | ||
|
||
### Piece 추상 클래스 | ||
```mermaid | ||
classDiagram | ||
class Piece | ||
<<Abstract>> Piece | ||
|
||
Piece <|-- King | ||
Piece <|-- Queen | ||
Piece <|-- Bishop | ||
Piece <|-- Rook | ||
Piece <|-- Knight | ||
Piece <|-- Pawn | ||
``` | ||
|
||
### 상태 패턴 | ||
```mermaid | ||
classDiagram | ||
class ChessState | ||
<<Interface>> ChessState | ||
|
||
class AbstractChessState | ||
<<Abstract>> AbstractChessState | ||
|
||
ChessState ..> Command | ||
ChessState <|.. AbstractChessState | ||
AbstractChessState <|-- Initialize | ||
AbstractChessState <|-- Running | ||
AbstractChessState <|-- End | ||
Command --> Type | ||
``` | ||
|
||
--- | ||
|
||
## 1단계 기능 요구 사항 | ||
|
||
- [x] 게임 시작 메세지 출력 | ||
- [x] 커맨드 입력 메세지 출력 | ||
- [x] 커맨드 입력 기능 | ||
- [x] 체스판 출력 | ||
- [x] 체스판 만드는 기능 | ||
|
||
### 기물 | ||
|
||
- [x] 위치를 갖는다. | ||
- [x] 종류는 킹, 퀸, 룩, 나이트, 비숍, 폰 이 있다. | ||
|
||
### 체스 판 | ||
|
||
- [x] 가로는 Rank 이다. | ||
- [x] 세로는 File 이다. | ||
- [x] 8 X 8 이다. | ||
|
||
--- | ||
|
||
## 2단계 기능 요구 사항 | ||
|
||
- [x] 게임 시작 및 커맨드 메세지 출력 | ||
- [x] 이동 커맨드 입력 기능 추가 | ||
- [x] White, Black 턴 반복 기능 추가 | ||
- [x] start, move, end 커맨드에 대한 로직 구현 | ||
|
||
### 기물 이동 | ||
|
||
- 공통 | ||
- [x] 도착지에 아군 기물이 있을 경우, 이동 불가능 | ||
- [x] 도착지에 상대 기물이 있을 경우, 이동 후 상대 기물 제거(Pawn 예외) | ||
- [x] 도착지까지 가는 경로에 기물이 있을 경우, 이동 불가능(Knight 예외) | ||
- [x] 상대 말 이동 불가능 | ||
- King | ||
- [x] 주변 한 칸만 이동 가능 | ||
- Queen | ||
- [x] 직선 거리 or 대각선 거리로 이동 가능 | ||
- Bishop | ||
- [x] 대각선 거리로 이동 가능 | ||
- Rook | ||
- [x] 직선 거리로 이동 가능 | ||
- Knight | ||
- [x] 앞으로 두 칸 -> 옆으로 한 칸 이동 가능 (8개) | ||
- Pawn | ||
- [x] 첫 이동인 경우, 앞으로 두 칸까지 이동 가능 | ||
- [x] 첫 이동이 아닌 경우, 앞으로 한 칸만 이동 가능 | ||
- [x] 앞에 상대 기물이 있는 경우, 이동 불가능 | ||
- [x] 대각선 위치에 상대 기물이 있는 경우, 이동 후 상대 기물 제거 가능 | ||
|
||
--- | ||
|
||
## 1, 2단계 리팩토링 요구 사항 | ||
|
||
- [x] `createExclusivePawn` 메서드 명 변경 | ||
- [x] 생성자에서 방어적 복사 하도록 변경 | ||
- [x] `PiecePosition`에서 File, Rank 순서 변경 | ||
- [x] 상태가 기물을 직접 움직이지 않도록 변경 | ||
- [x] 정적 팩토리 메서드와 생성자 사이에 기준 정하기 | ||
- [x] 부정 연산자 사용하지 않도록 수정 | ||
- [x] `incorrect` 메서드 명 변경 및 메서드 순서 변경 | ||
- [x] 한 줄에 점 하나만 찍도록 수정 | ||
- [x] getter 사용 지양하기 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package chess; | ||
|
||
import chess.controller.ChessController; | ||
|
||
public class ChessApplication { | ||
public static void main(String[] args) { | ||
new ChessController().start(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package chess.controller; | ||
|
||
import chess.domain.board.ChessBoard; | ||
import chess.domain.board.ChessBoardFactory; | ||
import chess.domain.piece.position.PiecePosition; | ||
import chess.domain.state.ChessState; | ||
import chess.domain.state.Initialize; | ||
import chess.domain.state.command.Command; | ||
import chess.view.InputView; | ||
import chess.view.OutputView; | ||
|
||
import java.util.ArrayList; | ||
|
||
public class ChessController { | ||
|
||
public void start() { | ||
OutputView.printStartMessage(); | ||
final ChessBoard chessBoard = ChessBoardFactory.create(); | ||
final ChessState state = new Initialize(); | ||
run(chessBoard, state); | ||
} | ||
|
||
private void run(final ChessBoard chessBoard, ChessState state) { | ||
while (state.isRunnable()) { | ||
state = chessState(chessBoard, state); | ||
} | ||
} | ||
|
||
private ChessState chessState(final ChessBoard chessBoard, ChessState state) { | ||
try { | ||
final Command command = Command.parse(new ArrayList<>(InputView.readCommand())); | ||
state = changeState(chessBoard, command, state); | ||
OutputView.showBoard(chessBoard.pieces()); | ||
} catch (Exception e) { | ||
System.out.println("[ERROR] " + e.getMessage()); | ||
} | ||
return state; | ||
} | ||
|
||
private ChessState changeState(final ChessBoard chessBoard, final Command command, ChessState state) { | ||
state = state.changeStateByCommand(command); | ||
if (command.isMove()) { | ||
state = movePiece(chessBoard, command, state); | ||
} | ||
return state; | ||
} | ||
|
||
private ChessState movePiece(final ChessBoard chessBoard, final Command command, ChessState state) { | ||
final PiecePosition from = PiecePosition.of(command.getFromParameter()); | ||
final PiecePosition to = PiecePosition.of(command.getToParameter()); | ||
chessBoard.movePiece(state, from, to); | ||
return state.changeTurn(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package chess.domain.board; | ||
|
||
import chess.domain.piece.Piece; | ||
import chess.domain.piece.position.PiecePosition; | ||
import chess.domain.piece.position.WayPoints; | ||
|
||
import chess.domain.state.ChessState; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
public class ChessBoard { | ||
|
||
private final List<Piece> pieces; | ||
|
||
public ChessBoard(final List<Piece> pieces) { | ||
this.pieces = new ArrayList<>(pieces); | ||
} | ||
|
||
public void movePiece(final ChessState state, final PiecePosition source, final PiecePosition destination) { | ||
final Piece from = get(source); | ||
validateCorrectTurn(state, from); | ||
validateNonBlock(destination, from); | ||
moveOrKill(destination, from); | ||
} | ||
|
||
private void validateCorrectTurn(final ChessState state, final Piece from) { | ||
if (state.isInCorrectTurn(from.color())) { | ||
throw new IllegalArgumentException("상대 말 선택하셨습니다."); | ||
} | ||
} | ||
|
||
private void validateNonBlock(final PiecePosition destination, final Piece from) { | ||
final WayPoints wayPoints = from.wayPointsWithCondition(destination); | ||
if (wayPoints.isBlocking(pieces)) { | ||
throw new IllegalArgumentException("경로 상에 말이 있어서 이동할 수 없습니다."); | ||
} | ||
} | ||
|
||
private void moveOrKill(final PiecePosition destination, final Piece from) { | ||
if (existByPosition(destination)) { | ||
final Piece to = get(destination); | ||
from.moveAndKill(to); | ||
pieces.remove(to); | ||
return; | ||
} | ||
from.move(destination); | ||
} | ||
|
||
private boolean existByPosition(final PiecePosition piecePosition) { | ||
return pieces.stream() | ||
.anyMatch(piece -> piece.existIn(piecePosition)); | ||
} | ||
|
||
public Piece get(final PiecePosition piecePosition) { | ||
return pieces.stream() | ||
.filter(piece -> piece.existIn(piecePosition)) | ||
.findAny() | ||
.orElseThrow(() -> new IllegalArgumentException("해당 위치에 존재하는 피스가 없습니다.")); | ||
} | ||
|
||
public List<Piece> pieces() { | ||
return pieces.stream() | ||
.map(Piece::clone) | ||
.collect(Collectors.toList()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package chess.domain.board; | ||
|
||
import chess.domain.piece.Color; | ||
import chess.domain.piece.Piece; | ||
import chess.domain.piece.position.File; | ||
import chess.domain.piece.position.PiecePosition; | ||
import chess.domain.piece.type.Bishop; | ||
import chess.domain.piece.type.King; | ||
import chess.domain.piece.type.Knight; | ||
import chess.domain.piece.type.Pawn; | ||
import chess.domain.piece.type.Queen; | ||
import chess.domain.piece.type.Rook; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
public class ChessBoardFactory { | ||
|
||
private static final List<Piece> pieces = new ArrayList<>(); | ||
|
||
static { | ||
createPieceExcludingPawn(1, Color.WHITE); | ||
createPieceExcludingPawn(8, Color.BLACK); | ||
createPawns(2, Color.WHITE); | ||
createPawns(7, Color.BLACK); | ||
} | ||
|
||
private static void createPawns(final int rank, final Color color) { | ||
for (char file = File.MIN; file <= File.MAX; file++) { | ||
pieces.add(new Pawn(color, PiecePosition.of(file, rank))); | ||
} | ||
} | ||
|
||
private static void createPieceExcludingPawn(final int rank, final Color color) { | ||
pieces.add(new Rook(color, PiecePosition.of('a', rank))); | ||
pieces.add(new Knight(color, PiecePosition.of('b', rank))); | ||
pieces.add(new Bishop(color, PiecePosition.of('c', rank))); | ||
pieces.add(new Queen(color, PiecePosition.of('d', rank))); | ||
pieces.add(new King(color, PiecePosition.of('e', rank))); | ||
pieces.add(new Bishop(color, PiecePosition.of('f', rank))); | ||
pieces.add(new Knight(color, PiecePosition.of('g', rank))); | ||
pieces.add(new Rook(color, PiecePosition.of('h', rank))); | ||
} | ||
|
||
public static ChessBoard create() { | ||
return new ChessBoard(pieces.stream() | ||
.map(Piece::clone) | ||
.collect(Collectors.toList())); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package chess.domain.board; | ||
|
||
import chess.domain.piece.Color; | ||
|
||
import java.util.Objects; | ||
|
||
public class Turn { | ||
private static final Turn whiteTurn = new Turn(Color.WHITE); | ||
private static final Turn blackTurn = new Turn(Color.BLACK); | ||
|
||
private final Color turn; | ||
|
||
public Turn(final Color turn) { | ||
this.turn = turn; | ||
} | ||
|
||
public Turn change() { | ||
if (equals(whiteTurn)) { | ||
return blackTurn; | ||
} | ||
return whiteTurn; | ||
} | ||
|
||
public boolean isIncorrect(final Color color) { | ||
return turn != color; | ||
} | ||
|
||
@Override | ||
public boolean equals(final Object o) { | ||
if (this == o) return true; | ||
if (!(o instanceof Turn)) return false; | ||
final Turn turn1 = (Turn) o; | ||
return turn == turn1.turn; | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(turn); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package chess.domain.piece; | ||
|
||
public enum Color { | ||
WHITE, | ||
BLACK; | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
게터/세터/프로퍼티를 쓰지 않는다.
추가 요구 사항을 리뷰가 마친 후 확인했는데요~프로퍼티를
.getField
형식이 아니라.field
형식으로 참조한 것 들도 게터라고 판단하고 리팩토링 진행해주시면 감사하겠습니다.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는 존재해도 되나요??
그리고
List<String>
을 필드로 가지고 있는 클래스에서 리스트 내 특정 위치의String
값만 반환하는 메서드로 바꾸는 것도 여전히 getter로 판단되어 지양해야 하는지 궁금합니다.또한, getter 사용을 피하면서 도메인이 서로 의존하게 된다면 이럴 땐 어떻게 하는 게 좋을까요..?
getter 사용 지양하기.. 너무 어렵습니다 😢
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 사용을 지양하라는 것은 알고 계시겠지만 객체에 역할을 부여할 수 있으면 해당 객체 내부에 역할을 주고 해당 메서드를 콜해서 사용해라~ getter를 통해서 값만 들고와서 바깥에서 처리하는 방식을 최대한 하지마라~~~ 라는 의미인데요.
이렇나 관점에서 각 객체의 필드를 가지고 오는 것을 getter 그 이외의 비즈니스 로직을 거친 필드가 아닌 다른 값을 내뱉는 것은 getter라고 보기 힘들죠. 하지만 getter가 필요한 경우도 당연 있습니다! 이럴 때에는 어쩔 수 없는거죠.
그냥 요구사항의 의미는 객체지향적으로 최대한 해라~ 이니 남은 체스 미션에서는 망고가 이 선을 어느정도 지키면서 융통성 있게 getter를 사용하시면 될 것 같습니다. view 클래스의 보드 현 상황을 출력하는 메서드를 호출을 할 때에
ChessBoard
의 piece를 그냥 getter를 통해서 가지고 와서 파라미터로 보내고 해당 클래스에서 조립을 하는 방식도 있을 수 있겠지만, 요구사항을 따른다면ChessBoard
내부에 보드현황에 대한 String을 만드는 메서드를 만들어서 호출하는 식으로 할 수도 있겠죠!망고가 미션을 진행하면서 아 이거는 getter를 그냥 쓰는게 진짜 맞는 것 같은데? 오히려 이걸 안쓰면 더 복잡해지겠는데? 싶으면 그냥 사용하시면 될 것 같습니다! 😆