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)