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단계 - 체스] 망고(고재철) 미션 제출합니다. #437

Merged
merged 47 commits into from
Mar 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
f819e70
docs: 기능 명세 문서 작성
shin-mallang Mar 14, 2023
8632943
feat: 체스 기물 생성
shin-mallang Mar 14, 2023
ebfe4a6
feat: 체스 기물에 색상 추가
shin-mallang Mar 14, 2023
efc16dd
feat: 체스 위치 추가
shin-mallang Mar 14, 2023
490bd32
refactor: Piece에서 PiecePosition 사용
shin-mallang Mar 14, 2023
8f786e1
refactor: Piece 의 역할을 enum으로 관리
shin-mallang Mar 14, 2023
d938d00
feat: ChessBoard 생성
shin-mallang Mar 14, 2023
b1b56a2
feat: 체스판 뷰 구현
shin-mallang Mar 14, 2023
d0674d0
refactor: Piece를 추상 클래스를 통해 구현하도록 변경
shin-mallang Mar 15, 2023
78a2104
feat: 1단계 뷰 구현 완료
shin-mallang Mar 15, 2023
d6cfaa0
feat: 방위를 나타내는 Direction 구현
shin-mallang Mar 15, 2023
03cea30
feat: PiecePosition에 기능 추가
shin-mallang Mar 15, 2023
2ecd841
feat: Path 구현
shin-mallang Mar 15, 2023
04a9fe7
feat: Piece 에 경로 탐색을 위한 템플릿 메서드 작성
shin-mallang Mar 15, 2023
ff4781a
feat: King 의 이동 조건 탐색 기능 구현
shin-mallang Mar 15, 2023
4cc1eb6
feat: Bishop 의 이동 조건 탐색 기능 구현
shin-mallang Mar 15, 2023
44335df
feat: Rook 의 이동 조건 탐색 기능 구현
shin-mallang Mar 15, 2023
c9e4051
feat: Queen 의 이동 조건 탐색 기능 구현
shin-mallang Mar 15, 2023
d50d83d
feat: Pawn 의 이동 조건 탐색 기능 구현
shin-mallang Mar 15, 2023
585e209
feat: Knight 의 이동 조건 탐색 기능 구현
shin-mallang Mar 15, 2023
db7727e
refactor: path -> wayPoints 단어 변경
shin-mallang Mar 15, 2023
aba299f
fix: Pawn 움직일 수 없는데 움직여지는 버그 수정
shin-mallang Mar 15, 2023
8d4c42a
feat: 체스 보드에서 말을 움직이는 기능 구현
shin-mallang Mar 15, 2023
a49ad60
refactor: IMPOSSIBLE Condition 제거
shin-mallang Mar 16, 2023
2debf96
refactor: Condition 완전히 제거
shin-mallang Mar 16, 2023
1e0b005
refactor: ChessBoard Polishing
shin-mallang Mar 16, 2023
0628e86
feat: 체스 게임 구현
shin-mallang Mar 16, 2023
60044eb
refactor: Command 분리
shin-mallang Mar 16, 2023
21e9aba
fix: 예외 메세지 수정
shin-mallang Mar 16, 2023
8f8675f
refactor: 매직 넘버 상수로 관리
shin-mallang Mar 16, 2023
f8e2753
refactor: ChessBoard 생성을 Factory에서 담당
shin-mallang Mar 16, 2023
f92e75f
docs: 2단계 기능 목록 정리
Go-Jaecheol Mar 16, 2023
b371786
refactor: 사용하지 않는 import문 삭제
Go-Jaecheol Mar 16, 2023
bc18b37
docs: 클래스 다이어그램 추가
Go-Jaecheol Mar 16, 2023
ad46fa0
mission: [미니 미션] Lambda, Stream 이해하기 - 미션 1 구현
Go-Jaecheol Mar 22, 2023
2c52ec8
mission: [미니 미션] Lambda, Stream 이해하기 - 미션 2 구현
Go-Jaecheol Mar 22, 2023
b5c5790
mission: [미니 미션] Lambda, Stream 이해하기 - 미션 3 구현
Go-Jaecheol Mar 22, 2023
6201740
refactor: createExcludePawn 메서드 명 변경
Go-Jaecheol Mar 22, 2023
37b99b4
refactor: 생성자에서 방어적 복사를 하도록 변경
Go-Jaecheol Mar 22, 2023
fdf03d0
refactor: PiecePosition에서 File, Rank 순서 변경
Go-Jaecheol Mar 22, 2023
ed024b5
refactor: Turn을 확인하는 incorrect 메서드 명 변경 및 메서드 순서 변경
Go-Jaecheol Mar 22, 2023
c95316d
style: 한 줄에 점을 하나만 찍도록 수정
Go-Jaecheol Mar 22, 2023
b2f7bd6
refactor: ChessState에서 기물을 직접 움직이지 않도록 변경
Go-Jaecheol Mar 22, 2023
eccf422
refactor: isEnemy 메서드 부정 연산자 사용하지 않도록 변경
Go-Jaecheol Mar 22, 2023
2a06db3
refactor: 필드와 파라미터가 같으면 정적 팩토리 메서드 대신 생성자를 사용하도록 수정
Go-Jaecheol Mar 22, 2023
48ea72c
refactor: getter 사용 지양하도록 수정
Go-Jaecheol Mar 22, 2023
571ba92
docs: 1, 2단계 리팩토링 요구 사항 정리
Go-Jaecheol Mar 22, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 138 additions & 0 deletions src/docs/READMD.md
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 사용 지양하기
9 changes: 9 additions & 0 deletions src/main/java/chess/ChessApplication.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package chess;

import chess.controller.ChessController;

public class ChessApplication {
Copy link
Member

Choose a reason for hiding this comment

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

게터/세터/프로퍼티를 쓰지 않는다. 추가 요구 사항을 리뷰가 마친 후 확인했는데요~
프로퍼티를 .getField 형식이 아니라 .field 형식으로 참조한 것 들도 게터라고 판단하고 리팩토링 진행해주시면 감사하겠습니다.

Copy link
Author

@Go-Jaecheol Go-Jaecheol Mar 22, 2023

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 사용 지양하기.. 너무 어렵습니다 😢

Copy link
Member

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를 그냥 쓰는게 진짜 맞는 것 같은데? 오히려 이걸 안쓰면 더 복잡해지겠는데? 싶으면 그냥 사용하시면 될 것 같습니다! 😆

public static void main(String[] args) {
new ChessController().start();
}
}
54 changes: 54 additions & 0 deletions src/main/java/chess/controller/ChessController.java
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();
}
}
67 changes: 67 additions & 0 deletions src/main/java/chess/domain/board/ChessBoard.java
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());
}
}
51 changes: 51 additions & 0 deletions src/main/java/chess/domain/board/ChessBoardFactory.java
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()));
}
}
40 changes: 40 additions & 0 deletions src/main/java/chess/domain/board/Turn.java
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);
}
}
6 changes: 6 additions & 0 deletions src/main/java/chess/domain/piece/Color.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package chess.domain.piece;

public enum Color {
WHITE,
BLACK;
}
Loading