Java Cap2
Java Cap2
“Java è un linguaggio di programmazione ad oggetti”. Questo è un modo breve di dire che il paradigma di
Java è il paradigma ad oggetti. Ricordiamo che per paradigma si intende “l’insieme degli strumenti
concettuali messi a disposizione dal linguaggio al programmatore, i quali determinano lo stile di scrittura di
un programma e l’approccio alla risoluzione di un problema”.
Come detto, il paradigma orientato agli oggetti richiede al programmatore di concepire un programma
come costituito da un insieme di oggetti che interagiscono tra di loro, scambiandosi messaggi. L’interazione
tra gli oggetti definiti dal programmatore porta alla soluzione del problema.
Per capire meglio la filosofia su cui si basa la programmazione ad oggetti, ricorriamo alla metafora della
fabbrica.
Ad esempio, queste possono essere alcune caratteristiche relative ad ogni scooter prodotto:
Quantità di benzina
Velocità
Chilometri fatti
Colore
Numero di targa
Se acceso
Freccia inserita
Contenuto bauletto
Attenzione! Questo non vuol dire che nel progetto c’è scritta la quantità di benzina di uno scooter, la sua
velocità, i chilometri fatti e così via. Nel progetto c’è scritto che uno scooter, una volta realizzato, sarà
caratterizzato da queste informazioni: uno scooter, ad esempio, ha sempre una quantità di benzina (che
può essere zero, un litro, un litro e mezzo etc). Quindi nella fase di progetto bisogna tenere conto che
queste informazioni ci saranno.
Per quanto riguarda le azioni, queste possono essere azioni eseguite sullo scooter dall’esterno (ad esempio,
da una persona) o azioni che lo scooter esegue internamente: questi due elenchi sono esempi.
Come si vede, le azioni del secondo tipo non si possono comandare dall’esterno ma sono azioni che
riguardano il funzionamento interno di uno scooter e vengono attivate dalle azioni del primo tipo: ad
esempio, non possiamo comandare di espellere gas di scarico, ma se accendiamo uno scooter, di
conseguenza, il gas di scarico uscirà.
Una volta terminato il progetto, gli ingegneri lo consegnano agli operai che inizieranno a costruire modelli
di scooter, secondo le richieste del mercato. Gli scooter usciranno dalla fabbrica più o meno l’uno uguale
agli altri: avranno tutti le stesse caratteristiche e le azioni possibili saranno le stesse; alcune caratteristiche
avranno lo stesso valore (ad esempio, tutti gli scooter usciranno dalla fabbrica con zero litri di benzina),
altre avranno un valore diverso (ad esempio, uno avrà colore rosso, uno nero, uno grigio). Quando uno
scooter è stato prodotto, su di esso sarà possibile eseguire le azioni previste dal progetto; in questo modo
ogni scooter “evolverà” in modo indipendente dagli altri: uno avrà più benzina, uno sarà spento, uno avrà
la spia riserva accesa etc…
Questo esempio è una metafora del paradigma ad oggetti: gli ingegneri rappresentano i programmatori, e
la fabbrica è l’ambiente di programmazione, ovvero il software usato per realizzare un programma. Un
progetto è un blocco di istruzioni che descrive una entità attraverso le sue caratteristiche e azioni: questo
blocco di istruzioni è detto classe. All’interno di una classe vengono specificate le caratteristiche, che
vengono detti attributi, e le azioni, che vengono detti metodi. Quando il programmatore ha terminato la
scrittura di una classe, la manda in esecuzione. La macchina virtuale java è l’ambiente runtime, che
possiamo paragonare alla catena di montaggio dove gli operai producono ciò che è stato progettato. Le
copie in esecuzione di elementi costruiti a partire da una classe vengono detti oggetti. Si dice che un
oggetto è una istanza di una classe, dove la parola istanza indica proprio il risultato della costruzione di un
oggetto a partire da un modello di riferimento. Si usa comunemente anche il verbo “istanziare”, indicando
l’azione di creare un oggetto a partire da una classe. Ogni oggetto, una volta creato, è indipendente dagli
altri: ha una sua copia degli attributi e i metodi agiranno su questi.
La parola chiave public che precede il nome della classe sta ad indicare che la classe è pubblica, cioè il suo
codice sarà accessibile da codice scritto in altre classi. Di solito una classe è pubblica, perché l’obiettivo
della programmazione ad oggetti è quello di risolvere problemi facendo interagire oggetti creati a partire
da una o più classi.
private questo modificatore di accesso indica che l’attributo è privato, ovvero che può essere
utilizzato (letto o scritto) soltanto da istruzioni presente dentro la classe di appartenenza.
Ecco due esempi di attributi che potremmo inserire nella nostra classe Scooter:
Il primo attributo è un booleano che indica se lo scooter è acceso, ed è un attributo pubblico. Il secondo è
un attributo privato che indica il livello di benzina, che viene inizialmente posto a zero. Come vedremo più
avanti, affrontando il concetto di incapsulamento dei dati, è buona norma dichiarare gli attributi privati.
Ora torniamo alla nostra metafora della fabbrica, e ragioniamo su quest’altro elenco di caratteristiche degli
scooter:
Nome del modello
Capacità serbatoio
Dimensioni sedile
Numero specchietti
Elenco spie cruscotto
Queste caratteristiche possono essere trattate come attributi della classe Scooter, ma hanno una differenza
rispetto a quelle già viste: sono caratteristiche il cui valore è unico e condiviso da tutti gli Scooter realizzati.
Infatti, se in fase di progetto abbiamo deciso che, ad esempio, il nostro scooter si chiamerà “Thunder”, il
nome sarà lo stesso e unico sia che gli scooter realizzati siano zero, uno, dieci, cento o oltre. Le
informazioni di questo tipo vengono dette statiche e vengono mantenute in variabili static :
static questo modificatore di accesso indica che l’attributo è statico, ovvero che a prescindere
dal numero di oggetti creati a partire dalla classe, esiste una unica copia condivisa di
questa informazione.
Possiamo dire che una variabile static non è una variabile di un oggetto ma è una variabile di classe: è
una informazione che appartiene al progetto, non ad ogni sua singola realizzazione.
final questo modificatore di accesso indica che il valore dell’attributo non può essere
modificato, dopo la sua inizializzazione.
Ad esempio, il colore di uno scooter viene stabilito in fase di creazione, ma due scooter possono avere
colore diverso: il colore è un attributo final.
Questi due modificatori possono combinarsi insieme: un attributo statico e finale è una unica variabile
condivisa da tanti oggetti dello stesso tipo ed ha un valore immutabile. In pratica è una costante.
- gli attributi final devono ricevere il loro primo valore, che potrà essere diverso per ciascuna
istanza della classe; vedremo più avanti come risolvere questo problema.
- gli attributi static final di solito sono dichiarati pubblici: non ha senso proteggerli
dichiarandoli privati perché sono comunque non modificabili.
- gli attributi static final sono le costanti in questo linguaggio e per convenzione hanno un
nome tutto maiuscolo.
In più, anche sui metodi si possono applicare i modificatori public, private e static:
public questo modificatore, applicato ad un metodo, indica che il metodo può essere chiamato
da istruzioni poste all’interno o esterno della classe
private questo modificatore, applicato ad un metodo, indica che il metodo può essere chiamato
solo da istruzioni poste all’interno della classe
Nel nostro esempio degli di scooter, avevamo individuato delle azioni che potevano essere eseguite sullo
scooter da un soggetto esterno: accendi, spegni, accelera, frena, metti benzina…queste sono azioni che
riguardano l’utilizzo di uno scooter e sono pubbliche. Invece azioni del tipo accendi spia riserva, inietta
carburante, espelli gas di scarico sono azioni che riguardano il funzionamento interno dello scooter, non
azionabili direttamente da un soggetto esterno ma azionate indirettamente da altre azioni. Queste sono
azioni private.
Quindi un metodo privato di una classe può essere chiamato (nel significato informatico di chiamata di
funzione) da metodi privati della stessa classe o da metodi pubblici della stessa classe o di altre classi; un
metodo pubblico di una classe può essere chiamato da metodi pubblici o privati della stessa classe o di altre
classi.
static questo modificatore, applicato ad un metodo, indica che il metodo agisce solo su
attributi statici.
Ricordiamo che un attributo statico è una variabile che ha un valore unico e condiviso per tutti gli oggetti
creati a partire da una classe.
Per spiegare il significato di static, torniamo ai nostri Scooter. Potremmo dire che un metodo statico non
agisce sui singoli scooter, ma sul progetto stesso. Supponiamo, ad esempio, di voler ingrandire il bauletto di
tutti gli scooter già prodotti. Questa azione non viene fatta su un singolo scooter, ma sulla totalità degli
scooter. E’ come se l’azione fosse fatta non su un oggetto, ma sulla classe: nella nostra metafora, gli operai
prendono il progetto stesso e lo vanno a modificare, con effetto immediato su tutti gli scooter già in
circolazione.
Un metodo statico non può agire invece, su attributi non statici: dato che la sua azione è sulla totalità degli
oggetti, non può riferirsi a uno specifico oggetto.
La parola chiave static può combinarsi con public o private, indicando metodi statici pubblici o
privati.
Ragioniamo sul primo aspetto: se ripercorriamo il concetto di dato e struttura dato, possiamo renderci
conto che una classe è una ulteriore evoluzione della struct del linguaggio C, dove oltre alle variabili e
alle strutture dati vengono infilate dentro anche le funzioni.
Nel linguaggio C, una volta dichiarata una struct, era possibile creare tante variabili che avevano la
struttura della struct; in effetti la struct permetteva all’utente di definire un nuovo tipo di dato. Qui il
concetto è simile ed esteso: definendo una classe il programmatore sta definendo un nuovo tipo di dato,
ma in aggiunta definisce le operazioni che sono possibili su quel dato.
Il secondo aspetto è l’accesso ai dati: l’uso dei modificatori public e private permette al
programmatore di impedire un accesso indiscriminato a variabili e funzioni, ma di controllare come essi
vengono usati.
Tornando all’esempio dello scooter, pensiamo all’informazione “quantità di benzina”, che viene mantenuta
nella variabile quantitaBenzina. Se queste fosse pubblica, vorrebbe dire che qualsiasi codice che
interagisce con un oggetto delle classe Scooter può utilizzare la variabile come vuole. E’ un po'
come dire che uno scooter può andare in giro mantenendo la benzina dentro una bacinella, dove chiunque
può versare o prelevare quello che vuole! Così come per gli scooter, anche per la programmazione questo
sarebbe un rischio.
BENZINA
Per questo motivo gli attributi vengono solitamente dichiarati privati, e vengono scritti dei metodi che si
preoccupano di modificare i valori degli attributi, aggiungendo dei controlli su chi e come modifica tali
valori.
Lo schema che segue mostra come l’informazione quantitaBenzina può venire incapsulata, grazie alla
combinazione di metodi pubblici e privati:
La quantità di benzina è un attributo privato: perciò, come si vede, una azione esterna metti/prendi benzina
è impedita. Il programmatore Definisce dei metodi privati per modificare la benzina (aumenta/diminuisci
benzina): i metodi privati realizzano in pratica il funzionamento interno dell’oggetto, quello che non si può
comandare dall’esterno. Le azioni fai rifornimento, accelera sono quelle “offerte” all’esterno: queste fanno
da “filtro”, in parte impediscono l’accesso diretto al dato, dall’altra ne permettono l’uso.
Incapsulamento dati Tecnica che consiste nell’inserire i dati in una struttura in modo che siano
accessibili solo attraverso dei metodi previsti dal programmatore.
Pensandoci bene, l’incapsulamento dati non è solo una forma di proibizione, ma anche un servizio: un
utente generico che usa lo scooter non è interessato a sapere quali e quanti meccanismi ci sono dentro uno
scooter (se si guasta, non è in grado di ripararlo): interessato a sapere come si usa più che come funziona.
Nascondere il funzionamento interno è una garanzia di sicurezza, offrire delle funzionalità per usare lo
scooter senza doversi interessare a cosa accade internamente se si accelera, frena, mette benzina etc è un
“servizio” che facilita l’uso dello scooter.
Dunque l’uso di private e public permette di separare la parte interna del funzionamento di un oggetto
dall’insieme delle funzioni per l’utilizzo dell’oggetto stesso. Definiamo meglio questo concetto:
Interfaccia L’interfaccia di una classe è l’insieme dei suoi metodi pubblici, i quali consentono ad un
soggetto esterno di utilizzare le funzionalità di una classe senza doversi interessare alla sua
implementazione interna.
Information hiding Proprietà che consiste nel nascondere i dettagli di una implementazione
mostrando una «interfaccia» con le operazioni disponibili dall’esterno.
Questo schema mostra il rapporto tra soggetto esterno, interfaccia e dati nascosti.
dati
Accesso
interfaccia
dall’esterno
Nella vita reale ci sono molti esempi di incapsulamento dati e information hiding. Quando preleviamo soldi
ad uno sportello bancomat o compriamo qualcosa ad uno sportello automatico inserendo soldi o una
tessera, le nostre operazioni sono pubbliche, il funzionamento interno che rilascia i soldi o i prodotti
acquistati è privata e non ci consente di mettere le mani al di là dello sportello. Lo sportello è interfaccia
che separa e regola le azioni possibili.
Quando mangiamo, le operazioni di masticare, bere, inghiottire sono pubbliche, in quanto siamo noi a
comandarle ed eseguirle. L’operazione di digestione invece è privata, comandata dal nostro organismo (in
seguito alle nostre azioni precedenti) e a dire il vero molti di noi neanche sanno cosa accade al cibo quando
si avvia la digestione (se fossimo noi a decidere cosa fare potremmo rischiare di avvelenarci o di morire
malnutriti…). Incredibile, il nostro stesso organismo ha delle operazioni private alle quali non abbiamo
accesso!
Questo ultimo schema riassume tutte le combinazioni tra attributi e metodi pubblici, privati, interni ed
esterni alla classe, mostrando con una freccia verde le interazioni permesse e in rosso quelle non permesse: