Incapsulamento : chiarimenti

di il
3 risposte

Incapsulamento : chiarimenti

Buona serata a tutti e un anticipato GRAZIE per un Vostro eventuale aiuto.
Da pensionato, e quindi non molto sveglio, sto cercando di studiare la programmazione in Java: reputo di andare avanti molto bene grazie ad un manuale, di cui non faccio nome per regolamento. Ora vorrei chiedere un aiuto a Voi che siete esperti:
Il capitolo del manuale è: come usare l'incapsulamento. Cito un condensato per farvi capire il problema:
Ma bisogna per forza dichiarare entrambi i metodi "set" e "get" per ogni variabile.
................................................
Ci sono, per esempio, casi in cui c'è bisogno di dichiarare classi le cui variabili d'istanza possono essere impostate una sola volta nel momento in cui si istanziano, e il cui valore non cambierà nel tempo. In quel caso basterà non fornire i metodi "set", e sfruttare un costruttore per la prima e unica impostazione.
Per esempio la seguente classe "Punto" estrae il concetto di punto "fisso":


public class PuntoFisso {
private int x, y;

public PuntoFisso (int x, int y) {
this.x = x;
this.y = y;
}
public int getX( ) {
return x ;
}

public int getY( ) {
return y ;
} 
}
[code]
infatti il valore delle variabili x ed y non sono modificabili una volta istanziato un oggetto:
[/code]
PuntoFisso puntoFisso = new PuntoFisso(3, 5);
// non esistendo i metodi "set".
Ho trascritto la parte del testo del manuale sperando di farmi capire.

Io vedo però che nel programmino sopra trascritto se cambio i due parametri , per esempio da (3, 5) a (120, 33) il programma gli accetta bellamente.
Chiedo a Voi, dove non riesco a capire il concetto che se non uso i metodi "set" i valori impostati non cambiano.
Grazie.
Marco

