Astrazioni sui dati : Specifica ed Implementazione di Tipi di Dato Astratti in Java Sommario cos’è un tipo di dato astratto



Scaricare 395 Kb.
03.06.2018
Dimensione del file395 Kb.


Astrazioni sui dati : Specifica ed Implementazione di Tipi di Dato Astratti in Java


Sommario

  • cos’è un tipo di dato astratto

  • astrazione tramite specifica:

  • specifica

  • implementazione

  • relazione tra specifica ed implementazione (nel seguito)



Perché l’astrazione sui dati

  • il più importante tipo di astrazione

  • il nucleo della programmazione orientata ad oggetti

  • lo scopo è quello di estendere il linguaggio

    • nel nostro caso, Java
  • con nuovi tipi di dato (oltre a quelli primitivi int, String…)

  • quali, dipende dall’applicazione

      • interprete: stacks e tabelle di simboli (frame, ambiente dei metodi)
      • applicazione bancaria: conti
      • applicazioni numeriche: matrici


Cos’è un tipo di dato astratto

  • in ogni caso è

    • un insieme di dati
      • stacks, conti, matrici
    • + un insieme di operazioni
      • per crearli e manipolarli
  • Cosa sta nella specifica?

      • La descrizione astratta dei dati
      • La descrizione delle relative operazioni


Implementazione

  • Cosa sta nell’ implementazione?

      • La rappresentazione (o implementazione) dei dati
      • L’implementazione delle relative operazioni
      • Notate che vedere nella specifica insieme oggetti ed operazioni e’ necessario per realizzare in modo proficuo l’astrazione tramite specifica


Astrazione sui dati via specifica

  • con la specifica vogliamo astrarre dall’implementazione del tipo di dato

    • l’utente deve operare sul tipo di dato indipendendentemente dalla sua rappresentazione
  • Se la specifica non contenesse la descrizione astratta dei data, l’utente vedrebbe direttamente la loro rappresentazione

  • Di conseguenza le modifiche della rappresentazione comporterebbero modifiche dei moduli che usano il tipo di dato



Astrazione sui dati via specifica

  • vedendo gli oggetti insieme alle operazioni, l’astrazione diventa possibile

    • la rappresentazione è nascosta all’utente esterno che accede ai dati solo tramite le operazioni (senza sapere come sono implementate)
    • la rappresentazione è visibile all’implementazione delle operazioni
    • Di conseguenza se una rappresentazione viene modificata, devono essere modificate le implementazioni delle operazioni, ma non i moduli che la utilizzano
      • è il tipo di modifica più comune durante la manutenzione


Come procediamo?

  • Prima definiamo la specifica del tipo di dato astratto

    • Interfaccia con l’utente
  • Poi la implementiamo

    • Puo’ al solito essere fatto in un momento successivo, in modo indipendente dai moduli che usano il tipo di dato


Gli ingredienti della specifica di un tipo di dato astratto

  • Java (parte sintattica della specifica)

    • classe o interfaccia
      • per ora solo classi
    • nome per il tipo
      • nome della classe
    • operazioni
      • metodi di istanza
      • incluso il(i) costruttore(i)
  • la specifica del tipo descrive proprietà generali degli oggetti

    • per esempio la modicabilità
  • per il resto la specifica è essenzialmente una specifica dei metodi

    • strutturata come già abbiamo visto per le astrazioni procedurali
    • l’oggetto su cui i metodi operano è indicato nella specifica da this


Formato della specifica

  • public class NuovoTipo {

  • // OVERVIEW: Gli oggetti di tipo NuovoTipo

  • // sono collezioni modificabili di ..

  • // costruttori

  • public NuovoTipo ()

  • // EFFECTS: ...

  • // metodi

  • // specifiche degli altri metodi

  • }



L’insieme di interi 1

  • public class IntSet {

  • // OVERVIEW: un IntSet è un insieme modificabile

  • // di interi di dimensione qualunque

  • // costruttore

  • public IntSet ()

  • // EFFECTS: inizializza this a vuoto

  • // metodi

  • public void insert (int x)

  • // MODIFIES: this

  • // EFFECTS: aggiunge x a this

  • public void remove (int x)

  • // MODIFIES: this

  • // EFFECTS: toglie x da this

  • public boolean isIn (int x)

  • // EFFECTS: se x appartiene a this ritorna

  • // true, altrimenti false

  • ...}



