Eheh, è proprio qui che volevo arrivare. Sembra strano, eh? Ma è così che si affrontano i problemi di progettazione.
Pensandoci un attimo possiamo concludere che il pezzo è effettivamente un oggetto e il tipo di pezzo che rappresenta è effettivamente una classe, poiché espone funzionalità e non solo dati. In questo caso, vogliamo che la funzionalità esposta sia quella di movimento, che è comune ad ogni pezzo ma che cambia a seconda del suo tipo. Questo si risolve ovviamente creando classi derivate. Ora il problema sta nel capire se la posizione del pezzo sulla scacchiera e la possibilità di muoversi appartengano al pezzo, alla scacchiera o al giocatore. Evidentemente non possono essere del giocatore, dato che, ancorché in grado di far muovere un pezzo, ne può sapere la posizione solo tramite la scacchiera; restano solo la scacchiera e il pezzo. Se le funzionalità appartenessero alla scacchiera allora nulla apparterrebbe al pezzo, poiché direbbe semplicemente il tipo e sarebbe quindi equivalente - concettualmente - a un intero. Dobbiamo concludere che la stessa classe Pezzo (e le sue derivate) dovrà incapsulare l'informazione della posizione e la funzionalità di movimento, poiché reciprocamente dipendenti. Ma a questo punto la scacchiera perde di significato come matrice, poiché le posizioni sono contenute nei pezzi: diventa quindi necessario vederla come una lista di pezzi, ognuno con le sue posizioni.
In questo modello, quindi, puoi richiamare Pezzo.Move(casella) e il pezzo si occuperà di fare tutto (e di dire se il movimento è possibile o meno). Analogamente potrai richiedere le possibili destinazioni tramite un ipotetico metodo GetMoves().
Se hai l'occhio avanti potrai rintracciare un ulteriore conflitto anche in questo modello. Infatti i pezzi sembrano sapere autonomamente le dimensioni della scacchiera, quando esse sono solo proprietà della scacchiera stessa. Tuttavia questo è molto più normale di quanto sembri. Infatti il pezzo non sa veramente quali sono le caselle della scacchiera, ma si limita a calcolare la sua posizione prendendo come riferimento il suo tipo di movimento e la posizione iniziale, che gli verrà assegnata proprio dalla scacchiera. Starà quindi alla scacchiera intervenire quando un pezzo tenta di muoversi al di fuori dei limiti consentiti e questo lo puoi gestire con degli eventi.
Puoi elaborare anche una soluzione in cui le caselle sono oggetti prodotti dalla scacchiera: in questo modo puoi allo stesso tempo far dipendere le posizioni dalla scacchiera e passarle come dato ai pezzi, senza che questi sappiano altro.
|