[Parte 1Parte 3]

La programmazione
orientata agli oggetti: tecniche per lo sviluppo dei programmi.
Francesco Colucci     II° parte
 fcoluc@tin.it
In questo secondo articolo sulla progettazione software object-oriented si discute di un caso reale: lo sviluppo completo di un gioco per Internet.

Ci accingiamo ora a prendere in esame un caso reale: lo sviluppo di un gioco per la Rete Internet. Inizieremo a definire le specifiche, svilupperemo poi un progetto object-oriented seguendo la metodologia vista nel primo articolo e infine, nel successivo articolo, dulcis in fundo, analizzeremo il codice Java del gioco.

Definizione del problema.

Vogliamo creare un puzzle da inserire in una pagina Web. Il gioco consiste nel sistemare 24 numeri, inizialmente disordinati, in ordine, dall'uno sino al ventiquattro, compiendo il minor numero di mosse. I numeri sono disposti su di una matrice che per semplicità chiameremo scacchiera sulla quale compare anche una posizione libera utilizzata per spostare i numeri ad essa adiacenti.
Il gioco, come avrete intuito è una versione ampliata a 24 numeri del famoso gioco del 15, che tanto piace a mia nipote!

Ecco la soluzione proposta.

Specifiche informali.
Questa animazione ripetuta spero aiuti a comprendere ciò che vogliamo realizzare:

Per scambiare i numeri sulla scacchiera è possibile usare i tasti freccia o usare il mouse. Usando i tasti freccia, si può portare il numero adiacente alla posizione libera nella casella vuota; il numero si sposterà nella direzione della freccia. Usando il mouse, si può fare clic su un numero adiacente alla posizione libera ed esso si sposterà nella casella vuota. Attraverso un'adeguata combinazione di spostamenti dei numeri si deve ordinare l'intera scacchiera nel minor numero di mosse.
Il pulsante "New Game" serve per far ripartire un nuovo gioco che inizia con tutti i numeri disordinati casualmente.

- Specifiche generali del software:
- Il gioco deve poter essere eseguito on-line sulla rete Internet.
- Deve essere codificato in un linguaggio object-oriented. La scelta cade inevitabilmente, per le sue caratteristiche, su Java.
- Deve avere una presentazione grafica.

- Specifiche del gioco:
-  La scacchiera deve essere composta da celle che possono contenere numeri da 1 a 24 e una cella vuota.
- Obiettivo del gioco è ordinare i numeri in modo crescente, dalla prima riga fino alla quinta e da sinistra verso destra attraverso spostamenti successivi.
- Può essere spostato solo un numero adiacente alla posizione vuota (un numero a destra, a sinistra, in alto o in basso rispetto alla posizione vuota).
- Vi è un pulsante "New Game" che consente di iniziare un nuovo gioco che parte con tutti i numeri disordinati casualmente. Appena compare la prima volta il gioco è già inizializzato. Il pulsante "New Game" viene attivato attraverso il mouse.
- L'utente deve poter interagire con il gioco sia attraverso il mouse che con i tasti freccia.
- Durante l'esecuzione devono essere visualizzati sia il numero di mosse che il punteggio.

- Specifiche del punteggio del gioco:
- Il giocatore ha a disposizione 800 mosse per terminare il gioco.
- Durante il gioco, dopo ogni 80 mosse, vi è una penalizzazione di 350 punti; inoltre, le righe non ancora ordinate, subiscono un rimescolamento casuale, la cui intensità è proporzionale al numero di mosse compiute fino a quel momento.
- Il punteggio viene assegnato dopo l'ordinamento di una singola riga, e sommato progressivamente ai punteggi assegnati per l'ordinamento di precedenti righe, secondo la seguente tabella:

Riga ordinata

Punti(*)

riga 1 - (1-2-3-4-5)

  1.000

riga 2 - (6-7-8-9-10)

  2.000

riga 3 - (11-12-13-14-15)

  3.000

riga 4 - (16-17-18-19-20)

  4.000

riga 5 - (21-22-23-24- )

 8.000


- Dal punteggio complessivo viene sottratto il numero di mosse compiute sino a quel momento.

- Specifiche dei suoni:
- Un suono sarà emesso dopo ogni mossa
.
- Un suono sarà emesso quando i numeri della scacchiera vengono rimescolati.
- Un suono sarà emesso all'inizio di un nuovo gioco o quando il gioco viene completato con successo.
- Un suono sarà emesso al completamento di una riga.

Disegno software object-oriented.

L'inizio della progettazione software, seguendo i principi esposti nel primo articolo, inizia analizzando la definizione del problema e le specifiche precedentemente indicate. Gli oggetti vengono individuati sia analizzando il testo del problema, che le specifiche prodotte.

Gli oggetti saranno preceduti dal simbolo *, gli attributi dal simbolo +, mentre i metodi dal simbolo --, i nomi delle classi dal simbolo §. 

