Utente:Arien~itwiki

Da Wikipedia, l'enciclopedia libera.
Vai alla navigazione Vai alla ricerca

Le relazioni tipo-sottotipo e classe.sottoclasse NON SONO EQUIVALENTI. · Subtyping: se le istanze di un tipo S possono sostituire istanze di tipo T in tutti i programmi in cui compare T, allora S è un sottotipo di T; · Subclassing: se un tipo C ridefinisce tutte le operazioni di un tipo P, allora C è una sottoclasse di P.

Si vede come la relazione tipo-sottotipo possa esistere anche al di fuori di un contesto gerarchico. Es.: · Posso definire un tipo Punto e un tipo Coordinate_2D. Se vengono definite delle procedure in termini di Punto ma vengono poi usate delle istanze Coordinate_2D, non cambia nulla (à esiste una relazione tipo-sottotipo); · Posso avere il tipo Punto e il tipo Punto_Colorato, derivato da Punto, che aggiunge lo slot Colore; in questo caso si ha una relazione tipo-sottotipo ma anche classe-sottoclasse; · Posso avere i tipi Punto_2D e Punto_3D; questi hanno due metodi diversi per calcolare la distanza: in questo caso c’è una relazione classe-sottoclasse, ma non tipo-sottotipo.

Interpretazione “model-based” della relazione tipo-sottotipo. Un tipo B è un sottotipo di un tipo A quando tutti i valori di B sono anche valori di A. In una relazione tipo-sottotipo, tipi di base possono essere Boolean, Integre, Character, Natural,… I tipi derivati sono: ü Subrange ü Funzioni ü Record ü Vincoli (semantic subtyping)

Subrange. E’ un sottoinsieme di un tipo ordinato. Esiste un ordinamento totale tra gli elementi di un subrange e devono essere contigui. Il costruttore è “..” à 2 .. 4 = { 2, 3, 4} Un elemento di un subrange s .. t, può essere sempre utilizzato in un programma scritto in termini di un subrange σ..τ a patto che s .. t sia contenuto in σ..τ.

Funzioni. Supponiamo di avere 2 funzioni, f e g. La funzione prende come input l’insieme α e produce in uscita l’insieme ρ. g , invece, prende in ingresso a e restituisce r.

Perché tra f e g ci sia una relazione tipo-sottotipo α e a devono essere controvarianti (α deve essere contenuto in a): infatti g potrà essere sempre sostituita ad f solo se è in grado di trattare tutti gli argomenti che f può ricevere dal programma in cui è inserita. Inoltre, ρ ed r devono essere covarianti (r deve essere contenuto in ρ): infatti g deve produrre dei risultati che possano essere trattati nel programma in cui f è inserita.

Record. E’ il prodotto cartesiano di n tipi A1, A2, …, An che possono essere considerati come tipi funzione. La relazione tipo-sottotipo tra record può essere definita in 2 modi: 1) Definizione di campi addizionali (record extension); 2) Ridefinizione di campi già esistenti (record overriding).

Per quanto riguarda la prima modalità, se abbiamo i tipi: Point ={x: Integer, y: Integer} Colored_Point = {x: Integer, y: Integer, select: Color} Colored_Point è un sottotipo di Point, perchè il suo campo addizionale select può essere trascurato in programmi definiti in termini di Point.

Si può quindi dire che un record con n campi può essere considerato sottotipo di un record con k campi, se i campi in comune hanno gli stessi tipi.

Seconda modalità: questa volta abbiamo i tipi:

Point = { x: Integer, y: Integer} Positive_Point = {x: Positive, y: Positive} Dove Positive = {k : Integer | k > 0}

Un elemento di tipo Positive_Point può sempre sostituire un elemento di tipo Point in un programma definito in termini di Point. In altre parole, due record con n campi sono in relazione tipo-sottotipo se esiste una relazione tipo-sottotipo tra i campi del primo rispetto al secondo.

Componendo le due regole si può affermare che un record con n campi può essere considerato sottotipo di un record con k campi (k<=n) se i primi k campi sono in relazione tipo-sottotipo.

Vincoli. Un vincolo su un tipo A è una funzione che mappa ogni elemento di A in un insieme {false, true}. Quindi un vincolo p (x) può sempre generare un nuovo tipo B <= A. - aggiungendo uno o più vincoli agli assiomi di un tipo A, si genera un sottotipo B perché, in generale, qualche elemento del tipo A sarà escluso da B, poiché non soddisfa ai vincoli addizionali (additional constraint); - Più in generale, cambiare i vincoli associati ad un tipo produce un nuovo sottotipo se i nuovi assiomi implicano quelli originali (generalized constraint).

