Incapsulamento

L’incapsulamento è la chiave dellaprogrammazione orientata agli oggetti. Tramite esso, unaclasse riesce ad acquisire caratteristiche di robustezza,indipendenza e riusabilità. Inoltre la suamanutenzione risulterà più semplice alprogrammatore.

Una qualsiasi classe è essenzialmente costituita dadati e metodi. La filosofia dell’incapsulamentoè semplice. Essa si basa sull’accessocontrollato ai dati mediante metodi che possono prevenirnel’usura e la non correttezza dei dati stessi. A livellodi implementazione, ciò si traduce nel dichiarareprivati i membri di una classe e quindi inaccessibili al difuori della classe stessa (a tale scopo esiste ilmodificatore private). Allora, l’accesso ai dati,potrà essere fornito da un’interfaccia pubblicacostituita da metodi dichiarati public, e quindi accessibilida altre classi. In questo modo, tali metodi potrebbero adesempio permettere di realizzare controlli prima diconfermare l’accesso ai dati privati. Sel’incapsulamento è gestito in manieraintelligente, le nostre classi potranno essere utilizzate nelmodo migliore e più a lungo, giacché lemodifiche e le revisioni potranno riguardare solamente partidi codice non visibili all’esterno. Se volessimo fareun esempio basandoci sulla realtà che ci circonda,potremmo prendere in considerazione un telefono. La maggiorparte degli utenti, infatti, sa utilizzare il telefono, ma neignora il funzionamento interno. Chiunque infatti, puòalzare la cornetta, comporre un numero telefonico, econversare con un’altra persona, ma pochi conoscono indettaglio la sequenza dei processi scatenati da queste poche,semplici azioni. Evidentemente per utilizzare il telefono,non è necessario essere un tecnico: basta conoscere lasua interfaccia pubblica, non la sua implementazione interna.

Di seguito è presentata una classe che utilizzal’incapsulamento, gestendo l’accesso ad un saldobancario personale, mediante l’inserimento di un codicesegreto:

 

class ContoBancario {  private String contoBancario = "5000000 di Euro";  private int codice= 1234;  public String getContoBancario(int codiceDaTestare)     {if (codiceDaTestare==codice)  {    return contoBancario;  }else  {    return "codice errato!!!";  }     } }

 

In generale, nella programmazione ad oggetti, si preferiscesempre, dichiarare i dati privati, e semmai fornire allaclasse metodi pubblici di tipo "set" e"get" per accedervi. Possiamo comunque chiamarequesti metodi utilizzando un qualsiasi identificatore,possibilmente significativo: "set" e"get" non sono parole chiave. Per correttezza laclasse Data definita nel modulo 3, dovrebbe essere definitacomunque in questo modo:

 

 

