여기서 문제는 어떤 클래스가 어떤 작업을 처리해야하는지에 대한 명확한 설명이 없다는 것입니다. 각 수업이 무엇을해야하는지에 대한 좋은 설명이라고 생각하고 아이디어를 보여주는 일반적인 코드의 예를 들어 보겠습니다. 우리는 코드가 덜 결합되어 있으므로 실제로 순환 참조가 없습니다.
각 클래스의 기능을 설명하는 것으로 시작하겠습니다.
이 GameState
클래스에는 게임의 현재 상태에 대한 정보 만 포함되어야합니다. 게임의 과거 상태 또는 향후 움직임에 대한 정보는 포함하지 않아야합니다. 여기에는 체스의 사각형에 어떤 조각이 있는지, 주사위 놀이의 점에 몇 개, 어떤 유형의 체커가 있는지에 대한 정보 만 포함되어야합니다. 여기 GameState
에는 체스에서의 캐스터 링 또는 주사위 놀이의 배가 큐브에 대한 정보와 같은 추가 정보가 포함되어야합니다.
Move
클래스는 조금 까다 롭습니다. 이동을 수행 GameState
한 결과를 지정하여 재생할 이동을 지정할 수 있다고 말하고 싶습니다 . 따라서 이동을로 구현할 수 있다고 상상할 수 있습니다 GameState
. 그러나, 예를 들어 보드에서 단일 지점을 지정하여 이동을 지정하는 것이 훨씬 쉽다고 상상할 수 있습니다. 우리는 Move
클래스가 이들 중 하나를 처리 할 수있을 정도로 유연해야합니다. 따라서 Move
클래스는 실제로 사전 이동을 수행 GameState
하고 새로운 post-move를 반환하는 메소드가있는 인터페이스가 GameState
됩니다.
이제 RuleBook
수업에 규칙에 대한 모든 것을 알고 있습니다. 이것은 세 가지로 나눌 수 있습니다. 이니셜 GameState
이 무엇인지 알아야하고, 합법적 인 움직임이 무엇인지 알아야하며, 플레이어 중 한 사람이 이겼는지 알 수 있어야합니다.
또한 GameHistory
모든 동작과 GameStates
발생한 모든 동작을 추적 하는 클래스를 만들 수 있습니다 . 우리는 싱글 GameState
이 GameState
그 앞에 온 모든 것을 아는 것에 대한 책임을지지 않기로 결정했기 때문에 새로운 클래스가 필요 합니다.
이것으로 내가 논의 할 수업 / 인터페이스를 마칩니다. Board
수업 도 있습니다 . 그러나 다른 게임의 보드는 보드로 일반적으로 무엇을 할 수 있는지 알기에는 충분히 다르다고 생각합니다. 이제 일반 인터페이스를 제공하고 일반 클래스를 구현하겠습니다.
첫 번째는 GameState
입니다. 이 클래스는 특정 게임에 전적으로 의존하기 때문에 일반적인 Gamestate
인터페이스 나 클래스 가 없습니다 .
다음은 Move
입니다. 내가 말했듯이, 이것은 이동 전 상태를 취하고 이동 후 상태를 생성하는 단일 메소드를 갖는 인터페이스로 나타낼 수 있습니다. 이 인터페이스의 코드는 다음과 같습니다.
package boardgame;
/**
*
* @param <T> The type of GameState
*/
public interface Move<T> {
T makeResultingState(T preMoveState) throws IllegalArgumentException;
}
유형 매개 변수가 있습니다. 예를 들어, ChessMove
pre-move의 특정 사항에 대해 알고 있어야하기 때문 ChessGameState
입니다. 따라서, 예를 들어,의 클래스 선언 ChessMove
될 것이다
class ChessMove extends Move<ChessGameState>
,
이미 ChessGameState
클래스를 정의했을 것 입니다.
다음으로 일반 RuleBook
수업에 대해 설명하겠습니다 . 코드는 다음과 같습니다.
package boardgame;
import java.util.List;
/**
*
* @param <T> The type of GameState
*/
public interface RuleBook<T> {
T makeInitialState();
List<Move<T>> makeMoveList(T gameState);
StateEvaluation evaluateState(T gameState);
boolean isMoveLegal(Move<T> move, T currentState);
}
다시 GameState
클래스에 대한 유형 매개 변수가 있습니다. RuleBook
초기 상태가 무엇인지 알고 있어야 하므로 초기 상태를 제공하는 방법을 마련했습니다. RuleBook
는 어떤 움직임이 합법적인지 알아야하기 때문에 , 우리는 움직임이 주어진 상태에서 합법적인지 테스트하고 주어진 상태에 대한 법적 움직임 목록을 제공하는 방법을 가지고 있습니다. 마지막으로를 평가하는 방법이 GameState
있습니다. (가)에 주목 RuleBook
하나 또는 다른 플레이어가 이미 이겼는지 여부를 설명하는 책임을 져야한다,하지만 게임의 중간에 더 나은 위치에있는 사람. 누가 더 나은 위치에 있는지 결정하는 것은 자신의 수업으로 옮겨야하는 복잡한 일입니다. 따라서 StateEvaluation
클래스는 실제로 다음과 같이 간단한 열거 형입니다.
package boardgame;
/**
*
*/
public enum StateEvaluation {
UNFINISHED,
PLAYER_ONE_WINS,
PLAYER_TWO_WINS,
DRAW,
ILLEGAL_STATE
}
마지막으로 GameHistory
수업에 대해 설명하겠습니다 . 이 수업은 게임에서 도달 한 모든 포지션과 플레이 한 움직임을 기억하는 역할을합니다. 할 수있는 가장 중요한 것은 Move
연주 된 대로 녹음하는 것입니다. 의 실행 취소 기능을 추가 할 수도 있습니다 Move
. 아래에 구현이 있습니다.
package boardgame;
import java.util.ArrayList;
import java.util.List;
/**
*
* @param <T> The type of GameState
*/
public class GameHistory<T> {
private List<T> states;
private List<Move<T>> moves;
public GameHistory(T initialState) {
states = new ArrayList<>();
states.add(initialState);
moves = new ArrayList<>();
}
void recordMove(Move<T> move) throws IllegalArgumentException {
moves.add(move);
states.add(move.makeResultingState(getMostRecentState()));
}
void resetToNthState(int n) {
states = states.subList(0, n + 1);
moves = moves.subList(0, n);
}
void undoLastMove() {
resetToNthState(getNumberOfMoves() - 1);
}
T getMostRecentState() {
return states.get(getNumberOfMoves());
}
T getStateAfterNthMove(int n) {
return states.get(n + 1);
}
Move<T> getNthMove(int n) {
return moves.get(n);
}
int getNumberOfMoves() {
return moves.size();
}
}
마지막으로 Game
모든 것을 하나로 묶는 수업을 상상할 수 있습니다. 이 Game
클래스는 사람들이 현재 상태 GameState
를보고, 누군가가 있다면 누구가 어떤 동작을하는지 볼 수 있고, 움직임을 할 수 있게하는 메소드를 제공해야합니다 . 아래에 구현이 있습니다.
package boardgame;
import java.util.List;
/**
*
* @author brian
* @param <T> The type of GameState
*/
public class Game<T> {
GameHistory<T> gameHistory;
RuleBook<T> ruleBook;
public Game(RuleBook<T> ruleBook) {
this.ruleBook = ruleBook;
final T initialState = ruleBook.makeInitialState();
gameHistory = new GameHistory<>(initialState);
}
T getCurrentState() {
return gameHistory.getMostRecentState();
}
List<Move<T>> getLegalMoves() {
return ruleBook.makeMoveList(getCurrentState());
}
void doMove(Move<T> move) throws IllegalArgumentException {
if (!ruleBook.isMoveLegal(move, getCurrentState())) {
throw new IllegalArgumentException("Move is not legal in this position");
}
gameHistory.recordMove(move);
}
void undoMove() {
gameHistory.undoLastMove();
}
StateEvaluation evaluateState() {
return ruleBook.evaluateState(getCurrentState());
}
}
이 클래스 RuleBook
에서 전류 GameState
가 무엇인지 아는 것은 책임지지 않습니다 . 그게 GameHistory
직업 이야 . 따라서 현재 상태가 무엇인지 Game
묻고 합법적 인 움직임이 무엇인지 또는 누군가 이겼는지 말할 필요가 있을 GameHistory
때이 정보를 제공합니다 .RuleBook
Game
어쨌든이 대답의 요점은 일단 각 클래스가 담당하는 것을 합리적으로 결정하고 각 클래스를 적은 수의 책임에 중점을두고 각 책임을 고유 한 클래스에 할당 한 다음 분리되는 경향이 있으며 모든 것이 쉽게 코딩됩니다. 희망적으로 그것은 내가 준 코드 예제에서 분명합니다.
RuleBook
를 예했습니다State
인수로하고 유효한 반환MoveList
즉, "다음에 무엇을 할 수 있는지, 우리가 지금 어디에 여기를?"