L’insieme di interi 2

  • public class IntSet {

  • ...

  • // metodi

  • ...

  • public int size ()

  • // EFFECTS: ritorna la cardinalità di this

  • public int choose () throws EmptyException

  • // EFFECTS: se this è vuoto, solleva

  • // EmptyException, altrimenti ritorna un

  • // elemento qualunque contenuto in this

  • }



IntSet: commenti 1

  • public class IntSet {

  • // OVERVIEW: un IntSet è un insieme modificabile

  • // di interi di dimensione qualunque

  • ....

  • }

  • gli oggetti della classe sono descritti nella specifica in termini di concetti noti

    • in questo caso, gli insiemi matematici
  • gli stessi concetti sono anche utilizzati nella specifica dei metodi

    • aggiungere, togliere elementi
    • appartenenza, cardinalità


IntSet: commenti 2

  • public class IntSet {

  • // OVERVIEW: ...

  • // costruttore

  • public IntSet ()

  • // EFFECTS: inizializza this a vuoto

  • ...}

  • un solo costruttore (senza parametri)

    • inizializza this (l’oggetto nuovo)
    • non è possibile vedere lo stato dell’oggetto tra la creazione e l’inizializzazione


IntSet: commenti 3

  • public class IntSet {

  • ...

  • // metodi

  • public void insert (int x)

  • // MODIFIES: this

  • // EFFECTS: aggiunge x a this

  • public void remove (int x)

  • // MODIFIES: this

  • // EFFECTS: toglie x da this

  • ...}

  • modificatori

    • modificano lo stato del proprio oggetto (MODIFIES: this)
    • notare che nè insert nè remove sollevano eccezioni
      • se si inserisce un elemento che c’è già
      • se si rimuove un elemento che non c’è


IntSet: commenti 4

  • public boolean isIn (int x)

  • // EFFECTS: se x appartiene a this ritorna

  • // true, altrimenti false

  • public int size ()

  • // EFFECTS: ritorna la cardinalità di this

  • public int choose () throws EmptyException

  • // EFFECTS: se this è vuoto, solleva

  • // EmptyException, altrimenti ritorna un

  • // elemento qualunque contenuto in this...}

  • osservatori

    • non modificano lo stato del proprio oggetto
    • choose può sollevare un’eccezione (se l’insieme è vuoto)
      • EmptyException può essere unchecked, perché l’utente può utilizzare size per evitare di farla sollevare
      • choose è sottodeterminata (implementazioni corrette diverse possono dare diversi risultati)








Specifica di un tipo “primitivo”

  • le specifiche sono ovviamente utili per capire ed utilizzare correttamente i tipi di dato “primitivi” di Java

  • vedremo, come esempio, il caso dei vettori

    • Vector
    • arrays dinamici che possono crescere e accorciarsi
    • contengono Object (per il principio di sostituzione possiamo metterci qualsiasi oggetto, non direttamente int, bool e char)


Specifica di un tipo “primitivo”

    • La specifica e’ di fatto la descrizione che troviamo nel manuale (a parte il formato dei commenti)
    • Quando usiamo il tipo di dato primitivo non sappiamo come e’ realizzato, ovvero come e’rappresentato il Vector e come sono realizzate le operazioni relative
    • Lo stesso meccanismo di astrazione tramite specifica deve valere per i tipi di dato astratti che definiamo


Vector 1

  • public class Vector {

  • // OVERVIEW: un Vector è un array modificabile

  • // di dimensione variabile i cui elementi sono

  • // di tipo Object: indici tra 0 e size - 1

  • // costruttore

  • public Vector ()

  • // EFFECTS: inizializza this a vuoto

  • // metodi

  • public void add (Object x)

  • // MODIFIES: this

  • // EFFECTS: aggiunge una nuova posizione a

  • // this inserendovi x

  • public int size ()

  • // EFFECTS: ritorna il numero di elementi di

  • // this

  • ...}