3 Risposte

  • Re: Incapsulamento : chiarimenti

    Il concetto e' abbastanza semplice: e' tutta questione di 'filosofia'.

    Considera l'oggetto (un intero) '33' (trentatre): questo e' un oggetto ATOMICO, cioe' non ulteriormente suddivisibile, anche se, volendo, lo potresti vedere come l'insieme di DUE cifre decimale.
    Ma nel 99.9999% dei casi, non ti interessa sapere di quante cifre e' composto, ma solo del suo valore (medico al paziente: 'dica trentatre')

    Ora pensa ad una casella della scacchiera (dama, scacchi, ...), oppure ad un punto su un foglio di carta millimetrata: quella casella o quel punto avranno ben determinate coordinate ('riga 3, colonna=A', 'x=3, y=3').

    Consideriamo il caso della carta millimetrata: ti serve un oggetto Java per rappresentare quel 'punto': supponiamo, guarda i casi della vita, che questo oggetto tu lo voglia rappresentare mediante una classe, dal nome 'Punto' e da due membri, 'x' e 'y', che rappresentano le 'coordinate cartesiane' di quel punto (x=3, y=3).

    E qui' sta la parte 'filosofica': come vuoi 'considerare' una 'istanza' della classe Punto (ad esempio il punto con coordinate x=3, y=5)?
    Ci sono due possibilita':

    1) come un oggetto ATOMICO, cioe' indivisibile, immutabile, come il '33' di prima, formato da DUE numero (x=3, y=5), ma che rimarranno quelli per tutta la vita di quell'oggetto, e quindi se ti serve il punto (x=120,y=33), DEVI creare un nuovo oggetto, ed in questo caso scriveresti
    
    p1=new Point(3,5);
    p2=new Point(120,33);
    
    OPPURE

    2) come un CONTENITORE, uno scatolotto che OGGI contiene i valori x=3 e y=5, ma un domani potrebbe contenere i valori x=120,y=33, ed in questo caso scriveresti:
    
    p1=new Point(3,5);
    p2=p1; p2.setx(120); p2.sety(33);
    
    Ora, OSSERVA BENE questo secondo esempio (che puoi provare a scrivere in Java).
    p2 e' il punto di coordinate (x=120,y=33), ma QUALI coordinate sono rappresentate dal punto p1?

    Come scoprirai con piacere/ORRORE, p1 ha ESATTAMENTE le coordinate di p2 (x=120,y=33)!
    Ma p1 non doveva essere il punto di coordinate x=3,y=5?

    La questione filosofica sta' tutta qui!
    Se
    1) vuoi SCONGIURARE ASSOLUTAMENTE questa situazione (e nel caso di Punto, ha perfettamente senso), allora il tuo oggetto deve venir inizializzato SOLO passando i parametri al costruttore
    2) il tuo oggetto e' un CONTENITORE e quindi, come tale, ti servono i metodi setter (setx(), sety() ) per cambiare i valori CONTENUTI di x e y.

    La presenza dei 'getter' ('getx()', 'gety()') NON E' OBBLIGATORIA: e' comoda, perche ti puo' servire per SMONTARE l'oggetto Punto in ALTRE PARTI del programma, ma se ti vuoi assicurare che NESSUNO possa accedere alle coordinate, ecco che NON DEVI fornire i metodi getter.

    Quindi, ricapitolando:
    a) i metodi 'setter' si definiscono SE vuoi che il tuo oggetto sia un CONTENITORE e non un OGGETTO ATOMICO
    b) i metodi 'getter' si definiscono SE vuoi che CHIUNQUE possa accedere alle informazioni INTERNE del tuo oggetto.

    Questo concetto 'filosofico' si chiama information hiding / incapsulamento

    Ed ora un ultimo passetto:
    ma PERCHE' devo usare dei metodi getter e setter per poter cambiare o leggere i valori di x e y?
    Perche' non posso scrivere semplicemente:
    
    p1 = new Point();
    p1.x = 3; p1.y = 5;
    
    p2 =p1; p2.x = 120; p2.y= 33;
    
    (in questo caso STO COSCIENTEMENTE consideranto Point come un CONTENITORE !!!) ?
    Per fare questo e' semplice: basta rendere x e y public.

    In realta' non c'e' NESSUNO che te lo impedisca: e' di nuovo una questione 'filosofica'.
    L'uso di metodi per accedere ai dati interni di un oggetto ti apre la possibilita', un domani, di poter aggiugere delle logiche extra nell'accesso a tali valori, cosa che, evidentemente, non puoi fare se accedi ai dati interni direttamente.
  • Re: Incapsulamento : chiarimenti

    Marco Mascardi ha scritto:


    Il capitolo del manuale è: come usare l'incapsulamento. Cito un condensato per farvi capire il problema:
    Ma bisogna per forza dichiarare entrambi i metodi "set" e "get" per ogni variabile.

    Ci sono, per esempio, casi in cui c'è bisogno di dichiarare classi le cui variabili d'istanza possono essere impostate una sola volta nel momento in cui si istanziano, e il cui valore non cambierà nel tempo. In quel caso basterà non fornire i metodi "set", e sfruttare un costruttore per la prima e unica impostazione.
    Attenzione a distinguere bene i due concetti.

    Uno è il concetto di "incapsulamento" (più in generale di information hiding). Information hiding vuol dire in generale nascondere "dettagli" (dati ma anche "comportamenti") che per altre parti di codice esterne non sono (non dovrebbero essere) importanti da sapere e conoscere, rendendo quindi più semplice e facile modificare quelle parti nascoste senza andare poi ad impattare direttamente su N altre parti di codice esterne.

    L'aspetto dei metodi getter/setter riguarda l'incapsulamento dei dati, che è un caso particolare di information hiding. Perché si dovrebbe "nascondere" un campo e mettere i metodi accessori pubblici? C'è l'aspetto filosofico/concettuale: è veramente importante dall'esterno sapere che una classe ha un campo xyz di tipo X? In generale (e senza avere uno scenario preciso) probabilmente no.

    Ma poi c'è l'aspetto pratico/tecnico: se un campo è accessibile direttamente dall'esterno, allora l'accesso a quel campo è sparpagliato in chissà quanti punti della applicazione e la classe che contiene il campo NON ne ha il controllo. Il seguente esempio rende sicuramente più chiaro.

    Immagina una classe Rettangolo con i campi altezza e larghezza (double o int ... il tipo non importa molto ora). Se i due campi fossero accessibili dall'esterno, chiunque (e ovunque) può settarli quando e come gli pare. Potrebbe anche impostare valori errati o inappropriati. In questo caso di un rettangolo, non ha senso assegnare ad altezza/larghezza dei valori negativi. Ma se sono pubblici, chiunque può settare es. -1 .

    E se volessi impedire questo? Basta "incapsulare" l'accesso al campo e mantenere il controllo su quello che si definisce un "invariante". Un invariante è una proprietà che rappresenta una condizione che deve essere sempre vera durante il ciclo di vita di un oggetto, di una struttura dati o in generale di una applicazione.
    In questo caso l'invariante che vogliamo mantenere è semplice: "altezza e larghezza non devono essere negativi". Questo principio deve rimanere per tutti gli oggetti Rettangolo. Quindi si può fare es.
    private double altezza;
    
    public void setAltezza(double altezza) {
        if (altezza < 0) {
            throw new IllegalArgumentException("altezza non può essere negativa");
        }
    
        this.altezza = altezza;
    }
    In questo modo la classe mantiene il controllo sul valore del CAMPO. A meno di usare truschini con la reflection, non è possibile accedere direttamente al campo altezza (idem da fare per larghezza). Bisogna passare dal setAltezza, che applica il controllo in modo da garantire l'invariante detto prima. Se l'accesso fosse diretto al campo, questo non si potrebbe garantire.

    -------------------

    Un altro concetto è quello della "immutabilità". Un oggetto può essere reso immutabile semplicemente facendo in modo che il suo "stato" NON possa cambiare dopo che l'oggetto è stato istanziato. E questo vuol dire: sicuramente niente metodi "setter" e in generale nessun altro metodo che vada a cambiarne lo stato.
    Per essere precisi, è importante che sia lo stato visto dall'esterno che non cambi. Un oggetto immutabile potrebbe avere dello stato mutabile, che cambia, purché questo non traspaia all'esterno. C'è un caso "eccellente": la classe String, che è immutabile (la sequenza di caratteri non può essere modificata) ma il suo hash (che è un campo interno restituito dal hashCode()) viene calcolato solo al primo uso (non quando l'oggetto String viene creato). Ma questa è solo una ottimizzazione, e dall'esterno non si vede alcun cambiamento.

    In generale ha senso rendere immutabile una classe che rappresenta un valore ben preciso (tipicamente "piccolo", con pochi dati) e che per il concetto d'uso non ha senso che cambi. Pensa ad una classe CartaDaGioco (con i campi seme e valore). Se creo un oggetto per un cinque di picche, a meno di ragioni ben precise (che in questo momento non saprei) non ha molto senso che quell'oggetto possa cambiare e diventare es. un 7 di quadri !

    La immutabilità comunque ha diversi VANTAGGI, specialmente se entriamo nell'argomento del multi-threading e "concorrenza" in generale. Se un oggetto è immutabile può essere tranquillamente condiviso tra più thread, "cachato" in qualche cache o memorizzato in qualche ambito "globale". Senza doversi preoccupare di cosa potrebbe succedere se cambiasse, dato che appunto non può cambiare.

    Marco Mascardi ha scritto:


    grazie ad un manuale, di cui non faccio nome per regolamento
    Non mi pare sia vietato dire quale libro si sta studiando. Più volte utenti hanno chiesto informazioni su libri o hanno fornito (anche io) dritte su libri da valutare e studiare.
  • Re: Incapsulamento : chiarimenti

    Buona sera a tutti gli amici del forum i programmatori.
    Ringrazio per la risposta al mio quesito " migliorabile del 02 03 2017.
    Ringrazio " andbin " anche lui per la valida risposta.
    Non sono ancora in grado di potervi dire di aver capito totalmente quello che Voi avete cercato di chiarirmi, per problemi personali oggi ho appena letto
    i Vostri chiarimenti ma non ho potuto studiarli seriamente. Con certezza appena sarò riuscito ad acquisirli ve ne darò notizia.
    Per quanto riguarda il testo che provvede ad insegnarmi la programmazione Java è: " Manuale di Java 8 di Claudio De Sio Cesari".
    Mi scuso con "andbin" per non aver menzionato tale titolo , ma sinceramente credevo che non fosse giusto menzionare nomi di autori e di testi.
    Ringrazio per avermi dato un ragguaglio ed un grande supporto. Grazie.
    Nuovamente grazie per le Vostre delucidazioni. Sarete informati se il mio cervello sarà riuscito a percepirle.
    Grazie ancora,
    Marco Masscardi
Devi accedere o registrarti per scrivere nel forum
3 risposte