- Identificazione degli oggetti principali:
Dalle specifiche dei requisiti si possono individuare i seguenti oggetti principali:

* Il Puzzle
L'oggetto puzzle è l'astrazione dell'intero gioco. E' l'oggetto che conterrà tutti gli altri oggetti. Esso è composto da una scacchiera (composta a sua volte da celle), dal pulsante "New Game", dagli indicatori del numero di mosse e di punti. Un puzzle può essere Inizializzato, Eseguito, Interrotto ecc.

* La Scacchiera
La scacchiera dei numeri è un altro oggetto principale. Essa è una matrice di celle ed ha un certo numero di righe e colonne. Poichè ogni cella ha la stessa dimensione, attributi della scacchiera saranno anche la larghezza e l'altezza in pixel di ogni cella. Altro metodo è ScambioCelle che consente lo scambio di una cella che contiene un numero con la cella vuota adiacente. 

* La Calcolatrice
Attraverso questo oggetto si realizza la gestione del numero di mosse compiute e del punteggio durante il gioco. Operazioni della nostra calcolatrice sono Reset per azzerarla, Incremento e Decremento (in caso di penalizzazione) Punti e  Incremento Mosse.

- Individuazione degli attributi degli oggetti principali:

* Il Puzzle

  + TheScac: la scacchiera del gioco.
  + Calc: La calcolatrice del gioco.
 
  + Mossa : suono emesso dopo ogni mossa.
  + Vittoria: suono emesso quando il gioco viene completato o quando viene inizializzato.
  + RigaOrdinata: suono emesso quando una riga viene ordinata.
  + Disordine: suono emesso al momento del rimescolamento dei numeri.
  + g: l'ambiente grafico del gioco.
  + bNewGame: il pulsante "New Game".
  + fileIm: vettore contenente i nomi dei file delle immagini delle celle in ordine (vettore di costanti).

* La Scacchiera
  + Larghezza:  un numero che indica la larghezza in pixel di ogni cella della scacchiera.
 
+ Altezza:  un numero che indica l'altezza in pixel di ogni cella della scacchiera.
 
+ NumRigh: numero di righe della scacchiera. 
   + NumCol: numero di colonne della scacchiera.
 
+ Mappa:  una matrice che contiene le celle con i numeri. (cella è un oggetto secondario da definire)

* La Calcolatrice
   + Mosse:   numero corrente di mosse compiute.
   + Punti:   numero di punti del gioco.

- Identificazione degli oggetti secondari e dei relativi attributi:

* La Cella
   + col: la coordinata colonna della cella nella scacchiera.
   + rig: la coordinata riga della cella nella scacchiera.
   + immagine: l'immagine contenuta nella cella.
 
+ NomeFile: il nome del file contenente l'immagine della cella.
 
+ IsNumero: un valore boolean che indica se la cella contiene un numero (true) o una cella vuota (false).

- Sviluppo di una gerarchia delle classi:
Alcune precisazioni sulla terminologia: finora abbiamo parlato di oggetti, ora invece iniziamo a parlare di classi. Come è noto una classe è un modello, mentre un oggetto viene generato da una classe attraverso una sua istanza. Ora definiremo le classi a partire dalle quali saranno generati gli oggetti di cui abbiamo parlato, oggetti che  serviranno in effetti per far "girare" il programma.
La gerarchia inizia con la super-classe Java "Object" dalla quale discende ogni altra classe.  Component, Container e Panel sono, così come Object, classi predefinite Java che sono state indicate per completezza. La classe principale §Puzzle, dovendo il programma essere integrato in una pagina Web, sarà una sottoclasse della classe Applet. Gli attributi identificati nel progetto sono racchiusi tra le parentesi.

§Object
    §Component
     
§Container
         
§Panel
            §Applet
               §Puzzle(TheScac, Calc, Mossa, Vittoria
, RigaOrdinata,                                   Disordine, g, bNewGame, fileIm)
    §Scacchiera(Larghezza, Altezza, NumRigh, NumCol, Mappa)
    §Calcolatrice(Mosse, Punti)
   
§Cella(col, rig, immagine, NomeFile, IsNumero) 

Identificazione e definizione dei principali metodi per gli oggetti principali e secondari.

* Il Puzzle
-- Init: L'inizializzazione del Puzzle, e quindi dell'applet, avverrà con questo metodo. Tale inizializzazione si verificherà quando il Puzzle sarà caricato. Prevede la creazione e inizializzazione della Scacchiera con l'assegnazione delle immagini alle sue celle, la creazione di una Calcolatrice. Infine il metodo rimescolerà le righe della scacchiera.

-- Run: L'esecuzione del Puzzle avverrà attraverso questo metodo. In effetti il metodo non fa nulla, ma aspetta che l'utente premi un tasto freccia o faccia click su un numero. Quando si verificherà uno di questi eventi, allora sarà eseguito o il metodo handleEvent o keyDown.