Vector 2

  • ...

  • public Object get (int n) throws IndexOutOfBoundsException

  • // EFFECTS: se n<0 o n>= this.size solleva

  • // IndexOutOfBoundsException, altrimenti

  • // ritorna l’oggetto in posizione n in this

  • public void set (int n, Object x) throws IndexOutOfBoundsException

  • // MODIFIES: this

  • // EFFECTS: se n<0 o n>= this.size solleva

  • // IndexOutOfBoundsException, altrimenti

  • // modifica this sostituendovi l’oggetto x in

  • // posizione n}



Vector 3

  • ...

  • public void remove (int n) throws IndexOutOfBoundsException

  • // MODIFIES: this

  • // EFFECTS: se n<0 o n>= this.size solleva

  • // IndexOutOfBoundsException, altrimenti

  • // modifica this eliminando l’oggetto in

  • // posizione n

  • public void insert (Object o, int n) throws IndexOutOfBoundsException

  • // MODIFIES: this

  • // EFFECTS: se n<0 o n>= this.size solleva

  • // IndexOutOfBoundsException, altrimenti

  • // modifica this inserendo o nella posizione n

  • }



Vector: commenti 1

  • public class Vector {

  • // OVERVIEW: un Vector è un array modificabile

  • // di dimensione variabile i cui elementi sono

  • // di tipo Object: indici tra 0 e size - 1

  • ....

  • }

  • gli oggetti della classe sono descritti nella specifica in termini di concetti noti

    • in questo caso, gli arrays
  • gli stessi concetti sono anche utilizzati nella specifica dei metodi

    • indice, elemento identificato dall’indice
  • il tipo è modificabile (come l’array)

  • notare che gli elementi sono di tipo Object

    • Per il principio di sostituzione possiamo mettterci per esempio, non possono essere int, bool e char


Vector: commenti 2

  • public class Vector {

  • // OVERVIEW: un Vector è un array modificabile

  • // di dimensione variabile i cui elementi sono

  • // di tipo Object: indici tra 0 e size - 1

  • // costruttore

  • public Vector ()

  • // EFFECTS: inizializza this a vuoto

  • ...}

  • un solo costruttore (senza parametri)

    • inizializza this (l’oggetto nuovo) ad un “array” vuoto


Vector: commenti 3

  • public void add (Object x)

  • // MODIFIES: this

  • // EFFECTS: aggiunge una nuova posizione a

  • // this inserendovi x

  • public void set (int n, Object x) throws IndexOutOfBoundsException

  • // MODIFIES: this

  • // EFFECTS: se n<0 o n>= this.size solleva

  • // IndexOutOfBoundsException, altrimenti modifica

  • // this sostituendovi l’oggetto x in posizione n

  • sono modificatori

    • modificano lo stato del proprio oggetto (MODIFIES: this)
    • set e remove possono sollevare un’eccezione primitiva unchecked


Vector: commenti 3

  • public void remove (int n) throws IndexOutOfBoundsException

  • // MODIFIES: this

  • // EFFECTS: se n<0 o n>= this.size solleva

  • // IndexOutOfBoundsException, altrimenti modifica

  • // this eliminando l’oggetto in posizione n

  • public void insert (Object o, int n) throws IndexOutOfBoundsException

  • // MODIFIES: this

  • // EFFECTS: se n<0 o n>= this.size solleva

  • // IndexOutOfBoundsException, altrimenti

  • // modifica this inserendo o nella posizione n

  • sono modificatori

  • In entrambi i casi vengono shiftati gli elementi che occorrono dopo la posizione n, in indietro o avanti



Vector: commenti 4

  • public int size ()

  • // EFFECTS: ritorna il numero di elementi di

  • // this

  • public Object get (int n) throws IndexOutOfBoundsException

  • // EFFECTS: se n<0 o n>= this.size solleva

  • // IndexOutOfBoundsException, altrimenti

  • // ritorna l’oggetto in posizione n in this

  • public Object lastElement ()

  • // EFFECTS: ritorna l’ultimo oggetto in this

  • sono osservatori

    • non modificano lo stato del proprio oggetto
    • get può sollevare un’eccezione primitiva unchecked


Vector

  • Altri metodi rimandiamo alla specifica del manuale



