여기서 문제는 어떤 클래스가 어떤 작업을 처리해야하는지에 대한 명확한 설명이 없다는 것입니다. 각 수업이 무엇을해야하는지에 대한 좋은 설명이라고 생각하고 아이디어를 보여주는 일반적인 코드의 예를 들어 보겠습니다. 우리는 코드가 덜 결합되어 있으므로 실제로 순환 참조가 없습니다.
각 클래스의 기능을 설명하는 것으로 시작하겠습니다.
이 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;
}
유형 매개 변수가 있습니다. 예를 들어, ChessMovepre-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때이 정보를 제공합니다 .RuleBookGame
어쨌든이 대답의 요점은 일단 각 클래스가 담당하는 것을 합리적으로 결정하고 각 클래스를 적은 수의 책임에 중점을두고 각 책임을 고유 한 클래스에 할당 한 다음 분리되는 경향이 있으며 모든 것이 쉽게 코딩됩니다. 희망적으로 그것은 내가 준 코드 예제에서 분명합니다.
RuleBook를 예했습니다State인수로하고 유효한 반환MoveList즉, "다음에 무엇을 할 수 있는지, 우리가 지금 어디에 여기를?"