Bence buradaki problem, hangi görevlerin hangi sınıflar tarafından yerine getirileceğinin net bir tanımını vermemiş olmanızdır. Her sınıfın ne yapması gerektiğinin iyi bir açıklaması olduğunu düşündüğümü anlatacağım, sonra fikirleri gösteren bir genel kod örneği vereceğim. Kodun daha az eşlendiğini göreceğiz ve bu nedenle döngüsel referansları yoktur.
Her sınıfın ne yaptığını tarif etmeye başlayalım.
GameState
Sınıf yalnızca oyunun mevcut durumu hakkında bilgi içermelidir. Oyunun geçmiş durumları ya da gelecekteki hareketlerin mümkün olabileceği hakkında herhangi bir bilgi içermemelidir. Sadece satrançta hangi karelerde ne olduğu, ya da tavlada hangi noktalarda ne kadar ve hangi tür dama olduğu hakkında bilgi içermelidir. Bu GameState
irade, satrançta oyun oynama ya da tavlada ikişer küp hakkında bilgi gibi, bazı ekstra bilgiler içermelidir.
Move
Sınıf biraz zordur. Hareketi oynamanın sonucunu belirterek oynayacak bir hamle belirleyebileceğimi söyleyebilirim GameState
. Böylece bir hareketin sadece bir olarak uygulanabileceğini hayal edebilirsiniz GameState
. Ancak, (örneğin), hareket halindeyken tahtada tek bir nokta belirterek bir hareketi belirtmenin daha kolay olduğunu hayal edebilirsiniz. Move
Sınıfımızın her ikisini de davalara taşıyacak kadar esnek olmasını istiyoruz . Bu nedenle, Move
sınıf aslında bir hamle öncesi GameState
ve yeni bir hamle sonrası dönen bir yöntem ile bir arayüz olacak GameState
.
Şimdi RuleBook
sınıf kuralları hakkında her şeyi bilmek sorumludur. Bu üç şeye ayrılabilir. İlk olanın ne GameState
olduğunu bilmesi, hangi hamlenin yasal olduğunu bilmesi ve oyunculardan birinin kazanıp kazanmadığını anlayabilmesi gerekiyor.
Ayrıca GameHistory
, yapılan tüm hareketleri ve GameStates
olanları takip etmek için bir sınıf oluşturabilirsiniz . Yeni bir sınıf gereklidir, çünkü bir GameState
kişinin GameState
kendisinden önce gelenleri bilmekle sorumlu olmayacağına karar verdik .
Bu tartışacağım sınıfları / arayüzleri sonlandırıyor. Ayrıca bir Board
sınıfın var. Ancak farklı oyunlardaki tahtaların yeterince farklı olduğunu düşünüyorum; tahtalarla ne yapılabileceğini görmek zor. Şimdi genel arayüzler vermeye ve genel sınıfları uygulamaya devam edeceğim.
İlk GameState
. Bu sınıf tamamen oyuna bağlı olduğundan, genel bir Gamestate
arayüz veya sınıf yoktur.
Sonraki olduğunu Move
. Dediğim gibi, bu, hareket öncesi durumu alan ve hareket sonrası durumu üreten tek bir metoda sahip bir arayüz ile gösterilebilir. İşte bu arayüzün kodu:
package boardgame;
/**
*
* @param <T> The type of GameState
*/
public interface Move<T> {
T makeResultingState(T preMoveState) throws IllegalArgumentException;
}
Bir tür parametresi olduğuna dikkat edin. Bunun nedeni, örneğin, ChessMove
hamle öncesi işlemin özellikleri hakkında bir bilgiye ihtiyaç duyulması gerektiğidir ChessGameState
. Yani, örneğin, sınıf beyanı ChessMove
olacaktır
class ChessMove extends Move<ChessGameState>
,
zaten bir ChessGameState
sınıf tanımladığınız yer .
Sonra genel RuleBook
sınıfı tartışacağım . İşte kod:
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);
}
Yine GameState
sınıf için bir type parametresi var . Yana RuleBook
başlangıç durumu ne olduğunu bilmek gerekiyordu, biz başlangıç durumunu vermek için bir yöntem koyduk. Yana RuleBook
hamle yasal olduğunu bilmek gerekiyordu bir hareket belli bir durumda yasal ve belirli bir devlet için yasal hamle listesini vermek, biz teste yöntemleri var. Son olarak, değerlendirmek için bir yöntem var GameState
. Dikkat RuleBook
sadece bir veya diğer oyuncular zaten kazanmışsa açıklayan sorumlu olmalıdır, ancak bir oyunun orta daha iyi bir konumda olduğunu kim. Kimin daha iyi bir pozisyonda olduğuna karar vermek, kendi sınıfına taşınması gereken karmaşık bir şeydir. Bu nedenle, StateEvaluation
sınıf aslında aşağıdaki gibi verilen basit bir numaradır:
package boardgame;
/**
*
*/
public enum StateEvaluation {
UNFINISHED,
PLAYER_ONE_WINS,
PLAYER_TWO_WINS,
DRAW,
ILLEGAL_STATE
}
Son olarak, GameHistory
sınıfı tanımlayalım . Bu sınıf, oyunda ulaşılan tüm pozisyonları ve oynanan hamleleri hatırlamaktan sorumludur. Yapması gereken en önemli şey Move
, oynanan bir kaydı kaydetmektir . Ayrıca geri almak için işlevler ekleyebilirsiniz Move
. Aşağıda bir uygulamam var.
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();
}
}
Sonunda, Game
her şeyi birbirine bağlamak için bir sınıf yapmayı hayal edebiliriz . Bu Game
sınıfın, insanlar için akımın ne olduğunu görmesini mümkün kılan, GameState
hangisinin varsa, hangi hamlelerin oynanabileceğini görebilecek ve hamle oynayabilecek yöntemleri görmesi gerekiyor. Aşağıda bir uygulama var
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());
}
}
Bu sınıfta RuleBook
, akımın ne olduğunu bilmekle sorumlu olmadığına dikkat edin GameState
. Bu GameHistory
iş. Yani Game
sorar GameHistory
geçerli durumdur ve bu bilgiyi verir ihtiyaçları yasal hamle veya kimse kazandı eğer ne olduğunu söylemek.RuleBook
Game
Her neyse, bu cevabın amacı, bir defada her bir sınıfın neyin sorumlu olduğunu belirledikten sonra, her bir sınıfı az sayıda sorumluluk üzerine odakladığınız ve her sorumluluğu kendine özgü bir sınıfa, ardından sınıflara verdiğinizdir. ayrıştırılma eğilimindedir ve her şeyin kodlanması kolaydır. Umarım verdiğim kod örneklerinde açıkça görülmektedir.
RuleBook
meselaState
tartışmayı kabul edip, geçerliyseMoveList
, örneğin "şimdi buradayız, daha sonra ne yapılabilir?"