Un tipo di dato non modificabile

  • public class Poly {

  • // OVERVIEW: un Poly è un polinomio a

  • // coefficienti interi non modificabile

  • // esempio: c0 + c1*x + c2*x2 + ...

  • // costruttori

  • public Poly ()

  • // EFFECTS: inizializza this al polinomio 0

  • public Poly (int c, int n) throws

  • NegativeExponentExc

  • // EFFECTS: se n<0 solleva NegativeExponentExc

  • // altrimenti inizializza this al polinomio cxn

  • // metodi

  • ...}



I polinomi 2

  • public class Poly {

  • ...

  • // metodi

  • public int degree ()

  • // EFFECTS: ritorna 0 se this è il polinomio

  • // 0, altrimenti il più grande esponente con

  • // coefficiente diverso da 0 in this

  • public int coeff (int d)

  • // EFFECTS: ritorna il coefficiente del

  • // termine in this che ha come esponente d

  • public Poly add (Poly q) throws

  • NullPointerException

  • // EFFECTS: q=null solleva NullPointerException

  • // altrimenti ritorna this + q

  • ...}



I polinomi 3

  • public class Poly {

  • ...

  • // metodi

  • ...

  • public Poly mul (Poly q) throws

  • NullPointerException

  • // EFFECTS: q=null solleva NullPointerException

  • // altrimenti ritorna this * q

  • public Poly sub (Poly q) throws

  • NullPointerException

  • // EFFECTS: q=null solleva NullPointerException

  • // altrimenti ritorna this - q

  • public Poly minus ()

  • // EFFECTS: ritorna -this

  • }



Poly: commenti 1

  • public class Poly {

  • // OVERVIEW: un Poly è un polinomio a

  • // cofficienti interi non modificabile

  • // esempio: c0 + c1*x + c2*x2 + ...

  • ...}

  • gli oggetti della classe sono descritti nella specifica in termini di concetti noti

    • in questo caso, i polinomi
  • gli stessi concetti sono anche utilizzati nella specifica dei metodi

    • operazioni di +, *, e -


Poly: commenti 2

  • public class Poly {

  • // OVERVIEW: ...

  • // costruttori

  • public Poly ()

  • // EFFECTS: inizializza this al polinomio 0

  • public Poly (int c, int n) throws

  • NegativeExponentExc

  • // EFFECTS: se n<0 solleva NegativeExponentExc

  • // altrimenti inizializza this al polinomio cxn

  • ...}

  • due costruttori overloaded

    • stesso nome (quello della classe, in questo caso)
    • diverso numero o tipo di parametri
      • se no, errore di compilazione
    • la scelta tra metodi overloaded viene effettuata in base al numero e tipo di parametri
      • eventualmente a run time scegliendo il più specifico


Poly: commenti 3

  • public class Poly {

  • // OVERVIEW: ...

  • // costruttori

  • public Poly ()

  • // EFFECTS: inizializza this al polinomio 0

  • public Poly (int c, int n) throws

  • NegativeExponentExc

  • // EFFECTS: se n<0 solleva NegativeExponentExc

  • // altrimenti inizializza this al polinomio cxn

  • ...}

  • l’eccezione NegativeExponentExc non è definita qui

    • nello stesso package di Poly?
    • può essere unchecked, perché non è probabile che l’utente usi esponenti negativi


Poly: commenti 4

  • // metodi

  • public int degree ()

  • // EFFECTS: ritorna 0 se this è il polinomio

  • // 0, altrimenti il più grande esponente con

  • // coefficiente diverso da 0 in this

  • public int coeff (int d)

  • // EFFECTS: ritorna il coefficiente del

  • // termine in this che ha come esponente d

  • public Poly add (Poly q) throws

  • NullPointerException

  • // EFFECTS: q=null solleva NullPointerException

  • // altrimenti ritorna this + q

  • ...

  • non ci sono modificatori

    • il tipo è non modificabile!
    • degree e coeff sono osservatori
    • add, mul, sub e minus ritornano nuovi oggetti di tipo Poly