-- handleEvent: Questo metodo è un metodo fondamentale perchè avrà il compito di catturare l'evento click del mouse e richiamerà il metodo di ScambioCelle della Scacchiera. Questo metodo intercetterà anche la pressione del pulsante "New Game". 

-- keyDown: Questo metodo realizza una funzione analoga a quella del metodo precedente, ma invece che catturare un evento provocato dalla tastiera, rileverà la pressione di un tasto freccia.

-- stop: Questo metodo fermerà il gioco. 

* La Scacchiera
-- Create: Questo metodo serve per creare nuovi oggetti Scacchiera (è il metodo costruttore)

-- ScambioCelle: Scambia di due celle le immagini, le proprietà "Isnumero" e i files contenenti le immagini. Questo metodo sarà richiamato ogni volta che si dovrà spostare un numero nella posizione libera (cella vuota).

-- Casuale(rig): Dispone casualmente i numeri della scacchiera a partire dalla riga successiva a quella specificata dal parametro rig.

-- Fatto(VetIm): La scacchiera, con questo metodo, restituirà True se i nomi dei file delle immagini del vettore VetIm, passato come parametro, coincideranno, nell'ordine, con i nomi dei file delle immagini delle celle. In tal caso la scacchiera sarà ordinata, in caso contrario restituirà False. Il parametro effettivamente passato al momento dell'invocazione del metodo sarà l'attributo fileIm dell'oggetto Puzzle.

* La Cella
-- Create: Questo metodo serve per creare nuovi oggetti Cella (è il metodo costruttore).

* La Calcolatrice
-- Create: Questo metodo serve per creare nuovi oggetti Calcolatrice(è il metodo costruttore). Questo metodo genererà una nuova calcolatrice con Mosse =0 e Punti =0.

-- DecPunteggio(valore):   Diminuisce il punteggio di una quantità pari al parametro "valore".  Questo metodo sarà usato per realizzare le penalizzazioni di punteggio.

-- IncPunteggio(valore):  Incrementa il punteggio di una quantità pari al parametro "valore".Questo metodo sarà usato per l'incremento di punteggio quando sarà completata una riga.

-- IncMosse: Incrementa di 1 il numero di mosse. Sarà richiamato dopo ogni mossa eseguita.

-- Reset: Resetta la calcolatrice azzerando Mosse e Punti. Sarà richiamato alla partenza di un nuovo gioco, ovvero quando l'utente farà click sul pulsante "New Game".

- Sviluppo di un prototipo di soluzione utilizzando i metodi degli oggetti.

Utilizzando un approccio top-down si possono utilizzare i metodi principali dell'oggetto Puzzle(il gioco) per definire uno scheletro della soluzione software che generata nella fase di codifica. Questo passo rappresenta la linea di confine tra la fase di progettazione e quella di codifica. Si possono prevedere più livelli. 

Ad un primo livello il prototipo di soluzione sarà:
  Puzzle.Init
  Puzzle.Run 
  Puzzle.stop

Ad un secondo livello possiamo specificare meglio cosa accadrà con l'esecuzione dei precedenti metodi:

Puzzle.Init:
   TheScac = new Scacchiera  //crea la scacchiera TheScac
   inizializzazione delle immagini della scacchiera creata
   TheScac.Casuale(0)
   emette il suono del disordine
   Calc = new Calcolatrice()
 //crea la calcolatrice Calc

Puzzle.Run:  
In effetti questo metodo non fa proprio nulla! Ovvero sono i metodi  handleEvent e keyDown che intercettano rispettivamente il click del mouse e la pressione di un tasto all'interno del riquadro del gioco.

Ad un terzo livello del prototipo possiamo specificare i due metodi  Puzzle.handleEvent e Puzzle.keyDown:

Puzzle.handleEvent
    Intercetta le coordinate della cella dove si è fatto click
   TheScac.ScambioCelle  //scambio celle
   Calc.IncMosse             // Incrementa le mosse
   emette il suono della mossa
   Se è stata completata una riga allora
       Calc.IncPunteggio(valore)    //Incrementa il punteggio
       emette il suono della riga completata 
   Se Calc.Mosse è un multiplo di 80 allora
       Calc.DecPunteggio(350)     // Penalizzazione
       Casuale(riga);      // Rimescola le righe non ordinate
       emette il suono del disordine
   Se TheScac.Fatto(fileIm) = true allora
       emette il suono del completamento gioco
       stop      // fine gioco

Puzzle.keyDown
Le operazioni realizzate da questo metodo sono identiche al metodo Puzzle.hendleEvent con la differenza che sono svolte in funzione del tipo di tasto freccia premuto.

- Rielaborazione delle classi per renderle più generiche, possibilmente riusabili e per raggrupparle in moduli.

Le classi Scacchiera, Calcolatrice e Cella vengono, in questo passo, riunite in un Package Java.

Questo è tutto. Nel prossimo articolo analizzeremo il codice delle classi racchiuse nel package chiamato PuzzleClass e il codice dell'applet Puzzle.