Bisogna precisare che ogni assioma deve essere distinto dagli altri e non deducibile da quelli rimanenti.

Principio di sostituibilità di Liskov (LSP).

“Dati due tipi S e T, se gli elementi di S si comportano come quelli di T per tutti i programmi P in cui compare T, allora S è un sottotipo di T.”

Oppure: moduli che usano riferimenti ad una classe base devono poter usare riferimenti alle sue classi derivate, senza accorgersi della differenza. Le relazioni tipo-sottotipo definite nella formalizzazione model-based costituiscono condizioni necessarie ma non sufficienti per garantire LSP. LSP implica delle proprietà che devono essere vere all’interno di gerarchie in cui sono definite delle relazioni classe-sottoclasse. Se C è una sottoclasse di P, affinché C possa essere anche sottotipo di P: 1) C deve supportare tutte le operazioni di P 2) Gli invarianti di classe di C devono essere gli stessi o più forti di quelli di P (gli invarianti di classe non sono legati alle operazioni ma direttamente alla classe:devono risultare veri prima e dopo l’applicazione di un’operazione) 3) Le precondizioni definiscono i requisiti minimi che devono essere soddisfatti dal chiamante perché il metodo vada a buon fine: per ogni metodo m in P, le precondizioni P.m devono essere le stesse o più forti di quelle di C.m 4) Le postcondizioni definiscono i requisiti per cui l’esecuzione del metodo può ritenersi corretta: per ogni metodo m in P le postcondizioni di P.m devono essere più deboli o le stesse di quelle di C.m

Le ultime 2 regole possono essere spiegate come segue: quando un client chiama un metodo, conosce solo le pre e post condizioni della classe base: non è in grado di fornire precondizioni più stringenti e non può accettare postcondizioni meno stringenti. Quindi le precondizioni vincolano il chiamante mentre le postcondizioni vincolano il metodo: se il chiamante deve garantire le precondizioni, il metodo deve garantire le postcondizioni. Quindi se ne può dedurre che i parametri di input di m in P e C devono essere controvarianti, i parametri di output di m in P e in C devono essere covarianti e i parametri di input-output devono essere dello stesso tipo. Una relazione classe-sottoclasse in cui LSP è soddisfatto è anche una relazione tipo-sottotipo. Varie forme di derivazione di classe. Static vs. Dynamic Binding -Se non c’è dynamic binding, un’istanza di una sottoclasse è indistinguibile da un’istanza della classe genitore in tutti i programmi definiti in termini di quest’ultima (LSP vale!) -Se c’è il dynamic binding un’istanza di una sottoclasse può avere comportamenti uguali o diversi rispetto a quelli di un’istanza della classe genitore in tutti i programmi definiti in termini di quest’ultima (LSP può valere o no). · Specializzazione (specialization). C’è una relazione is-a (cioè quella che sussiste tra classi e sottoclassi e corrisponde al concetto di eredità) tra C (sottoclasse) e P (superclasse). C può sovrascrivere il comportamento di P e soddisfa le specifiche di P in tutti i comportamenti rilevanti. à Preserva LSP. · Specificazione (specification). P è una classe astratta, cioè specifica ALMENO UN COMPORTAMENTO CHE VA IMPLEMENTATO DA C (e dalle eventuali altre classi derivate). Tipicamente si ha la definizione di un’interfaccia comune per un gruppo di classi concrete. à Preserva LSP. · Costruzione (construction). Non sussiste una relazione tipo-sottotipo, ma una classe-sottoclasse per motivi legati al riutilizzo del codice. à Spesso viola LSP. · Generalizzazione (generalization). C sovrascrive almeno un metodo di P e ne estende il comportamento a un tipo più generale (è l’opposto della specializzazione). A volte viene usato quando c’è da costruire una classe a partire da una classe non modificabile. à Viola LSP. · Estensione (extension). C aggiunge delle nuove funzionalità a P, ma non cambia alcun comportamento ereditato. Questa tecnica la si usa per implementare nuovi comportamenti a partire da una classe che non può essere modificata. à Preserva LSP. · Limitazione (limitation). C impedisce l’uso di almeno un comportamento ereditato da P. à Viola LSP. · Varianza (variance). C e P sono varianti l’una dell’altra e la relazione classe-sottoclasse è arbitraria. à viola LSP, sarebbe meglio definire una classe più generale da cui derivare sia C che P. · Combinazione (combination). C eredita caratteristiche da diverse classi genitore (è l’eredità multipla!).