Implementazione

  • scelta fondamentale è quella della rappresentazione (rep)

    • come i valori del tipo astratto sono implementati in termini di altri tipi
      • tipi primitivi o già implementati
      • nuovi tipi astratti che facilitano l’implementazione del nostro
        • tali tipi vengono specificati
        • la scelta deve tener nel dovuto conto la possibilità di implementare in modo efficiente i costruttori e gli altri metodi
  • poi viene l’implementazione dei costruttori e dei metodi (più eventuali altri metodi ausiliari privati)



La rappresentazione

  • in Java, gli oggetti del nuovo tipo sono semplicemente collezioni di valori di altri tipi

    • definite (nella implementazione della classe) da un insieme di variabili di istanza private
      • accessibili solo dai costruttori e dai metodi della classe
      • questo e’ necessario per evitare l’accesso diretto alla rappresentazione dell’oggetto da parte degli utenti


Usi “corretti” delle classi in Java

  • nella definizione di astrazioni procedurali

    • le classi contengono essenzialmente metodi statici
      • eventuali variabili statiche possono servire per avere dati condivisi fra le varie attivazioni dei metodi
        • procedure con stato interno
      • variabili e metodi di istanza (inclusi i costruttori) non dovrebbero esistere, perchè la classe non sarà mai usata per creare oggetti
  • nella definizione di astrazioni sui dati

    • le classi contengono essenzialmente metodi di istanza e variabili di istanza private
      • eventuali variabili statiche possono servire per avere informazione condivisa fra oggetti diversi
      • eventuali metodi statici non possono comunque vedere l’oggetto e servono solo a manipolare le variabili statiche


Implementazione di IntSet 1

  • public class IntSet {

  • // OVERVIEW: un IntSet è un insieme modificabile

  • // di interi di dimensione qualunque

  • private Vector els; // la rappresentazione

  • // costruttore

  • public IntSet ()

  • // EFFECTS: inizializza this a vuoto

  • {els = new Vector();}

  • ...}

  • un insieme di interi è rappresentato da un Vector

    • più adatto dell’Array, perché l’insieme ha dimensione variabile
  • gli elementi di un Vector sono di tipo Object

    • non possiamo memorizzarci valori di tipo int
    • usiamo oggetti di tipo Integer
      • interi visti come oggetti tramite conversione


Implementazione di IntSet 2

  • public void insert (int x)

  • // MODIFIES: this

  • // EFFECTS: aggiunge x a this

  • {Integer y = new Integer(x);

  • if (getIndex(y) < 0) els.add(y); }

  • Inseriamo l’elemento solo se non occorre gia’ nell’insieme

  • Vogliamo che non ci siano occorrenze multiple di elementi

    • implementazione piu’ efficiente (vedi remove e size)
  • A tale fine si utilizza un metodo ausiliario getIndex , che ritorna -1 se l’elemento non occorre



Implementazione di IntSet 2

  • private int getIndex (Integer x)

  • // EFFECTS: se x occorre in this ritorna la

  • // posizione in cui si trova, altrimenti -1

  • {for (int i = 0; i < els.size(); i++)

  • if (x.equals(els.get(i))) return i;

  • return -1; }

  • il metodo privato ausiliario getIndex ritorna un valore speciale e non solleva eccezioni

  • non sta nella specifica (va bene perché è privato)

  • notare l’uso del metodo equals su Integer



Implementazione di IntSet 3

  • public void remove (int x)

  • // MODIFIES: this

  • // EFFECTS: toglie x da this

  • {int i = getIndex(new Integer(x));

  • if (i < 0) return;

  • els.set(i, els.lastElement());

  • els.remove(els.size() - 1);}

  • nella rimozione, se l’elemento c’è, ci scrivo sopra l’ultimo corrente ed elimino l’ultimo elemento

  • E’ corretto sse l’elemento occorre in una sola posizione del vettore………



Implementazione di IntSet 3

  • public boolean isIn (int x)

  • // EFFECTS: se x appartiene a this ritorna

  • // true, altrimenti false

  • { return getIndex(new Integer(x)) >= 0; }

  • Usa il solito metodo ausiliario



Implementazione di IntSet 4

  • public int size ()

  • // EFFECTS: ritorna la cardinalità di this

  • {return els.size(); }

  • E’ corretto sse non ci possono essere occorrenze multiple di elementi nel vettore