class Data    {  private int giorno;  private int mese;  private int anno;  public void setGiorno(int g)    {if (g>0 && g<=31)  {//Altri controlli conoscendo il mese. . .    giorno = g;   }else . . .     }  public int getGiorno()    {return giorno;    }  public void setMese(int m)    {if (m>0 && m<=12)  {    mese = m;     }else . . .    }  public int getMese()    {return mese;    }  public void setAnno(int a)    {anno = a;   }  public int getAnno()    {return anno;    }   }

 

 

Supponiamo inoltre che esista un’ altra classe chedichiara il seguente blocco di codice:

1)

Data oggi=new Data(); oggi.setGiorno(9);

 

Il lettore avrà notato che utilizzarel’incapsulamento, richiederà un maggior sforzod’implementazione. Tuttavia, questo sforzo saràpresto ricompensato. Infatti, supponiamo di voler apportaremodifiche migliorative alla classe precedente. Per esempio,notiamo che per testare efficacemente il settaggio dellavariabile giorno, dovremmo tener conto anche del valore delmese e dell’anno (se l’anno è bisestile, eil mese è febbraio, allora risulterà legale ilgiorno con valore 29…). Ecco allora che le modificheriguarderanno solo l’implementazione del metodosetGiorno all’interno della classe Data e le classi chedichiarano il blocco di codice 1)non dovranno esseremodificate.

- Prima osservazione sull’incapsulamento:

Sino ad ora abbiamo visto degli esempi di incapsulamentoabbastanza classici, dove nascondevamo all’internodelle classi gli attributi mediante il modificatore private.Nulla ci vieta di utilizzare private, anche come modificatoredi metodi, ottenendo così un incapsulamentofunzionale. Un metodo privato infatti, potrà essereinvocato solo da un metodo definito nella stessa classe, chepotrebbe a sua volta essere dichiarato pubblico. Per esempiola classe ContoBancario definita precedentemente, in unprogetto potrebbe evolversi nel seguente modo:

 

class ContoBancario{  private String contoBancario = "5000000 di Euro";  private int codice= 1234;  public String getContoBancario(int codiceDaTestare)     {return controllaCodice(codiceDaTestare);     }  private String controllaCodice(int codiceDaTestare)     { if (codiceDaTestare==codice) {    return contoBancario; }     else{  return "codice errato!!!";}   }}

 

Ciò favorirebbe il riuso di codice in quanto,introducendo nuovi metodi (come probabilmente accadràin progetto che si incrementa), questi potrebbero risfruttareil metodo controllaCodice.

 

- Seconda osservazione sull’incapsulamento:

Abbiamo affermato che un membro di una classe dichiaratoprivate, diventa "inaccessibile da altre classi".Questa frase è ragionevole per quanto riguardal’ambito della compilazione, dove la dichiarazionedelle classi è il problema da superare. Ma, se cispostiamo nell’ambito della Java Virtual Machine, dove,come abbiamo detto, i protagonisti assoluti non sono leclassi ma gli oggetti, dobbiamo rivalutarel’affermazione precedente. L’incapsulamentoinfatti, permetterà a due oggetti istanziati dallastessa classe di accedere in "modo pubblico" , airispettivi membri privati.

Facciamo un esempio, consideriamo la seguente classeDipendente:

 

class Dipendente{  private String nome;  private int anni; //intendiamo età in anni  . . .  public String getNome()    {return nome;    }  public void setNome(String n)   {nome=n;   } public String getAnni()  {return anni;  } public void setAnni(int n)  {anni=n;} public int getDifferenzaAnni(Dipendente altro)  {return (anni – altro.anni);  }}

 

Nel metodo getDifferenzaAnni notiamo che è possibileaccedere direttamente alla variabile anni dell’oggettoaltro, senza dover utilizzare il metodo getAnni. Il lettoreè invitato a riflettere soprattutto sul fatto che ilcodice precedente è valido per la compilazione, ma, ilseguente metodo

 

public int getDifferenzaAnni(Dipendente altro){  return (getAnni() – altro.getAnni());}

 

favorirebbe sicuramente di più il riuso di codice, equindi è da considerarsi preferibile. 

- Il reference "this":

L’esempio precedente potrebbe aver provocato nellettore qualche dubbio. Sino ad ora, avevamo dato perscontato che accedere ad una variabile d’istanzaall’interno di una classe che la definisce, fosse unprocesso naturale che non aveva bisogno di reference. Adesempio della classe precedentemente descritta Data,all’interno del metodo getGiorno, accedevamo allavariabile giorno, senza referenziarla. Alla lucedell’ultimo esempio ci potremmo chiedere: se giornoè una variabile d’istanza, a quale istanzaappartiene? La risposta a questa domanda che sino ad ora nonavevamo neanche preso in considerazione, è: dipende"dall’oggetto corrente" , ovverodall’oggetto su cui è chiamato il metodogetGiorno. In fase di esecuzione di un certa applicazione,potrebbe essere istanziati due particolari oggetti,supponiamo che si chiamino mioCompleanno e tuoCompleanno.Entrambi questi oggetti hanno una propria variabile giorno.Ad un certo punto, all’interno del programma potrebbepresentarsi la seguente istruzione:

System.out.println(mioCompleanno.getGiorno());

Sappiamo che sarà stampato a video il valore dellavariabile giorno dell’oggetto mioCompleanno, ma dalmomento che sappiamo che una variabile ancheall’interno di una classe potrebbe (e dovrebbe) esserereferenziata, dovremmo sforzarci di capire come fa la JavaVirtual Machine a scegliere la variabile giusta senza avere adisposizione reference! In realtà, se il programmatorenon referenzia una certa variabile d’istanza, almomento della compilazione il codice sarà modificatodal compilatore stesso, che aggiungerà un referenceall’oggetto corrente davanti alla variabile. Ma qualereference all’oggetto corrente? La classe nonpuò conoscere a priori i reference degli oggetti chesaranno istanziati da essa in fase di runtime!

Java introduce una parola chiave, che per definizionecoincide ad un reference all’oggetto corrente: this(questo). Il reference this viene quindi implicitamenteaggiunto nel bytecode compilato, per referenziare ognivariabile d’istanza non esplicitamente referenziata.Ancora una volta Java cerca di facilitare la vita delprogrammatore. In un linguaggio orientato agli oggetti puro,non è permesso non referenziare le variabilid’istanza.

In pratica il metodo getGiorno che avrà a disposizionela J.V.M. dopo la compilazione sarà:

 

public int getGiorno() {  return this.giorno; //il this lo aggiunge }     //il compilatore

 

In seguito vedremo altri utilizzi del reference"segreto" this.

N.B.: anche in questo caso, abbiamo notato un’altro diquei comportamenti del linguaggio, che fanno sì cheJava sia definito come "semplice". Se non ci siamoposti il problema della referenzazione dei membriall’interno di una classe sino a questo punto, vuoldire che anche questa volta, "Java ci ha dato unamano".

- Due stili di programmazione a confronto:

Nel secondo modulo abbiamo distinto le variabilid‘istanza dalle variabili locali. La diversitàtra i due concetti è tale che il compilatore cipermette di dichiarare una variabile locale (o un parametrodi un metodo) ed una variabile di istanza, aventi lo stessoidentificatore, nella stessa classe. La parola chiave this siinserisce in questo discorso nel seguente modo. Abbiamopiù volte avuto a che fare con passaggi di parametriin metodi, al fine di inizializzare variabilid’istanza. Siamo stati costretti, sino ad ora, adinventare per il parametro passato un identificatoredifferente da quello della variabile d’istanza dainizializzare. Consideriamo la seguente classe:

 

class Cliente {  private String nome, indirizzo;  private int numeroDiTelefono;  . . .    public void setCliente(String n,String ind,    int num)   {    nome=n;    indirizzo=ind;    numeroDiTelefono= num;   } }

 

Notiamo l’utilizzo dell’identificatore n perinizializzare nome, num per numeroDiTelefono, e ind perindirizzo. Non c’è nulla di sbagliato in questo.Conoscendo l’esistenza di this però, abbiamo lapossibilità di scrivere equivalentemente:

 

class Cliente{    . . .  public void setCliente(String nome,String indirizzo  int numeroDiTelefono)    {this.nome=nome;this.indirizzo=indirizzo;this.numeroDiTelefono= numeroDiTelefono;    }  . . .}

 

Infatti, tramite la parola chiave this, specifichiamo che lavariabile referenziata, appartiene all’istanza. Diconseguenza la variabile non referenziata sarà ilparametro del metodo. Non c’è ambiguitàquindi, nel codice precedente. Questo stile di programmazioneè da alcuni (compreso chi vi scrive) consideratopreferibile. In questo modo, infatti, non c’èpossibilità di confondere le variabili con nomisimili. Nel nostro esempio potrebbe capitare di assegnare ilparametro n alla variabile d’istanza numeroDiTelefono,ed il parametro num alla variabile nome. Potremmo affermareche l’utilizzo di this aggiunge chiarezza al nostrocodice.

N.B.: il lettore noti che se scrivessimo:

 

class Cliente{    . . .  public void setCliente(String nome,String indirizzo,int numeroDiTelefono)   {nome=nome;indirizzo=indirizzo;numeroDiTelefono= numeroDiTelefono;   }  . . .}

 

il compilatore, non trovando riferimenti espliciti,considererebbe variabili locali le prime incontrate, e diistanza le seconde, in pratica leggerebbe:

 

class Cliente     {    . . .public Cliente(String nome,String indirizzoint numeroDiTelefono){    nome=this.nome;    indirizzo=this.indirizzo;    numeroDiTelefono=this.numeroDiTelefono; }  }

Ovviamente, in questo modo, comunque istanziamo un oggetto diquesta classe, le sue variabili verranno inizializzate airelativi valori nulli (poiché così sonoinizializzate automaticamente le variabili d’istanza).

 



 

 



Ti potrebbe interessare anche

commenta la notizia