Esempio di Realizzazione di una Classe in Java
- La classe
Stack
implementa uno stack di interi con metodi per aggiungere e rimuovere elementi. - L'incapsulamento dei dati è raggiunto attraverso l'uso di metodi pubblici e variabili di istanza private.
- La classe
TestStack
dimostra l'uso della classeStack
creando due stack e operando su di essi.
Esempio di Realizzazione di una Classe: la classe Stack
Mentre la classe Scatola vista nelle lezioni precedenti è utile per illustrare gli elementi essenziali di una classe, ha poco valore pratico.
Per mostrare il vero potere delle classi, in questa lezione si mostrerà un esempio più sofisticato. Come si può ricordare dalla discussione sulla programmazione orientata agli oggetti (OOP) presentata nelle lezioni precedenti, uno dei benefici più importanti dell'OOP è l'incapsulamento dei dati e del codice che manipola quei dati. Come si è visto, la classe è il meccanismo attraverso il quale l'incapsulamento è raggiunto in Java. Creando una classe, si crea un nuovo tipo di dato che definisce sia la natura dei dati che vengono manipolati che le routine utilizzate per manipolarli. Inoltre, i metodi definiscono un'interfaccia coerente e controllata ai dati della classe.
Quindi, è possibile utilizzare la classe attraverso i suoi metodi senza doversi preoccupare dei dettagli della sua implementazione o di come i dati sono effettivamente gestiti all'interno della classe. In un certo senso, una classe è come un "motore di dati." Non è richiesta alcuna conoscenza di ciò che succede all'interno del motore per utilizzare il motore attraverso i suoi controlli. In effetti, poiché i dettagli sono nascosti, il suo funzionamento interno può essere modificato secondo necessità. Finché il codice utilizza la classe attraverso i suoi metodi, i dettagli interni possono cambiare senza causare effetti collaterali all'esterno della classe.
Per vedere un'applicazione pratica della discussione precedente, si sviluppa uno degli esempi archetipici dell'incapsulamento: lo stack. Uno stack o pila memorizza dati utilizzando un ordinamento LIFO (Last In, First Out: l'ultimo ad entrare sarà il primo ad uscire). Cioè, uno stack è come una pila di piatti su un tavolo: il primo piatto posato sul tavolo è l'ultimo piatto ad essere utilizzato. Gli stack sono controllati attraverso due operazioni tradizionalmente chiamate push e pop. Per mettere un elemento in cima allo stack, si utilizza push. Per togliere un elemento dallo stack, si utilizza pop.
Come si vedrà, è facile incapsulare l'intero meccanismo dello stack.
Ecco una classe chiamata Stack
che implementa uno stack per fino a dieci interi:
// Questa classe definisce uno stack di interi
// che può contenere 10 valori
class Stack {
int[] stck = new int[10];
int tos;
// Inizializza la cima-dello-stack
Stack() {
tos = -1;
}
// Spinge un elemento sullo stack
void push(int elemento) {
if(tos==9)
System.out.println("Stack è pieno.");
else
stck[++tos] = elemento;
}
// Estrae un elemento dallo stack
int pop() {
if(tos < 0) {
System.out.println("Stack underflow.");
return 0;
}
else
return stck[tos--];
}
}
Come si può vedere, la classe Stack
definisce due elementi di dati, due metodi e un costruttore. Lo stack di interi è contenuto dall'array stck
. Questo array è indicizzato dalla variabile tos
, che contiene sempre l'indice della cima dello stack. Il costruttore Stack()
inizializza tos
a –1, il che indica uno stack vuoto. Il metodo push()
mette un elemento sullo stack. Per recuperare un elemento, si chiama pop()
. Poiché l'accesso allo stack è attraverso push()
e pop()
, il fatto che lo stack sia contenuto in un array non è effettivamente rilevante per utilizzare lo stack. Per esempio, lo stack potrebbe essere contenuto in una struttura dati più complicata, come una lista collegata, tuttavia l'interfaccia definita da push()
e pop()
rimarrebbe la stessa.
La classe TestStack
, mostrata qui, dimostra la classe Stack
. Crea due stack di interi, spinge alcuni valori su ciascuno, e poi li estrae.
class TestStack {
// Questo programma dimostra la classe Stack
public static void main(String[] args) {
Stack miostack1 = new Stack();
Stack miostack2 = new Stack();
// spinge alcuni numeri sullo stack 1
for(int i = 0; i < 10; i++)
miostack1.push(i);
// spinge alcuni numeri sullo stack 2
for(int i = 10; i < 20; i++)
miostack2.push(i);
// estrae quei numeri dallo stack
System.out.println("Stack in miostack1:");
for(int i = 0; i < 10; i++)
System.out.println(miostack1.pop());
System.out.println("Stack in miostack2:");
for(int i = 0; i < 10; i++)
System.out.println(miostack2.pop());
}
}
Questo programma genera il seguente output:
Stack in miostack1:
9
8
7
6
5
4
3
2
1
0
Stack in miostack2:
19
18
17
16
15
14
13
12
11
10
Come si può vedere, i contenuti di ogni stack sono separati.
Un ultimo punto riguardo alla classe Stack
. Come è attualmente implementata, è possibile che l'array che contiene lo stack, stck
, sia alterato da codice esterno alla classe Stack
. Questo lascia Stack
aperto a uso improprio o malizia. Nelle prossime lezioni, si vedrà come rimediare a questa situazione.