INTRODUZIONE ALLA MODELLAZIONE UML

Transcript

INTRODUZIONE ALLA MODELLAZIONE UML
INTRODUZIONE
ALLA MODELLAZIONE
UML
UC n. 4: Basi di dati
[email protected]
2
Che cos’è UML?
• UML è un linguaggio di modellazione object-oriented
• Versione 1.0 definita come standard nella seconda metà
degli anni ’90 sotto l’egida dell’Object Management Group
(OMG)
• Versione corrente 2.4.1 (Agosto 2011)
(http://www.uml.org)
• Ma
• Cos’è un linguaggio di modellazione?
• Cosa significa standard? Perché è importante?
• E, soprattutto, a cosa serve?
3
Modelli (1/2)
• Modello di un sistema
• Rappresentazione di un sistema (complesso) che ne descrive le
proprietà ed il comportamento, astraendo da dettagli meno rilevanti
• Quali sono i dettagli meno rilevanti?
• Dipende dagli obiettivi di analisi
• Dipende dagli obiettivi di comunicazione
• Ad esempio
• Se doveste descrivere il funzionamento di un’automobile, quale
descrizione la più adeguata?
4
Modelli e astrazione: un esempio
• Altissimo Livello
• Struttura: corpo macchina
• Interfaccia: Volante, pedali, cambio
• Comportamento: la pressione dei pedali regola l’accelerazione/decelerazione
dell’automibile, ...
• Livello Medio
• Struttura: abitacolo, ruote, trasmissione, motore …
• Interfaccia: Angolo di torsione del volante, apertura valvola dell’accelerazione,
pressione dell’impianto frenante, …
• Comportamento: …
• Basso Livello
• Struttura: schemi dettagliati di progetto dei singoli componenti (moduli)
dell’automobile
• Interfaccia: Ingressi specifici per ciascuno dei sottosistemi
• Comportamento: a cosa servono e come interagiscono i diversi
sottocomponenti?
5
Linguaggio di modellazione
• Nell’esempio (banale) precedente abbiamo usato il
linguaggio naturale
• Può essere ambiguo e/o più prolisso del necessario
• Non soddisfa i requisiti di precisione ed espressività necessarie in
ogni tipo di contesto di progettazione
• Un linguaggio di modellazione definisce in maniera
formale, precisa, e non ambigua
• La notazione con la quale descrivere (modellare) i sistemi
• Il significato esatto di ogni simbolo della notazione
• Proprietà desiderabili
• Facilmente utilizzabile e comprensibile da progettisti umani
• Interpretabile ed utilizzabile da strumenti automatici al calcolatore
6
Perché standard?
• (da http://www.certifiedopen.com/ )
Uno Standard Aperto si riferisce a un formato o ad un protocollo che
è:
• soggetto a una completa valutazione pubblica e a un uso privo di obblighi in
•
•
•
•
modo che sia ugualmente disponibile a tutte le parti;
[…]
libero da clausole legali o tecniche che ne limitino l'uso da parte di qualsiasi
soggetto o in qualsiasi modello di business;
[…]
disponibile in molteplici e complete implementazioni realizzate da distributori
concorrenti, o come una completa implementazione a disposizione di tutti i
soggetti in modo uguale.
• In particolare il fatto che UML sia standard permette
• Di essere usato, supportato e compreso dalla stragrande maggioranza dei
progettisti sviluppatori
• Di disporre di un buon numero di strumenti di supporto per creare / leggere ed
elaborare documenti UML
7
Unified Modeling Language
• Insieme di notazioni grafiche utilizzabili per descrivere sistemi e
componenti software
• Ad ogni elemento grafico corrisponde un concetto ben preciso
descritto formalmente nella specifica UML
• Adotta i principi della programmazione OO per descrivere
componenti software (non necessariamente scritti usando
linguaggi OO)
• E’ utlizzabile in ogni fase della progettazione con diversi
livelli di astrazione/dettaglio
• Definisce un insieme di possibili tipi di diagramma che possono
essere usati per descrivere aspetti differenti di un software
• Nell’ambito di ciascun diagramma possono essere usati elementi
grafici differenti
8
Processo di sviluppo del software
Unified Modeling Language
• Diagrammi strutturali
•
•
•
•
•
•
Diagramma delle classi
Diagramma degli oggetti
Diagramma dei componenti
Diagramma dei package
Diagramma di deployment
Diagramma delle strutture composite (UML 2.0)
• Diagrammi comportamentali
•
•
•
•
•
•
•
Diagramma dei casi d’uso
Diagramma delle attività
Diagramma degli stati
Diagramma di sequenza
Diagramma di comunicazione (ex di collaborazione)
Diagramma dei tempi (UML 2.0)
Diagramma di sintesi dell’interazione (UML 2.0)
UML: DIAGRAMMA
DELLE CLASSI
10
Diagramma delle classi UML
• Forse il diagramma UML più utilizzato e compreso (non
necessariamente il più utile)
• Descrive i tipi di oggetti in un sistema e le loro relazioni
• Si dice anche che rappresenta le relazioni statiche tra le classi
• Non modella, invece, il comportamento dei singoli oggetti e le loro
interazioni
• L’elemento base del diagramma delle classi è – senza
sorprese – la Classe
• Stesso concetto di classe che esiste nella programmazione OO
• Classe: proprietà (solitamente private) + operazioni sullo stato
(interfaccia della classe)
11
Jump Start
• Descrizione in UML della
Film
+titolo
+regista
+anno
+numSpettatori
+calcolaIncasso(in prezzoBiglietto)
classe Film
• Rappresentazione di alto
livello della struttura della
classe
• Sapreste intuitivamente dire il
significato del diagramma?
• Sapreste scrivere una classe
corrispondente in Java?
• C’è qualcosa che secondo voi
manca?
12
Film – Mapping in Java
public class Film {
public ? titolo;
public ? regista;
public ? anno;
public ? numSpettatori;
public calcolaIncasso(? prezzo) {
}
}
13
Film – Mapping in Java
public class Film {
public ? titolo;
public ? regista;
public ? anno;
public ? numSpettatori;
public calcolaIncasso(? prezzo) {
}
}
• Cosa manca?
14
Film – Una specifica più completa
Film
+titolo : string
+regista : string
+anno : int
+numSpettatori : int
+calcolaIncasso(in prezzoBiglietto : float) : float
public class Film {
public String titolo;
public String regista;
public int anno;
public int numSpettatori;
public calcolaIncasso(float prezzo) { … }
}
15
Film – Una specifica più completa
Film
+titolo : string
+regista : string
+anno : int
+numSpettatori : int
+calcolaIncasso(in prezzoBiglietto : float) : float
public class Film {
public String titolo;
public String regista;
public int anno;
public int numSpettatori;
• Ci sono altri problemi?
public calcolaIncasso(float prezzo) { … }
}
16
Film – Una specifica (ancora) più
completa
Film
-titolo : string
-regista : string
-anno : int
-numSpettatori : int
+Film(in titolo : string, in regista : string, in anno : int)
+getTitolo() : string
+getRegista() : string
+getAnno() : string
+getNumSpettatori() : int
+setNumSpettatori(in num : int) : void
+calcolaIncasso(in prezzoBiglietto : float) : float
17
Film – Una specifica (ancora) più
completa
Film
-titolo : string
-regista : string
-anno : int
-numSpettatori : int
+Film(in titolo : string, in regista : string, in anno : int)
+getTitolo() : string
+getRegista() : string
+getAnno() : string
+getNumSpettatori() : int
+setNumSpettatori(in num : int) : void
+calcolaIncasso(in prezzoBiglietto : float) : float
• Esercizio: implementare in Java la classe
descritta nel diagramma UML
18
Esercizio 2
• Obiettivo: Familiarizzare con il software di modellazione UML
che utilizzeremo nel corso
• Scaricare il software all’indirizzo http://argouml.tigris.org
• ArgoUML: software per la creazione di modelli UML scritto in
Java
• Gratuito ed OpenSource
• Supporto di un buon numero di elementi UML
• Capacità di generare (parti di) codice a partire dai diagrammi in diversi
linguaggi
• Esistono tool commericali molto più avanzati (es. MagicDraw UML).
• ArgoUML è uno dei pochi ad essere contemporaneamente gratuito,
multipiattaforma, e relativamente facile da usare
19
Esercizio 2
• Creare un nuovo modello in ArgoUML
• Creare un diagramma delle classi
• Inserire un nuovo elemento, corrispondente alla classe
Film (slide 17)
• Provare a generare lo scheletro delle classi Java con
ArgoUML ed importarle in Eclipse
20
Diversi livelli di dettaglio in UML
• Tutti i diagrammi UML sono pensati per poter essere usati
fornendo diversi livelli di dettaglio
• Chi crea un diagramma UML ha il compito di scegliere il
livello di dettaglio più adeguato a seconda:
• Della fase di progettazione (analisi, design, implementazione, …)
• Del destinatario del diagramma (cliente, manager, programmatore,
…)
• Del particolare aspetto da evidenziare
• Estremi
• UML come sketch
• UML come «blueprint»
21
Un importante principio
• La flessibilità d’uso di UML è ottenibile grazie ad un
semplice ma fondamentale principio
• In ogni diagramma UML si può scegliere di omettere qualsiasi
dettagli (es., il tipo di proprietà, alcune delle operazioni, …)
• Se qualcosa non è visualizzata nel diagramma non si può dedurre
che essa non sia presente nel modello
• Non è necessario rappresentare TUTTO in un diagramma
• Ci si può concentrare solo su particolari parti (es. solo un
sottoinsieme delle classi del sistema)
• Difficilmente un sistema molto complesso verrà rappresentato in un
singolo diagramma UML (sarebbe praticamente illegibile)
22
UML: rappresentazione di una Classe
• Proprietà
• Caratteristiche strutturali della
classe
NomeClasse
• Corrispondono, in prima
approssimazione, al concetto di
Proprietà (attributi)
campo nei linguaggi di
Operazioni()
programmazione ad oggetti
• Come vedremo una proprietà può
essere definita in due diversi modi:
• Attributi
• Associazioni
23
Proprietà: notazione attraverso attributi
• Formato di un attributo
visibilità nome: tipo [molteplicità] =
default {property-string}
• Solo il campo nome è obbligatorio quando una
proprietà è rappresentata in una Classe
• Rappresentati nel secondo box
NomeClasse
-nome[1] : string = "SenzaNome"{frozen}
24
Proprietà: notazione attraverso attributi
• Visibilità
• Corrisponde al concetto di visibilità studiato in Java
• Simbolo ‘-’ per indicare una proprietà privata (non
accessibile ad oggetti di altre classi)
• Simbolo ‘+’ per indicare una proprietà pubblica
(accessibile liberamente da oggetti di altre classi)
• Simboli ‘#’ e ‘~’ per visibilità protected o di package
NomeClasse
-nome[1] : string = "SenzaNome"{frozen}
nome è una proprietà privata
25
Proprietà: notazione attraverso attributi
• Molteplicità
• Dichiara il numero di oggetti che possono riempire la proprietà
• In Java, una proprietà con molteplicità > 1 può essere
implementata come un array di oggetti di quel tipo
• Si esprime tra quadre con la seguente notazione
• [n] la proprietà a molteplcità n (es. [1] è il caso di default)
• [n..m] la proprietà può essere «riempita» da minimo n oggetti, ed al
massimo m oggetti
• * è un simbolo speciale che significa «qualsiasi numero di oggetti»
Videoteca
-catalogoFilm[0..*] : Film
Una videoteca può avere un catalogo di zero o più film
26
Proprietà: notazione attraverso attributi
• Tipo
• È il tipo di dato che rappresenta la proprietà (es. intero,
stringa, Persona, … )
• Può essere un tipo primitivo oppure una Classe
definita dall’utente
NomeClasse
-nome[1] : string = "SenzaNome"{frozen}
La proprietà nome è di tipo string
(String in Java)
27
Proprietà: notazione attraverso attributi
• Valore di default
• Indica il valore che la proprietà ha di «default», ossia se non
gli viene associato esplicitamente un valore diverso (ad es.,
attraverso il costruttore)
• Viene utilizzato più tipicamente per proprietà di tipo
primitivo
• Se omessa, viene omesso anche il simbolo ‘=‘ (e il default
della proprietà sarà indeterminato)
NomeClasse
-nome[1] : string = "SenzaNome"{frozen}
La proprietà nome è inizializzata al valore «SenzaNome»
28
Proprietà: notazione attraverso attributi
• Property-String
• Indicato tra graffe ({, }) indica caratteristiche addizionali da
attribuire alla proprietà
• Ad esempio, la proprietà {frozen} indica che il valore della
proprietà (una volta inizializzato) non è più modificabile
• Corrispondente alla keyword final in Java
• Non considereremo in questo corso le property-string
NomeClasse
-nome[1] : string = "SenzaNome"{frozen}
La proprietà nome non è modificabile
una volta inizializzata
29
Proprietà: Mapping in Java
Attributi a molteplicità 1
• Come visto spesso la corrispondenza è uno ad uno con un
campo di classe
• In diagrammi UML di «alto livello» (utilizzati in fase di analisi)
spesso la visibilità della proprietà è pubblica
• In linguaggi come Java i campi di classe dovrebbero avere sempre
visibilità privata
• Altri linguaggi (es. Microsoft C#) supportano direttamente il concetto
di proprietà (separato da quello di campo)
• Proprietà pubbliche/protette/package vengono implementate
in Java tramite un corrispondente campo privato e i metodi di
accesso (getter/setter) con la visibilità corrispondente
30
Proprietà: Mapping in Java
Proprietà {frozen} o {readOnly}
• Un attributo marcato con {frozen} indica
che il suo valore, una volta inizializzato,
non è mai modificato
• Spesso viene usata la property-string {readOnly}
con significato del tutto simile
• Molto utile per proprietà il cui valore non
cambia mai durante la vita di un’istanza
della classe
• Es. Nella classe Persona le proprietà nome,
cognome e data di nascita sono buoni candidati
ad essere marchiati come {frozen} o {readOnly}
• Il valore per tali proprietà viene impostato
tramite parametri passati al costruttore
delle istanze di classe
Persona
+ nome : string{frozen}
+ cognome : string{frozen}
+ annoNascita : int{ frozen}
+ calcolaEta() : int
31
Proprietà: Mapping in Java
Proprietà {frozen} o {readOnly}
• In Java si può dichiarare un
campo immutabile tramite la
parola chiave final
public class Persona {
private final String nome;
private final String cognome;
private final int annoNascita;
• Implementazione di proprietà
public Persona(String nome,
String cognome, int annoNascita) {
this.nome= nome;
this.cognome = cognome;
this.annoNascita = annoNascita;
}
immutabile tramite
combinazione di campi final e
metodi getter
public String getNome() { return nome; }
public String getCognome() { return cognome; }
public getAnnoNascita() { return annoNascita;}
Persona
+ nome : string{frozen}
+ cognome : string{frozen}
+ annoNascita : int{ frozen}
+ calcolaEta() : int
public calcolaEta() { ... }
}
32
UML: rappresentazione di una Classe
NomeClasse
Proprietà (attributi)
Operazioni()
• Operazioni
• Caratteristiche comportamentali
della Classe
• Possono corrispondere al concetto
di metodi nei linguaggi ad oggetti
33
Operazioni: notazione
• Formato di un’operazione
visibilità nome (parametri) : valore-ritorno
{property-string}
• Solo il campo nome - seguito da una coppia di
parentesi (anche vuote) - è obbligatorio
• Rappresentate nel terzo box
NomeClasse
+add(in a : int = 0, in b : int = 0) : int
34
Operazioni: notazione
• Visibilità e Nome
• Stessa rappresentazione usata per gli attributi
• Lista di parametri
• Lista di elementi separati da virgole
• Ogni elemento ha a sua volta il formato:
• direzione nome: tipo = default
NomeClasse
+add(in a : int = 0, in b : int = 0) : int
visibilità e nome Lista dei parametri
35
Operazioni: parametri
• Direzione
• Può essere in, out o inout
• Indica se il parametro è un ingresso per il metodo, un’uscita o entrambi
• Per i parametri passati per copia (tipi primitivi in Java) la direzione è sempre in
perché il metodo non può modificare i valori originali
• Per i parametri passati come riferimento
• Se il valore dell’oggetto passato come riferimento viene utilizzato dal’operazione,
ma il suo contenuto non viene modificato allora la direzione è in (default, e più
usata)
• Se l’oggetto riferito può essere modificato dall’operazione, ma il suo valore
iniziale non è importante ai fini del risultato dell’operazione, allora il metodo ha
direzione out
• Se il valore dell’oggetto riferito viene utilizzato dal metodo e tale valore può
anche essere modificato durante l’esecuzione dell’operazione allora la direzione è
inout
NomeClasse
+add(in a : int = 0, in b : int = 0) : int
a e b non sono modificati dal metodo add
36
Esempio: direzione in Java
int sum(int[] values) {
void swap(int[] values,
int a, int b) {
int risultato = 0;
for ( v : values) {
risultato += v;
}
return risultato;
}
int maxExchange (int[] values,
int a, int b) {
if (values[b] < values[a]) {
int t = values[b];
values[b] = valies[a];
values[a] = t;
}
int t = values[a];
values[a] = values[b];
values[b] = t;
return;
}
return values[a];
}
ArrayOps
+sum(in values : int[]) : int
+swap(out values : int[], in a : int, in b : int) : void
+maxExchange(inout values : int[], in a : int, in b : int) : int
37
Operazioni: parametri
• Nome
• Tipo
• Valore di default
• Hanno lo stesso significato discusso per quanto
riguarda la notazione per proprietà in forma di attributi
NomeClasse
+add(in a : int = 0, in b : int = 0) : int
a e b sono parametri di tipo int, con valore di default 0
38
Operazioni: notazione
• Valore di ritorno
• Indica il tipo del risultato dell’operazione
• Può essere sia un valore primitivo (int, float, … ) che
un un tipo definito dall’utente (es., un’istanza di una
Classe definita in precedenza)
NomeClasse
+add(in a : int = 0, in b : int = 0) : int
L’operazione add produce un risultato di tipo intero
39
Definire proprietà tramite Associazioni
• Associazione
• Un’associazione (binaria) definisce una relazione tra
due Classi
• E’ rappresentata tramite una linea continua che
connette le due classi
• Definisce due proprietà: una per ciascun lato
dell’associazione
• Ognuno dei due lati (come negli attributi) può avere un
nome, una specifica di visibilità, una molteplcità, …
-catalogoFilm
Film
Videoteca
0..*
40
Associazioni: aggregazione e
composizione
• Un’associazione può
• Rappresentare un contenimento logico
(aggregazione)
• Una lista d’esame contiene degli studenti
• Rappresentare un contenimento fisico
(composizione)
• Un triangolo contiene tre vertici
• Non rappresentare un reale contenimento
• Una fattura si riferisce a un cliente
• Un matrimonio è associato ad una data
41
Associazioni: aggregazione
• Un oggetto x di classe X è associato a (contiene) un
oggetto y di classe Y in modo non esclusivo
• y ha senso di esistere indipendentemente da x
• Una lista d’esame contiene degli studenti
• Uno studente può essere contemporaneamente in più liste
d’esame
• La cancellazione della lista d’esame non comporta
l’eliminazione “fisica” degli studenti in lista
• L’aggregazione in UML è rappresentata con un rombo
vuoto sul lato «contenitore» dell’associazione
-iscritti
ListaEsame
1
*
Studente
42
Associazioni: composizione
• Un oggetto x di classe X è associato a (contiene)
un oggetto y di classe Y in modo esclusivo
• L’esistenza di y e strettamente legata all’esistenza di x
• Un triangolo contiene tre punti (i suoi vertici)
• L’eliminazione del triangolo comporta l’eliminazione dei tre
punti
• La composizione in UML è rappresentata con un
rombo pieno sul lato «contenitore»
dell’associazione
-punti
Poligono
1
3..*
Vertice
43
Associazioni: navigabilità
• Un’associazione può opzionalmente definire la «navigabilità»
su uno o entrambi i suoi lati
• La navigabilità si indica aggiungendo la punta di una freccia
sul lato dell’associazione che si vuole rendere navigabile
• Il lato verso cui punta la freccia è detto «destinazione», quello
opposto «sorgente»
• Indica che istanze della classe «destinazione» sono
raggiungibili a partire da istanze del lato «sorgente»
• Nell’esempio indica che a partire da un oggetto di classe Videoteca
si possono raggiungere le istanze di Film a cui è associato
• La navigabilità è un concetto molto più vicina all’implementazione
pratica che alla relazione logica tra classi
-catalogoFilm
Videoteca
Film
0..*
44
Associazioni: come implementarle
• Dipende da
• Molteplicità dell’associazione (uno a uno, uno a molti, molti a
molti)
• Navigabilità
• Composizione/aggregazione
• Scelte progettuali (performance / memoria)
sposato-con
Automobile
1
0..*
+coniuge
immatricolazione
Persona
Persona
1
1
-catalogoFilm
Videoteca
Film
0..*
0..*
45
Associazioni: come implementarle
• In generale:
• Campi privati in una (o entrambe) le Classi
corrispondenti ai lati dell’associazione
• Opportuni metodi di accesso (getter/setter) se
navigabili o modificabili pubblicamente
• Molteplicità implementata tramite l’utilizzo opportuno di
oggetti contenitore (es. array, liste, collezioni in
generale ... )
46
Associazioni: alcuni esempi
Associazione uno a uno con navigabilità esplicita
public class Persona {
// … altri campi …
private Persona coniuge;
// costruttore
public Persona( Persona coniuge) {
this.coniuge = coniuge;
// altre inizializzazioni
}
public Persona getConiuge() {
return this.coniuge;
}
public void setConiuge(Persona p) {
this.coniuge = p;
}
}
47
Associazioni: alcuni esempi
Associazione molti a molti con navigabilità esplicita
public class Videoteca {
// altri campi di classe
private Film[] catalogoFilm;
//usata per vedere quanti elementi sono
//effettivamente nel vettore
private int catDim;
public Videoteca(int max) {
this.catalogoFilm = new Film[max];
this.catDim = 0;
}
public class Film {
// campi di classe
// costruttori
// metodi
}
-catalogoFilm
Videoteca
Film
0..*
public void aggiungiFilm(Film f) { … }
public boolean rimuoviFilm(String titolo)
{ … }
public Film cercaFilm(String titolo){...}
}
0..*
48
Esercizio – Requisiti
Bisogna realizzare un sistema per la gestione di una videoteca.
Una videoteca – identificabile tramite il suo nome - posside in catalogo
almeno un film, e può possederne fino ad una certa capacità comunque
limitata.
Ogni film è caratterizzato dal suo titolo, il suo regista e l’anno di
produzione.
Il sistema deve essere in grado di aggiungere film alla videoteca, ma anche di
effettuare ricerche sul catalogo ed eventualmente di rimuovere film non più
disponibili.
In particolare, per quanto riguarda ricerca ed eliminzazione, il sistema deve
supportare la ricerca basata sul titolo del film: dato il titolo di un film deve
essere possibile ottenere tutti i suoi dettagli dalla videoteca nel caso in cui quel
film sia in catalogo; inoltre, dato il titolo, deve essere possibile rimuovere il film
se presente nella videoteca.
49
Esercizio
• Dal precedente documento dei requisiti, scritto a
quattro mani con il cliente, si è ottenuto il
seguente diagramma delle classi di analisi
Videoteca
Film
-catalogo
+nome
+aggiungiFilm(in f : Film) : void
+rimuoviFilm(in titolo : string) : bool
+cercaFilm(in titolo : string) : Film
0..*
1..*
+titolo : string
+regista : string
+anno : int
50
Esercizio
• Implementare in un nuovo progetto Eclipse la
specifica richiesta, implementando opportunamente i
diagrammi UML di analisi tramite i costrutti del
Linguaggio Java
• Aggiunta dei costruttori
• Proprietà pubbliche -> getter/setter
• Realizzare una classe VideoMain in cui sia definito il
metodo statico main. Tale metodo dovrà:
• Creare una istanza di Videoteca il cui nome sarà
«JavaTeque»
• Creare tre istanze di film
51
Esercizio
• Realizzare una classe VideoMain in cui sia definito il
metodo statico main. Tale metodo dovrà:
• Creare una istanza di Videoteca il cui nome sarà «JavaTeque»
• Creare tre istanze di film:
• «Annie Hall», «W. Allen», 1977
• «Blade Runner», «R. Scott», 1982
• «Mulholland Dr.», «D. Lynch», 2001
• Aggiungere le tre istanze al catalogo di «JavaTeque»
• Cercare nel catalogo il film «Titanic» e verificare che il valore di
ritorno della ricerca sia quello aspettato
• Cercare nel catalogo il film «Mulholland Dr.» e verificare che il
valore di ritorno della ricerca sia quello aspettato
• Eliminare dal catalogo il film «Mulholland Dr.»
• Cercare nel catalogo il film «Mulholland Dr.» e verificare che il
valore di ritorno della ricerca sia quello aspettato
52
Associazioni: alcuni esempi
Associazione molti a molti con navigabilità non espressa
• Il progettista / designer deve
decidere la navigabiltà
dell’associazione
• Considerazioni dipendenti
dall’applicazione
• Ad es.:
• Nell’applicazione che si sta
sviluppando, è più frequente:
• Che si debba risalire al proprietario a
partire da un’automobile
• Alle auto possedute a partire da una
Persona
• Entrambi i casi sono molto frequenti
53
Associazioni: alcuni esempi
Associazione molti a molti con navigabilità non espressa
public class Persona {
// altri campi di classe
private Automobile[] automobili;
private int nAuto = 0;
// .. Eventuali costruttori...
public Automobile[] getAutomobili() {
Automobile[] copia =
new Automobile[nAuto];
for ( int i=0; i<nAuto; i++ ) {
copia[i] = automobili[i];
}
return copia;
}
public void addAutomobile(Auto a) {…}
public void rmAutomibile(Auto a) {...}
}
54
Associazioni: alcuni esempi
Associazione molti a molti con navigabilità non espressa
public class Persona {
// altri campi di classe
private Automobile[] automobili;
private int nAuto = 0;
// .. Eventuali costruttori...
public Automobile[] getAutomobili() {
Automobile[] copia =
new Automobile[nAuto];
for ( int i=0; i<nAuto; i++ ) {
copia[i] = automobili[i];
}
return copia;
}
public void addAutomobile(Auto a) {…}
public void rmAutomibile(Auto a) {...}
}
Cosa fa questo
codice e perché
è fatto così?
55
Associazioni: alcuni esempi
Associazione molti a molti con navigabilità non espressa
public class Persona {
// campi di classe
// costruttori
// getter/setter e metodi
}
public class Automobile {
// … altri campi
private Persona proprietario;
// costruttore/i
public Persona getProprietario() { … }
public void setProprietario(Persona p) { … }
// … altri getter/setter e metodi
}
56
Associazioni: alcuni esempi
Associazione molti a molti con navigabilità non espressa
public class Persona {
// altri campi di classe
private Automobile[] automobili;
private int nAuto = 0;
// costruttori
public Automobile[] getAutomobili() {…}
public void addAutomobile(Auto a) {
this.automobili[this.nAuto] = a;
this.nAuto += 1;
}
//altri getter/setter e metodi
}
public class Automobile {
// … altri campi
private Persona proprietario;
// costruttore/i
public Persona getProprietario() { … }
public void setProprietario(Persona p) {
this.proprietario = p
}
// … altri getter/setter e metodi
}
57
Associazioni: alcuni esempi
Associazione molti a molti con navigabilità non espressa
public class Persona {
// altri campi di classe
private Automobile[] automobili;
private int nAuto = 0;
// costruttori
public Automobile[] getAutomobili() {…}
public void addAutomobile(Auto a) {
this.automobili[this.nAuto] = a;
this.nAuto += 1;
}
//altri getter/setter e metodi
}
Ooops!
Quali sono i problemi?
public class Automobile {
// … altri campi
private Persona proprietario;
// costruttore/i
public Persona getProprietario() { … }
public void setProprietario(Persona p) {
this.proprietario = p
}
// … altri getter/setter e metodi
}
58
Associazioni: alcuni esempi
Associazione molti a molti con navigabilità non espressa
public class Persona {
// altri campi di classe
private Automobile[] automobili;
private int nAuto = 0;
// costruttori
public Automobile[] getAutomobili() {…}
public void addAutomobile(Auto a) {
this.automobili[this.nAuto] = a;
this.nAuto += 1;
}
//altri getter/setter e metodi
}
public class Automobile {
// … altri campi
private Persona proprietario;
// costruttore/i
public Persona getProprietario() { … }
public void setProprietario(Persona p) {
this.proprietario = p
}
// … altri getter/setter e metodi
}
Cosa succede se
qualcuno chiama
setProprietario su
un’automobile, ma non
addAutomobile sulla
persona
corrispondente al
nuovo proprietario?
59
Navigabilità bidirezionale (1/3)
Una soluzione (parziale) al problema di sincronizzazione
public class Automobile {
// … altri campi
private Persona proprietario;
// costruttore/i
public Persona getProprietario() { … }
public void setProprietario(Persona p) {
if (proprietario != null) {
proprietario.rmAuto(this);
}
this.proprietario = p;
if (proprietario != null) {
proprietario.addAuto(this);
}
}
// … altri getter/setter e metodi
}
60
Navigabilità bidirezionale (2/3)
Una soluzione (parziale) al problema di sincronizzazione
public class Persona {
// altri campi di classe
private Automobile[] automobili;
private int nAuto = 0;
// costruttori
public Automobile[] getAutomobili() {
Automobile[] copia =
new Automobile[nAuto];
for ( int i=0; i<nAuto; i++ ) {
copia[i] = automobili[i];
}
return copia;}
protected
protected
…}
void addAutomobile(Auto a) {...}
void rmAutomobile(Auto a) {...}
61
Navigabilità bidirezionale (3/3)
Una soluzione (parziale) al problema di sincronizzazione
• Il metodo setProprietario nella classe
Automobile si occupa di sistemare anche lo stato
della classe Persona relativa
• Siccome il contrario non avviene (perché?),
bisognerebbe evitare che qualcuno possa
chiamare il metodo addAuto / rmAuto della
classe Persona direttamente
• Limitata la visibilità di quei metodi
62
Esercizio di Riepilogo
• Partiremo da un Documento di Specifica dei
Requisiti scritto in seguito all’interazione con il
cliente
• Sistema di gestione degli ordini in un negozio
• Per arrivare a quello che viene detto Modello
Statico del sistema
• Mostra la struttura degli elementi (Classi) del sistema
senza evidenziarne le interazioni
• Realizzato con gli elementi del diagramma delle classi UML
visti fin ora
• Ma prima, un po’ di «buoni principi» per ricavare il
modello statico a partire dal DSR
2.63
Analisi
Individuazione delle classi
• Elencare i nomi (semplici o composti) che compaiono nei
documenti raccolti, convertendoli al singolare
• Eliminare i nomi che sicuramente
• non si riferiscono a classi
• indicano attributi (dati di tipo primitivo)
• indicano operazioni
• Scegliere un solo termine significativo se più parole indicano lo
stesso concetto (sinonimi)
• Il nome della classe deve essere un nome familiare
• all’utente o
• all’esperto del dominio del problema
• non allo sviluppatore!
2.64
Analisi
Individuazione delle classi
• Attenzione agli aggettivi e agli attributi, possono
• Indicare oggetti diversi
• Indicare usi diversi dello stesso oggetto
• Essere irrilevanti
Ad esempio:
• “Studente bravo” potrebbe essere irrilevante
• “Studente fuori corso” potrebbe essere una nuova classe
• Attenzione alle frasi passive, impersonali o con soggetti fuori dal
sistema
devono essere rese attive ed esplicite, perché potrebbero
mascherare entità rilevanti per il sistema in esame
2.65
Analisi
Individuazione delle classi
• Individuare Cose tangibili, cioè oggetti reali appartenenti al
dominio del problema
•
Banco, LavagnaLuminosa, Schermo, Computer, …
• Individuare Contenitori (fisici o logici) di altri oggetti
•
•
Facoltà, Dipartimento, Aula, SalaTerminali, …
ListaEsame, CommissioneDiLaurea, OrdineDegliStudi,
Window, Form, …
• Individuare Eventi o Transazioni che il sistema deve gestire e
memorizzare
- possono avvenire in un certo istante (ad es., una vendita) o
- possono durare un intervallo di tempo (ad es., un affitto)
•
Appello, EsameScritto, Registrazione, AppelloDiLaurea, …
2.66
Analisi
Individuazione delle classi
• Per determinare se includere una classe nel modello,
porsi le seguenti domande:
• il sistema deve interagire in qualche modo con gli
oggetti della classe?
• utilizzare informazioni (attributi) contenute negli oggetti
della classe
• utilizzare servizi (operazioni) offerti dagli oggetti della classe
• quali sono le responsabilità della classe nel
contesto del sistema?
2.67
Analisi
Individuazione delle classi
• Attributi e operazioni devono essere applicabili a tutti
gli oggetti della classe
• Se esistono
• attributi con un valore ben definito solo per alcuni oggetti della
classe e/o
• operazioni applicabili solo ad alcuni oggetti della classe
siamo in presenza di ereditarietà
• Esempio: dopo una prima analisi, la classe Studente potrebbe
contenere un attributo booleano inCorso, ma un’analisi più
attenta potrebbe portare alla luce la gerarchia:
Studente
StudenteInCorso
StudenteFuoriCorso
2.68
Esercizio di Riepilogo
Documento dei requisiti (1/2)
Si vuole realizzare la gestione degli ordini di un negozio.
Ogni negozio può avere un suo nome che non cambia mai durante
la vita del negozio. Un negozio ha a disposizione una serie di tipi di
prodotti. Ciascuna tipologia è caratterizzata da un codice che lo
individua univocamente; inoltre, opzionalmente, può essere
specificata una descrizione testuale per il tipo di prodotto.
Ovviamente, ciascun prodotto ha un suo prezzo unitario di vendita.
Il negozio conserva i prodotti disponibili in un magazzino. Il
magazzino ha capacità limitata, essendo in grado di mantenere al
massimo un certo numero di oggetti. Questo numero può variare di
magazzino in magazzino, ma non può né aumentare né diminuire
per uno stesso magazzino. Si assuma che ogni prodotto, di
qualunque tipologia sia, occupi un elemento dello spazio totale del
magazzino.
2.69
Esercizio di Riepilogo
Documento dei requisiti (2/2)
Un negozio accetta ordini da parte dei suoi clienti. Ogni cliente può
effettuare uno o più ordini. Un ordine può riguardare diversi tipi di
prodotto ed il cliente può naturalmente ordinare diverse copie di ogni
tipologia di prodotto. Per ciascun ordine è anche memorizzata una
data, corrispondente alla sua data di creazione. Del cliente, invece, il
sistema memorizza il nome e – opzionalmente – il suo indirizzo
postale.
Ogni qual volta che il negozio riceve un ordine, il sistema deve
verificare se sono presenti gli oggetti necessari a soddisfarlo
all’interno del magazzino associato. Se tale condizione è verificata,
tali elementi vengono rimossi dal magazzino, l’ordine viene marcato
come inviato, e il costo totale dell’ordine deve essere restituito come
risultato. Se al contrario anche solo uno degli oggetti richiesti
dall’ordine non è disponibile in magazzino, allora l’ordine non viene
processato e viene restituito un prezzo totale da pagare pari a 0.
70
Esercizio di Riepilogo
• Realizziamo insieme il modello
• Uso di Dia, preinstallato sulle macchine
• Si occupa solo della presentazione grafica dell’UML:
• Non effettua controlli sull’esistenza/validità dei simboli/notazioni/tipi
usati
• Non ha strumenti automatici come quelli della creazione del
software
• Più orientato a creare diagrammi che servano a
comunicare tra progettisti/programmatori/cliente
71
Diagramma delle Classi:
cos’altro vedremo
• Documentazione
• Dipendenze
• Generalizzazione
• Interfacce
• Classi Astratte
72
UML Documentazione
• Ogni elemento nel diagramma
delle classi può avere associata
della documentazione
• Classi
• Proprietà
• Associazioni
• Operazioni
• Un elemento di documentazione
può contenere testo in forma
libera
• Spesso commenti in linguaggio
naturale che arricchiscono il grafico
spiegando le parti meno chiare
73
UML: Generalizzazione
• Si mappa con il concetto di ereditarietà nel modello della
programmazione ad oggetti
• Dire che una classe A generalizza una o più classi B1, B2, …, Bn è
equivalente a dire che B1, B2, …, An ereditano da A
• La generalizzazione è indicata in UML con una linea solida avente
una freccia vuota ad uno dei suoi estremi
• La classe all’estremo indicato dalla punta della freccia è la classe «padre»
• La classe all’estremo opposto è la classe che eredita («figlio»)
Object
String
A
B1
B2
B3
74
UML: Generalizzazione
Object
• Si mappa con il concetto di
ereditarietà nel modello della
programmazione ad oggetti
• Dire che una classe A generalizza una
o più classi B1, B2, …, Bn è
equivalente a dire che B1, B2, …, An
ereditano da A
String
• La generalizzazione è indicata in
UML con una linea solida avente
una freccia vuota ad uno dei suoi
estremi
A
• La classe all’estremo indicato dalla
punta della freccia è la classe «padre»
(più propriamente superclasse)
• La classe all’estremo opposto è la
classe che eredita («figlio», o più
propriamente specializzazione o
sottoclasse)
B1
B2
B3
75
UML: Generalizzazione
• Come accade con
Object
+equals(in other : Object) : bool
+toString() : string
l’ereditarietà tra classi in
Java, anche in UML, una
classe eredita dal padre:
• Attributi
• Associazioni
• Operazioni
String
• UML supporta il concetto di
polimorfismo (ve lo
ricordate vero?)
2.76
Individuazione dell’ereditarietà
• L’ereditarietà deve rispecchiare una tassonomia
effettivamente presente nel dominio del problema
• Non usare l’ereditarietà dell’implementazione
• Non usare l’ereditarietà solo per riunire caratteristiche
comuni
• ad es., Studente e Abitazione hanno entrambi un indirizzo, ma non
per questo c’è ereditarietà!
• La ricerca delle relazioni di ereditarietà contribuisce a
chiarire il significato delle varie classi e può portare
alla scoperta di nuove classi
77
UML: Generalizzazione: Esempio
0..*
+prole
Mammifero
-eta
-lunghezza
-peso
+allatta()
OrcaAssassina
+commettiAssassinio()
+genitori
2
Umano
+blatera()
• Che metodi/attributi
proprietà avranno le istanze
di OrcaAssassina e Umano?
78
Dipendenze
• Quello di dipendenza è un concetto fondamentale nel software
• In generale esprime il fatto che il funzionamento di una parte A di software
dipende dal funzionamento di un’altra parte B di software
• Se B smette di funzionare, anche A smette di funzionare
• Se B cambia il suo modo di operare, allora o A smette di funzionare oppure anche A deve venire
adattato al nuovo B
• Esempio:
• Il funzionamento di un programma può dipendere dal tipo e dalla versione del sistema operativo
• Se si cambia sistema operativo il programma smette di funzionare, a meno che qualcuno non
abbia scritto una versione per l’altro tipo di sistema operativo
• Nella programmazione ad oggetti le dipendenze possono esistere tra
• Classi / Classi astratte / Interfacce
• Package
• Sotto-sistemi
79
Dipendenze in UML (1/2)
• Le dipendenze in UML si
rappresentano come frecce
tratteggiate
Negozio
• L’indicazione del verso della
freccia è di importanza critica
«use»
Ordine
• Dire «il superamento dell’esame
dipende dallo studio» è diverso
che «lo studio dipende dal
superamento dell’esame»
• Opzionalmente, una
dipendenza può essere
arricchita da una keyword
• Aggiunge significato al tipo
(motivo) della dipendenza
80
Dipendenze in UML (2/2)
• Perché è importante indicare le
dipendenze?
Negozio
«use»
• Una dipendenza indica che se una
classe venisse modificata, potrei dover
modificare anche tutte le classi che da
essa dipendono
• E’ bene ridurre al minimo possibile il
numero di dipendenze di ogni classe
• Modifiche successive richiederanno minore
Ordine
sforzo
• Nota bene: proprietà e relazioni di
ereditarietà/implementazione di
interfaccia nascondono dipendenze
• Se cambia la struttura di una classe
«padre» cambierà anche quella dei suoi
«figli»
81
Un esercizio completo
La compagnia ferroviaria SnowTrains ha richiesto
un’applicazione per il calcolo dei percorsi fra due città servite
della propria rete
• Ogni città dotata di stazione è identificata da un nome univoco
(che può contenere spazi – es. “Reggio Calabria”); se la città
ha un’unica stazione, il nome della città vale per entrambe,
mentre in caso contrario ogni stazione ha il proprio nome,
distinto da quello della città (es. “Firenze” indica la città, le cui
stazioni sono “Firenze S.M.N.”, “Firenze C.M.”, ”Firenze
Rifredi”, “Firenze Statuto”, etc.).
• Ogni treno è descritto da un identificativo (tipologia del treno +
numero identificativo univoco), i giorni di effettuazione
(1=lunedì,…, 7=domenica) e una serie di nomi di stazioni con i
rispettivi orari di partenza e arrivo; l’orario di partenza è
ovviamente indefinito per la stazione di arrivo, e lo stesso vale
per l’orario di arrivo nella stazione di origine.
82
Un esercizio completo
• Le tipologie di treni ammessi da SnowTrain sono
regionale (“R”), intercity (“IC”), supercosto (“SC”)
• Tutti i treni possono avere opzionalmente il riscaldamento,
l’intercity può avere o meno il servizio cuccetta (se lo ha deve
essere memorizzato il numero di cuccette totali), mentre il treno
supercosto ha opzionalmente un servizio ristobar
• Se presente, al servizio ristobar di un treno supercosto è associato
un prezzo di supplemento che sarà utilizzato per calcolare il costo
del biglietto del treno.
• Per ogni diverso treno il sistema deve memorizzare le
rispettive fermate con tanto di orario di arrivo, ed orario di
partenza
83
Un esercizio completo
• Tramite l’uso del sistema deve essere possibile, per ogni
treno, calcolare il tempo di percorrenza tra due qualunque
delle Stazioni che fanno parte del suo percorso
• Inoltre, date due stazioni deve essere possibile calcolare
il costo del viaggio. Tale valore è fornito dalla formula
• (5€*numero di fermate) per i treni regionali
• (10€ per ogni 60 minuti di percorrenza) per gli Intercity
• (20€ per ogni 60 minuti di percorrenza) + un supplemento di 2 €
per fermata se il treno supercosto possiede il vagone ristobar.
• Lo scopo dell’esercizio è progettare un modello statico del
dominio del problema (modello di analisi)
• Si utilizzi Dia per disegnare i grafici
84
Una possibile soluzione
Orario
+ora : int
+minuti : int
+minutiDallaMezzanotte() : int
+compareTo(in altro : Orario) : int
+toString() : string
+equals(in altro : Orario) : bool
Città
+nome[1] : string
+stazioni[1..*] : Stazione
<<enum>> GiornoSettimana
-giorni
1..7
1
+arrivo
Treno
-codice
+riscaldamento : bool
+calcolaDurata(in partenza : Stazione, in arrivo : Stazione) : int
+calcolaCosto(in partenza : Stazione, in arrivo : Stazione) : double
#fermateIntermedie() : Stazione[2..*]
+toString() : string
1
1
-fermate
Fermata
1
1
2..*
*
1
+stazione
Stazione
Regionale
InterCity
SuperCosto
+numCuccette
+supplementoRistobar : double
+nome : string
+nomeCitta : string
1
+partenza
85
Una considerazione
• Dato il modello precedente, che
senso ha instanziare una classe
di Treno (la superclasse)?
• Concetto di classe astratta
• Una classe astratta definisce uno
stato ed un comportamento
comune per un insieme di
sottoclassi
Treno
-codice
+riscaldamento : bool
+calcolaDurata(in partenza : Stazione, in arrivo : Stazione) : int
+calcolaCosto(in partenza : Stazione, in arrivo : Stazione) : double
#fermateIntermedie() : Stazione[2..*]
+toString() : string
• Ma non è instanziabile
• Come il nome suggerisce
rappresenta un concetto astratto
che non esiste fisicamente nel
dominio del problema
• Vedrete meglio in Java le classi
astratte (parola chiave abstract)
Regionale
InterCity
SuperCosto
+numCuccette
+supplementoRistobar : double
86
Interfacce
• Le interfacce sono uno dei concetti probabilmente più importanti ed
utilizzati della programmazione OO
• Idea di fondo:
• «Non mi importa dei dettagli di come una certa funzionalità (servizio) è
realizzata, mi interessa solo del cosa faccia e delle modalità di richiesta
del servizio
• Differenza tra interfaccia e classe astratta
• Una classe astratta può definire un comportamento comune ed uno stato
comune per le sue sottoclassi (es. concreto: l’implementazione di una
classe astratta può definire dei suoi campi e può definire l’implementazione
dei suoi metodi)
• Una interfaccia non definisce né stato né implementazione
• In una interfaccia si definiscono solo operazioni e la loro semantica
• SEMANTICA: Il significato dell’operazione: quello che l’operazione fa (non come lo
fa)
• Riprenderete più approfonditamente tutti questi concetti in JAVA
87
Interfacce in UML
• Le interfacce sono rappresentate in maniera simile alle
Classi (e le classi astratte)
• Il box degli attributi non è MAI visualizzato (le interfacce non
hanno stato)
• Come per le classi astratte, il nome dell’interfaccia è rappresentato
con un carattere corsivo
• Prima del nome della classe compare l’indicazione «interface»
(stereotipo)
88
Interfacce: un esempio
• Esistono diversi algoritmi di ordinamento, ognuno con
caratteristiche diverse
• Ma, in fondo, la differenza è solo nel come si effettua
l’ordinamento
• La loro interfaccia è comune
• Un algoritmo di ordinamento ha bisogno di
• Un insieme di oggetti da ordinare
• Un modo per sapere se un oggetto viene prima o dopo un altro
oggetto
89
Interfacce: un esempio
• Restituisce:
• Una nuova lista di oggetti (es. un array), stavolta ordinata
• Niente, ma modifica «in-place» la lista passata come argomento
90
Interfacce: un esempio
Interfaccia Comparable
• Definisce un comportamento
comune a tutte le classi che
prevedono dei confronti tra le
proprie istanze
• Due interi sono confrontabili ( >
o < ?)
• Due date sono confrontabili,
ma in maniera diversa
• …
• Non specifica come
effettuare il confronto, ma
solo il metodo tramite il quale
si ottiene il risultato del
confronto
91
Principio di progettazione
Design By Contract
• Nell’esempio precedente abbiamo visto che le classi di
ordinamento dipendevano solo dall’interfaccia Comparable e
non dalle classi concrete che la realizzavano
• Allo stesso modo, una classe che avesse bisogno di un
algoritmo di ordinamento, dipenderebbe solo da
SortProvider, e non dalle specifiche implementazioni
• Vantaggi:
• Posso modificare le implementazioni senza dover modificare le classi
cliente (robustezza)
• Posso aggiungere nel tempo implementazioni ulteriori senza dover
modificare nulla (estensibilità)
• Disaccoppiamento tra interfaccia/implementazione 
Disaccoppiamento del codice tra chiamante e chiamato
92
Principio di progettazione
Design By Contract
• Progettare pensando alle interfacce offerte per ottenere le
funzionalità del sistema
• Ideare progetti in termini di metodi e del loro significato e non del
modo in cui verranno poi implementati
• Si stabiliscono «contratti» tra le classi che richiedono e
offrono servizi
• Tali contratti includono:
• Le modalità di invocazione del servizio (signature del metodo)
• Precondizioni
• Postcondizioni
• Invarianti
93
Design By Contract
Precondizioni
• Le precondizioni sono un insieme di condizioni logiche
che devono essere rispettate dal chiamante di un
servizio (un metodo) affinché l’operazione richiesta vada
a buon termine.
• Sono le condizioni imposte da chi offre un servizio
• Numero e tipo dei parametri (es. implementino l’interfaccia
Comparable)
• Condizioni sul valore dei parametri passati (es. non siano null,
oppure siano un array di lunghezza almeno 2)
94
Design By Contract
Postcondizioni
• Le precondizioni sono un insieme di condizioni logiche
che devono essere rispettate da chi offre il servizio
• Descrivono le garanzie che si anno alla fine del servizio
• Cosa conterrà il valore di ritorno? (es. conterrà una nuova lista con
gli stessi elementi della lista in ingresso, ma ordinati in maniera
crescente)
• Promesse sul modo di trattare gli argomenti in ingresso (es. non
verranno modificati, oppure, gli elementi al suo interno verranno
ordinati)
• Indicazioni sullo stato del servizio chiamato (es. dopo la chiamata
ad aggiungiFilm, la videoteca avrà quel film in catalogo)
95
Design By Contract
Invarianti
• Predicati logici che – se verificati prima dell’invocazione –
continueranno ad essere validi dopo l’invocazione del
servizio
• Es. se al metodo sort si passa in ingresso una lista già
ordinata secondo il criterio di ordinamento stabilito, allora
alla fine del metodo l’ordine della lista non sarà modificato
(anche per gli elementi uguali secondo il criterio di
ordinamento)