Implementazione di IntSet 4

  • public int choose () throws EmptyException

  • // EFFECTS: se this è vuoto, solleva

  • // EmptyException, altrimenti ritorna un

  • // elemento qualunque contenuto in this

  • {if (els.size() == 0) throw

  • new EmptyException(“IntSet.choose”);

  • return ((Integer) els.lastElement()).intValue(); }

  • lastElement non puo’ sollevare eccezioni dato che si effettua un controllo che non sia vuoto

  • ritorna un Object (come tipo apparente), quindi per trasformarlo in int tramite intValue() bisogna fare il cast



Prima implementazione di Poly 1

  • public class Poly {

  • // OVERVIEW: un Poly è un polinomio a

  • // cofficienti interi non modificabile

  • // esempio: c0 + c1*x + c2*x2 + ...

  • private int[] termini; // la rappresentazione

  • private int deg; // la rappresentazione

  • i polinomi non cambiano la dimensione

    • Array invece che Vector
    • l’elemento in posizione n contiene il coefficiente del termine che ha esponente n (eventualmente 0)
    • Array ha la dimensione del grado del polinomio + 1
  • per comodità (efficienza) ci teniamo traccia nella rappresentazione del grado (degree) del polinomio

    • variabile di tipo int


Costruttori

  • // costruttori

  • public Poly ()

  • // EFFECTS: inizializza this al polinomio 0

  • {termini = new int[1]; deg = 0; }

  • public Poly (int c, int n) throws

  • NegativeExponentExc

  • // EFFECTS: se n<0 solleva NegativeExponentExc

  • // altrimenti inizializza this al polinomio cxn

  • if (n < 0) throw new NegativeExponentExc (“Poly(int,int) constructor”);

  • if (c == 0)

  • {termini = new int[1]; deg = 0; return; }

  • termini = new int[n+1];

  • for (int i = 0; i < n; i++) termini[i] = 0;

  • termini[n] = c; deg = n; }

  • il polinomio vuoto è rappresentato da un array di un elemento contenente 0



Costruttori

  • private Poly (int n)

  • {termini = new int[n+1]; deg = n; }

  • un costruttore privato di comodo (crea un array di dimensione n)

  • Non sta nella specifica non è infatti visibile da fuori (lo usiamo dopo……..)



Prima implementazione di Poly 3

  • public int degree ()

  • // EFFECTS: ritorna 0 se this è il polinomio

  • // 0, altrimenti il più grande esponente con

  • // coefficiente diverso da 0 in this

  • {return deg; }

  • public int coeff (int d)

  • // EFFECTS: ritorna il coefficiente del

  • // termine in this che ha come esponente d

  • {if (d < 0 || d > deg) return 0;

  • else return termini[d];}



Prima implementazione di Poly 3

  • public Poly minus ()

  • // EFFECTS: ritorna -this

  • {Poly y = new Poly(deg);

  • for (int i = 0; i < deg; i++)

  • y.termini[i] = - termini[i];

  • return y;}

  • public Poly sub (Poly q) throws

  • NullPointerException

  • // EFFECTS: se q=null solleva NullPointerException

  • // altrimenti ritorna this - q

  • {return add(q.minus()); }



Prima implementazione di Poly 4

  • più complesse

    • ma solo negli aspetti algoritmici
    • le implementazioni di add e mul
      • che non mostriamo
  • se i polinomi sono sparsi

    • questa implementazione non è efficiente
      • arrays grandi e pieni di 0
    • Vediamo una implementazione alternativa in cui solo coefficienti non 0 sono rappresentati


Seconda implementazione di Poly

    • Utilizzare due Vector i cui elementi contengono coefficienti ed esponenti rispettivamente
  • Private Vector coeffs;//coefficienti non-zero

  • Private Vector exps;// esponenti associati

    • Poco efficiente, difficile garantire che l’esponente nella n-esima posizione sia proprio quello dell’n-esimo coefficiente


Soluzione alternativa

    • Utilizzare un Vector i cui elementi sono coppie del tipo (esponente, coefficiente)
    • Come rappresentare le coppie?
    • Tipo di dato record, permette di definire una collezione di campi con un tipo ed un nome
    • I record sono tipi di dato primitivi in molti Linguaggi di Programmazione, non in Java






