ฉันคิดว่าปัญหาที่นี่คือคุณยังไม่ได้ให้คำอธิบายที่ชัดเจนเกี่ยวกับงานที่ต้องจัดการโดยชั้นเรียนใด ฉันจะอธิบายสิ่งที่ฉันคิดว่าเป็นคำอธิบายที่ดีเกี่ยวกับสิ่งที่แต่ละชั้นควรทำแล้วฉันจะให้ตัวอย่างของรหัสทั่วไปที่แสดงให้เห็นถึงความคิด เราจะเห็นว่ารหัสนั้นน้อยกว่าคู่กันดังนั้นจึงไม่มีการอ้างอิงแบบวงกลมจริงๆ
เริ่มจากอธิบายสิ่งที่แต่ละชั้นเรียนทำกัน
GameState
ชั้นเดียวควรมีข้อมูลเกี่ยวกับสถานะปัจจุบันของเกม ไม่ควรมีข้อมูลใด ๆ เกี่ยวกับสิ่งที่รัฐในอดีตของเกมหรือสิ่งที่เคลื่อนไหวในอนาคตเป็นไปได้ มันควรจะมีข้อมูลเกี่ยวกับชิ้นส่วนที่อยู่บนสี่เหลี่ยมในหมากรุกหรือมีจำนวนเท่าใดและตัวตรวจสอบชนิดใดที่อยู่ในจุดที่แบ็คแกมมอน GameState
จะต้องมีข้อมูลบางอย่างที่พิเศษเช่นข้อมูลเกี่ยวกับการใช้เรือในเกมหมากรุกหรือประมาณลูกบาศก์สองเท่าในการแบ็คแกมมอน
Move
ระดับเป็นเรื่องยุ่งยากเล็กน้อย ฉันจะบอกว่าฉันสามารถระบุการย้ายที่จะเล่นโดยการระบุGameState
ผลลัพธ์ที่ได้จากการเล่นการย้าย GameState
ดังนั้นคุณอาจคิดว่าการย้ายก็สามารถจะนำมาใช้เป็น อย่างไรก็ตามในระหว่างการเดินทาง (ตัวอย่าง) คุณสามารถจินตนาการว่าเป็นการง่ายกว่ามากในการระบุการย้ายโดยการระบุจุดเดียวบนกระดาน เราต้องการให้Move
ชั้นเรียนของเรามีความยืดหยุ่นเพียงพอที่จะรองรับกรณีเหล่านี้ ดังนั้นMove
คลาสจึงเป็นอินเทอร์เฟซที่ใช้วิธีการก่อนการย้ายGameState
และส่งคืนการย้ายGameState
ใหม่
ตอนนี้RuleBook
ชั้นเรียนมีความรับผิดชอบในการรู้ทุกอย่างเกี่ยวกับกฎ สิ่งนี้สามารถแบ่งออกเป็นสามสิ่ง มันจำเป็นต้องรู้ว่าการเริ่มต้นGameState
คืออะไรมันต้องรู้ว่าการเคลื่อนไหวนั้นถูกกฎหมายหรือไม่และจะต้องสามารถบอกได้ว่าผู้เล่นคนใดคนหนึ่งชนะ
คุณสามารถสร้างGameHistory
ชั้นเรียนเพื่อติดตามการเคลื่อนไหวทั้งหมดGameStates
ที่เกิดขึ้นและสิ่งที่เกิดขึ้นได้ทั้งหมด คลาสใหม่มีความจำเป็นเพราะเราตัดสินใจว่าGameState
จะไม่รับผิดชอบต่อการรู้ทั้งหมดGameState
ที่มาก่อน
นี่เป็นการสรุปคลาส / อินเตอร์เฟสที่ฉันจะพูดถึง คุณมีBoard
คลาสด้วย แต่ฉันคิดว่าบอร์ดในเกมต่าง ๆ นั้นแตกต่างกันมากจนยากที่จะเห็นสิ่งที่สามารถทำได้โดยทั่วไปกับบอร์ด ตอนนี้ฉันจะให้อินเทอร์เฟซทั่วไปและใช้คลาสทั่วไป
GameState
แรกคือ เนื่องจากคลาสนี้ขึ้นอยู่กับเกมโดยเฉพาะจึงไม่มีGamestate
อินเตอร์เฟสหรือคลาสทั่วไป
Move
ถัดไปคือ ดังที่ฉันได้กล่าวมาสิ่งนี้สามารถถูกแสดงด้วยอินเตอร์เฟสที่มีวิธีการเดียวที่ใช้สถานะ pre-move และสร้างสถานะ post-move นี่คือรหัสสำหรับส่วนต่อประสานนี้:
package boardgame;
/**
*
* @param <T> The type of GameState
*/
public interface Move<T> {
T makeResultingState(T preMoveState) throws IllegalArgumentException;
}
ขอให้สังเกตว่ามีพารามิเตอร์ประเภท เพราะนี่คือตัวอย่างเช่น a ChessMove
จะต้องรู้เกี่ยวกับรายการของการย้าย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
คลาสจึงเป็นเพียง enum อย่างง่ายที่ให้ไว้ดังนี้:
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
จำเป็นต้องพูดว่าการเคลื่อนไหวทางกฎหมายคืออะไรหรือใครก็ตามชนะ
อย่างไรก็ตามประเด็นของคำตอบนี้ก็คือเมื่อคุณตัดสินใจอย่างสมเหตุสมผลแล้วว่าแต่ละชั้นเรียนมีหน้าที่อะไรและคุณทำให้แต่ละชั้นเรียนมุ่งเน้นไปที่ความรับผิดชอบจำนวนเล็กน้อยและคุณมอบหมายความรับผิดชอบแต่ละชั้นให้กับชั้นเรียนที่ไม่ซ้ำใคร มีแนวโน้มที่จะ decoupled และทุกอย่างได้ง่ายรหัส หวังว่าจะเห็นได้ชัดจากตัวอย่างโค้ดที่ฉันให้
RuleBook
เอาเช่นState
การโต้แย้งและกลับมาถูกต้องMoveList
เช่น"นี่คือที่ที่เราอยู่ตอนนี้สิ่งที่สามารถทำได้ต่อไป?"