Un ‘altra rappresentazione di Poly

  • public class Poly {

  • // OVERVIEW: un Poly è un polinomio a

  • // cofficienti interi non modificabile

  • // esempio: c0 + c1*x + c2*x2 + ...

  • private Vector termini; // la rappresentazione

  • private int deg; // la rappresentazione

  • gli oggetti contenuti in termini sono Pair che rappresentano i termini con coefficiente diverso da 0

  • Non possiamo definire un Vector di Pair (lo vedremo in seguito)



Sommario

  • Abbiamo realizzato tipi di dato astratto usando l’astrazione fornita dalla specifica

  • La specifica e l’implementazione stanno nella stessa classe

  • la specifica funge da interfaccia con chi usa il tipo di dato, puo’ anche essere compilata senza avere l’implementazione insieme ai moduli che la usano

  • Specifica ed implementazione sono di conseguenza legate da un concetto di correttezza (che vedremo)



Cos’e un interfaccia?

  • E’ una classe che non contiene implementazione, ma solo gli headers dei metodi (niente costruttori)

  • E’ utile per definire implementazioni multiple di un tipo di dato astratto come sottoclassi dell’interfaccia (si dice che implementano l’interfaccia)



Perche’ non abbiamo usato interfacce?

  • Varie implementazioni sono sottotipi dell’interfaccia

  • Avremmo specifica nel supertipo e l’implementazione nel sottotipo

  • Piu’ difficile ragionare sul legame tra specifica ed implementazione (il sottotipo potrebbe aggiungere metodi, cambiare la specifica…)

  • Vedremo comunque il problema delle relazioni tra tipo e sottotipo a livello di specifiche, e quindi l’uso delle interfacce piu’ avanti



Esempio di una operazione

  • public int coeff (int d)

  • // EFFECTS: ritorna il coefficiente del

  • // termine in this che ha come esponente d

  • {for (int i = 0; i < termini.size(); i++)

  • {Pair p = (Pair) termini.get(i);

  • if (p.exp == d) return p.coeff;}

  • return 0;}

  • notare il casting necessario perche’ get ritorna un oggetto con tipo apparente Object

  • Accediamo direttamente al campo exp del tipo record Pair



Metodi addizionali

  • esistono vari metodi

    • definiti nella classe Object
  • che possono essere

  • ereditati

  • o ridefiniti da qualunque classe

  • Devono essere ridefiniti quando

    • L’implementazione di default non è corretta per la sottoclasse o è poco informativa


equals

  • in Object verifica se due oggetti sono lo stesso oggetto

    • non se i due oggetti hanno lo stesso stato
    • va bene per i tipi modificabili (può essere ereditata, Intset)
      • dove lo stato è variabile
    • dovrebbe essere ridefinita per i tipi non modificabili (Poly)
      • in termini di uguaglianza fra gli stati


equals

  • Nei tipi modificabili

    • Due oggetti che hanno lo stesso stato (ma non coincidono) possono comunque essere distinti
      • Quando ne cambiamo il valore
    • Quindi due oggetti che hanno lo stesso stato non sono equivalenti


clone

  • in Object genera una copia dell’oggetto

    • nuovo oggetto con lo stesso stato
      • copiando il frame delle variabili istanza
  • questa implementazione non è sempre corretta

    • per esempio, in IntSet i campi els dei due oggetti conterrebbero esattamente lo stesso Vector
      • creando una situazione di condivisione (con trasmissione di modifiche) non desiderata
  • il metodo viene ereditato solo se l’header della classe contiene la clausola implements Cloneable

  • se non va bene quella di default si deve reimplementare



toString

  • in Object genera una stringa contenente il tipo dell’oggetto ed il suo Hash code

  • normalmente si vorrebbe ottenere una stringa composta da

    • tipo
    • valori dello stato
  • se se ne ha bisogno, va ridefinita sempre



Esempi da provare

  • specifica dei tipi utilizzati nello stato della semantica operazionale

    • Frame e Stacks modificabili
    • gli altri non modificabili
    • si supponga che gli identificatori, le liste di parametri ed il codice siano stringhe
    • si usino eccezioni per trattare i casi particolari




Condividi con i tuoi amici:


©astratto.info 2019
invia messaggio

    Pagina principale