Dispensa a.a. 2013/2014 - SisInfLab
Transcript
Dispensa a.a. 2013/2014 - SisInfLab
Ingegneria del software Politecnico di Bari POLITECNICO DI BARI CORSO DI INGEGNERIA DEL SOFTWARE Prof.ssa Marina Mongiello A. A 2013/2014 1 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari CORSO DI INGEGNERIA DEL SOFTWARE Prof.ssa Marina Mongiello Email: [email protected] Telefono: 0805963524 Pagina Web: sisinflab.poliba.it/mongiello Orario di ricevimento: Giovedì mattina 10.00-11.00 II piano del DEI. Ai fini del corso è necessario svolgere un tema d’anno il cui intento è quello di essere impegnati costantemente con ciò che si fa in aula. Questo tema d’anno dovrà essere consegnato l’ultimo giorno di lezione e verrà svolto in gruppo (max 3 persone). IL TEMA D’ANNO NON INFLUIRA’ IN ALCUN MODO SULLA VOTAZIONE. Per quanto concerne l’esame, esso è strutturato in : Una prova scritta che prevede un esercizio di progettazione, uno di programmazione(linguaggio C) e una domanda di teoria. I testi di riferimento sono: Sommerville “Ingegneria del software” Pressman “Ingegneria del software” Di volta in volta verranno indicati capitoli e libri specifici per ogni argomento. 2 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari INTRODUZIONE AL CORSO Il corso di Ingegneria del software si occupa di capire come si arriva al codice. Viene dedicato un intero corso a ciò poiché si deve entrare nell’ottica di sistemi software che non sono più piccoli programmini, ma sistemi di grosse dimensioni che prevedono e necessitano di uno studio preliminare. Oltre a ciò però, il corso si occuperà anche di un approfondita fase di analisi dei costi, analisi del mercato, aspetti temporali, competenze (team) ecc.. Questo viene fatto perché bisogna avere un quadro completo di tutto ciò che compone un sistema software. ARGOMENTI DEL CORSO: PROCESSO SOFTWARE: in informatica il termine processo ha una sua precisa connotazione,esso è un’entità dinamica. Il programma è formato da una serie di esecuzioni,infatti quando si manda in esecuzione il programma, istante per istante, si verranno a creare varie configurazioni. La serie di queste configurazioni è il processo, per cui possiamo dire che il processo è il programma in esecuzione. Il processo software è la dinamica secondo cui le varie fasi che concorrono alla realizzazione di un sistema software vengono articolate. Le fasi possono essere articolate secondo vari modelli: Modello a cascata secondo cui le fasi sono strettamente sequenziali e propedeutiche (Se non finisce la prima fase non può partire la seconda). Questo modello ha però subito dimostrato dei limiti e per questo sono stati sviluppati modelli più dinamici. Sviluppo agile secondo cui il codice andava scritto senza alcuna documentazione, in modo da essere più snello. Ma anche ciò dimostrò di avere dei limiti perché poteva andare bene per piccoli programmi. Modello iterativo; Modello incrementale. Ma cosa è un modello? Un modello è un’astrazione di una realtà, uno schema. Tutto ciò che noi faremo sarà espresso in termini di modelli. PROGETTO SOFTWARE: inteso come gestione del progetto e non stesura. Si occuperà quindi dello studio delle risorse, della tempistica ecc ecc e per tutto l’arco temporale di realizzazione del progetto si controllerà se ci si sta attenendo alle previsione fatte. FASI DEL PROCESSO SOFTWARE: ANALISI DEL DOMINIO: durante questa fase si analizzano e si creano modelli di quel dominio per sapere cosa il sistema deve essere in grado di fare. Dall’analisi del dominio si tirano fuori i requisiti. Questi ultimi si possono anche negoziare col cliente. Cosa sono i requisiti? Sono funzionalità, ma anche altre richieste. Perciò si possono distinguere due tipi di requisiti, i requisiti funzionali e i requisiti non funzionali. Alla fine di tutto ciò si potrà estrapolare una modellazione che verrà espressa nel linguaggio che si preferisce. Si utilizzano 3 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari però, linguaggi standardizzati, ossia che utilizzano un certo protocollo. Universalmente è riconosciuto lo standard UML (Unified Modeling Language). Conclusa la fase di analisi si prosegue con: PROGETTAZIONE: In questa fase si sceglie che modello utilizzare, come strutturare il sistema e in particolare si progetta ogni singolo componente. IMPLEMENTAZIONE: alla fine di questa fase si ha il programma. TEST: con il test si può dire se il sistema è funzionante o meno. Ma sulla base di cosa posso stabilire se funziona o meno? Quanto più è ampio il range di dati su cui vado a fare il test tanto migliore sarà la probabilità che il programma sia corretto. LA CERTEZZA NON SI POTRA’ MAI AVERE! Non si potrà mai stabilire al 100% la funzionalità del sistema semplicemente perché i dati che posso inserire sono infiniti. E’ stato anche sviluppato un teorema che afferma e dimostra che, il fatto che il programma sia corretto, non è decidibile in maniera algoritmica. Dato questo vincolo, quello che si fa con il test è andare a controllare le situazioni di errore previste durante la fase di analisi. Alla fine verrà valutata la qualità del sistema. QUALITA’ DEL SISTEMA: come si stabilisce? Il programma per essere di qualità deve rispondere alle funzionalità richieste ma anche ai requisiti non funzionali, i quali a volte possono essere in contrasto tra loro. Si definiscono delle METRICHE DI PRODOTTO grazie alle quali si danno delle misure alla manutenibilità e ai principi che compongono essa. VERIFICA E VALIDAZIONE: se il sistema che si vuole creare è un software che controlla il traffico aereo e lo si vuole testare, ovviamente non avrebbe senso far partire gli aerei e accorgersi dopo che c’è qualche errore. Bisogna ricorrere a metodi più affidabili, non ci si può più basare solo sul test ma bisogna procedere o con simulazioni o con metodi più rigorosi che assicurino l’assenza di errori. Questi metodi sono detti METODI FORMALI e utilizzano linguaggi formali. Il linguaggio formale è un linguaggio la cui sintassi e il cui vocabolario sono limitati, e utilizza regole molto rigorose. Un esempio noto di linguaggio formale è il linguaggio di programmazione. I metodi formali si usano quindi in sistemi critici ove ad esempio o è a rischio la vita delle persone, o riguardano problemi economici. FUNCTION POINTS: quando ci si addentra in contesti di pubblica amministrazione o azienda bisogna fare un accurato studio dei costi e bisogna valutare quanto vale la retribuzione per quel sistema software. Per fare ciò vi sono dei modelli di stima dei costi. Tra i più famosi troviamo il CO.CO.MO. Negli ultimi anni però si sta utilizzando molto il metodo dei punti funzione, secondo cui non si valuta più il lavoro in base alle ore che l’uomo impiega nel svolgere l’attività ma in base a ciò che la persona fa. Per far ciò vi sono metriche non banali da cui poi si tira fuori la valutazione. Un altro metodo che si affianca alle metriche punti funzione è quello che utilizza le metriche USE CASE(più in voga negli ultimi anni). 4 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari PROCESSO SOFTWARE Il processo descrive tutta la sequenza delle varie fasi che si susseguono per arrivare allo sviluppo di un sistema software che, essendo un sistema complesso, non può avere sviluppo immediato. E’ una dinamica temporale che decide in che sequenza far susseguire le diverse fasi. Il processo è talvolta detto “ciclo di vita”. Ciò che studieremo sono i diversi modelli(schemi) secondo cui le fasi si alternano. Nel corso degli anni sono stati sviluppati vari modelli, ma si può dire che NON c’è un modello migliore in senso assoluto rispetto a un altro, poiché va sempre contestualizzato e scelto in base al contesto. In un contesto di “non-modello” ossia il code-‘n fix, si colloca lo sviluppo dell’ingegneria del software. Il code-‘nfix era formato da attività casuali, non organizzate, che dava vita a risultati come progetti non gestibili, caotici, difficilmente modificabili. I primi modelli che vennero creati rientrano nella categoria detta PROCESSO PRESCRITTIVO, cioè un processo secondo fasi predefinite. Il primo è il MODELLO A CASCATA, nato nel 1970, secondo cui le fasi si susseguono in maniera sequenziale. La caratteristica ed il grande vincolo di questo modello è il non poter ritornare a fasi precedenti. Ad esempio se si riscontrava un errore in fase di programmazione non si poteva rifare l’analisi ma si doveva procedere con il rifare da capo tutto il codice. SCHEMA MODELLO A CASCATA La prima fase è la studio di fattibilità, che non è una fase informatica, ma è una verifica di costi e risorse che mi permette di capire ciò che posso o non posso fare. La fase successiva è quella di analisi e specifica dei requisiti, ossia uno studio del dominio, l’individuazione dei requisiti e l’imposizione di vincoli. Nella fase di progettazione i requisiti devono essere trasformati in specifiche tecniche. Successivamente si procede con la programmazione e test di unità, dato che siamo in un’ottica modulare non ci sarà mai un blocco unico ma tanti blocchi separati per cui si può pensare di testare i 5 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari blocchi uno alla volta e poi unirli e testarli nuovamente nell’insieme. Fare quindi un test di unità e un test di integrazione. Infine la manutenzione che si svolge nell’arco del tempo. Il sistema deve presentarsi bene ad eventuali interventi dall’esterno. Dati i limiti esposti prima, di questo modello si sono proposte due varianti: CASCATA CON PROTOTIPAZIONE, secondo cui si realizza un prototipo, un esempio funzionante dall’inizio senza aspettare tutte le fasi. CASCATA CON RITORNI, che consente al termine di ogni fase di ritornare sulle precedenti e apportare modifiche. Dopo lo sviluppo del modello a cascata si trovano il modello incrementale e il modello evolutivo. Nei modelli incrementali è come se si avesse una cascata con ritorno. In quelli evolutivi si fa tutto e però poi si possono aggiungere e avere tante versioni diverse del prototipo fino a quando non si ottiene la versione definitiva. Tra i modelli evolutivi quello di riferimento è il MODELLO A SPIRALE ideato da Boehm. Il processo ritorna sempre su se stesso e ogni volta si pianifica la fase successiva. Ci si affida molto alla dinamicità del sistema. Tra le caratteristiche vi è una forte interazione col committente e uno studio economico molto più approfondito e forte. SCHEMA MODELLO A SPIRALE 6 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari Possiamo ancora trovare altri modelli, i modelli specializzati al processo unificato che non sono a sé stanti ma possono essere integrati con altri modelli. Precisamente lo sviluppo a componenti è una pratica di progettazione che dobbiamo necessariamente utilizzare quando si realizzano sistemi distribuiti, per garantire la modularità. Il PROCESSO UNIFICATO è un modello di processo che definisce le fasi ed è fortemente legato all’utilizzo dell’UML. Lo SVILUPPO AGILE(2001) è un modello secondo cui è inutile la documentazione e tende a snellire la programmazione. L’EXTREME PROGRAMMING(XP), è una programmazione estrema senza alcuna documentazione e con un codice estremamente complesso. 7 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari UML UML = UNIFIED MODELING LANGUAGE. Questo linguaggio di modellazione deriva dall’unificazione di tre diversi approcci di modellazione che si sono sviluppati con la programmazione orientata agli oggetti(OOP). Quali sono i principi delle OOP? Ereditarietà; Polimorfismo; Incapsulamento. Cosa è l’ereditarietà? E’ una relazione tra classi. Una relazione è una corrispondenza tra due insiemi. Una classe è un tipo di dato astratto che in particolare definisce degli attributi e dei metodi. Parliamo quindi di dati e di funzioni. I dati, ossia le informazioni che vengono gestiti dall’algoritmo, sono caratterizzati da un tipo e possono essere costanti o variabili. Il tipo di dato è un insieme di valori che una variabile può assumere e delle operazioni che possono essere eseguite sulla variabile. Le funzioni sono gli algoritmi, ossia sequenze finite di passi che servono a risolvere un determinato problema. Con la programmazione orientata agli oggetti si vuole togliere la separazione tra dati e funzioni per il principio dell’ Information Hiding (Incapsulamento), ossia per far si che si possa gestire meglio l’uso delle variabili limitando l’uso solo ad alcune funzioni che hanno bisogno di quelle variabili. L’information hiding quindi è un principio insito nella definizione stessa di programmazione orientata agli oggetti. La differenza tra un oggetto e una classe sta nel fatto che l’oggetto è un’istanza della classe. Cosa è il polimorfismo? Il polimorfismo è quella proprietà grazie alla quale è possibile che funzioni con stesso nome possono svolgere algoritmi diversi a seconda del numero e del tipo di parametri che gli vengono passati. Ad esempio immaginiamo di avere la funzione che calcola l’area di un quadrato, di un cerchio, di un triangolo e di un rettangolo. Abbiamo quindi quattro diverse implementazioni di un algoritmo il cui risultato è sempre l’area. In un programma strutturata dovrei usare quattro metodi diversi con il vincolo di usare quattro nomi diversi, con la OOP assolutamente no! Avremo una classe “padre” nominata figura geometrica contenente la funzione che calcola l’area e delle classi “figlie” quali rettangolo, triangolo e quadrato che ereditano attributi e funzioni dalla classe figura geometrica e in ogni classe figlia specificherò come va implementata la funzione del calcolo dell’aerea che però avrà sempre lo stesso nome in tutte le classi. Qualunque sia il linguaggio utilizzato il compilatore verifica sempre questi tre livelli: LESSICO, ossia le regole che definiscono il vocabolario. SINTASSI, regole del linguaggio per scomporre le frasi.(il compilatore controlla che la frase si attenga alle regole del linguaggio) SEMANTICA, controllo di corrispondenza tra ciò che è stato dichiarato ed utilizzato, tipo di dati,ecc ecc, dopodiché genera il linguaggio. Abbiamo quindi fatto un quadro degli elementi che ci servono per fare questa modellazione in UML. 8 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari Che cos’è un caso d’uso? Prendiamo la definizione che risale al 1995 di uno degli autori del linguaggio stesso secondo il quale è “una serie di transizioni di un sistema il cui compito è produrre un risultato di valore misurabile per uno o più attori del sistema”. Il caso d’uso stabilisce cosa accade nel sistema in seguito ad un evento di attivazione. Esattamente un caso d’uso è una particolare funzionalità, descritta mediante messaggi scambiati da un’entità e gli attori. Cosa è l’entità? E’ un sistema, un sottosistema, una classe ecc. L’attore è una qualunque entità esterna. Lo scopo della descrizione dei casi d’uso è di rendere comprensibile il funzionamento, o almeno l’individuazione delle funzionalità da realizzare, anche a persone che non sono esperte dell’informatica. Dove si collocano? Nella prima fase, quindi nella fase di Analisi che risulta essere la fase più astratta, concettuale, quella in cui si fa lo studio del dominio, poi si procede con l’individuazione dei requisiti, una volta tirati fuori i requisiti per rendere univoca la comprensione usiamo i casi d’uso. L’aspetto fondamentale è quello di descrivere il contorno di tutto il sistema. Tra attore e caso d’uso c’è una qualche relazione di attivazione, per cui quando un attore richiede una funzionalità si deve attivare il caso d’uso corrispondente. Generalmente i casi d’uso rappresentano solo una porzione dei requisiti ossia quelli funzionali. Tutti i requisiti detti esterni o non-funzionali non vengono rappresentati dai casi d’uso. Di un caso d’uso dobbiamo quindi definire SINTASSI E SEMANTICA. Non abbiamo un lessico perché come formalismo è elementare. La sintassi è la seguente: Nome caso d’uso SEMANTICAMENTE il caso d’uso ci rappresenta una funzionalità del sistema e quindi un requisito funzionale. Una volta che ho identificato tutti i requisiti ho trovato tutti i casi d’uso e questi casi d’uso, gli attori, e le relazioni tra di essi li vado a mettere insieme nel diagramma di casi d’uso dove ho lo schema di tutta la funzionalità del sistema che mi farà capire in linea di massima quali saranno i compiti del mio sistema. Altri elementi che trovo sono gli attori, da non confondere con gli utenti perché l’attore può anche essere un elemento hardware(es. stampante, tastiera ecc ecc) La sintassi dell’attore è la seguente: Studente universitario 9 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari Ogni attore ha un nome identificativo scritto sotto la figura dell’omino. Terzo elemento sintattico sono le frecce di collegamento, che rappresentano le relazioni, ossia le corrispondenze tra attore e caso d’uso, tra caso d’uso e attore, tra attore e attore o infine tra caso d’uso e caso d’uso. Elementi relati Relazione Funzione Attori Generalizzazione un attore, specializzato, eredita la partecipazione a tutti i casi d’uso con i quali l’attore specializzante comunica Esprime la partecipazione di uno o più attori ad un caso d’uso Attore - caso d’usoAssociazione Casi d’uso Notazione Estensione Il comportamento di un caso d’uso base può, opzionalmente, essere esteso dal comportamento definito da un altro caso d’uso Inclusione Il comportamento di un caso d’uso di base incorpora, sempre, il comportamento dei casi d’uso di inclusione Generalizzazione Un caso d’uso generale ed uno o più specifici casi d’uso che ne ereditano ed aggiungono caratteristiche Tra attore e attore ho una proprietà di generalizzazione, ossia una relazione il cui significato è analogo a quello dell’ereditarietà. Ho quindi un attore generico e un attore specializzato,quest’ultimo ha tutte le proprietà di quello generico con in più delle proprie. La notazione per questo caso è la seguente : Attore generico attore specializzato Tra attore e caso d’uso l’unica relazione ammessa è lì associazione con la caratteristica che può essere uni o bidirezionale, dall’attore al caso d’uso, dal caso d’uso all’attore o entrambe o non specifico nulla. Ho 4 possibilità diverse rappresentate da 4 frecce di collegamento diverse. 10 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari Qual è il significato? Esprime la partecipazione di uno o più attori al caso d’uso. Se per esempio abbiamo il caso in cui bisogna creare una password, il guest richiede l’ inserimento della password e il caso d’uso si attiva. (vedere esempio slide 22 della presentazione “Introduzione agli use case”) La cardinalità di una relazione è il numero di entità coinvolte da una parte e dall’altra. Se la cardinalità è uno a molti significa che a un’ entità ne corrisponderanno dall’altra parte molte. Esempio: Una persone può acquistare n libri, uno stesso libro può essere acquistato da una sola persona. Per quanto riguarda le relazioni tra i casi d’uso ne abbiamo di tre tipi diversi: estensione, inclusione e generalizzazione. Il simbolo della relazione di estensione e di inclusione è la freccia tratteggiata. Prendiamo un esempio di estensione. Ipotizziamo di avere un cliente che deve effettuare un prelievo da un conto corrente, deve essere fatto quindi un controllo sulla presenza minimo di importo disponibile. Per fare ciò possiamo pensare di notificare che l’importo residuo vada a zero e attivare un caso d’uso di notifica azzeramento credito solo nel caso in cui il credito vada a zero. Per questo si chiama relazione di estensione, poiché è quella parte che viene eseguita solo nel caso in cui si realizzi una condizione bene precisa che viene detta condizione di estensione. 11 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari Il punto di estensione è il dato su cui vado a fare il controllo della condizione che mi potrebbe portare nel caso d’uso estendente che in questo caso è l’importo prelevato. Quando l’importo prelevato fa si che si azzeri il credito passo al caso d’uso estendente. L’altro tipo di relazione è graficamente simile all’estensione ma concettualmente è la duale. Il caso d’inclusione prevede una parte di codice che viene eseguito sempre, tutte le volte che si esegue il caso d’uso includente. Lo porto fuori quindi per una questione di visibilità e per garantire la modularità. Graficamente è rappresentato così: Se devo effettuare un prelievo o se voglio richiedere un mutuo, in entrambi i casi, devo controllare se sul conto c’è un certo importo. E dato che devo controllare la stessa cosa in entrambi i casi, invece di ripetere la procedura due volte, la porto fuori e ho un caso d’uso che è incluso in entrambi. Qui il verso della freccia è verso il caso d’uso incluso. L’ultima relazione è quella di generalizzazione, in cui ho un caso d’uso più ampio e un caso d’uso “figlio” che specializza quello più ampio. Ad esempio in termini di metodi è il calcolo dell’area del cerchio o del rettangolo rispetto al calcolo dell’area della figura geometrica. E’ quindi un esempio di polimorfismo, di over-riding tra classi diverse. Abbiamo quindi ora un quadro completo per realizzare il modello dei casi d’uso. Attraverso tutte queste relazioni possiamo esprimere tutto il funzionamento di un intero sistema software a livello astratto, al termine dell’analisi dei requisiti. I vari casi d’uso, presi singolarmente, possono essere descritti in maniera più precisa perché bisogna formalizzare per bene tutta la sequenza di passi che vogliamo realizzare per avere quella funzionalità. E quindi oltre la rappresentazione grafica possiamo descrivere testualmente ciascun caso d’uso. Si 12 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari utilizzano altre informazioni che l’UML consiglia come ad esempio quali sono gli attori, se ci sono delle precondizioni o postcondizioni, e ci permette anche di distinguere lo scenario di base da scenari alternativi che possono essere gli insuccessi dello scenario base. 13 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari DIAGRAMMA DELLE CLASSI Il diagramma delle classi serve ad illustrare come è strutturato il sistema. Esso contiene le classi e le relazioni che sussistono tra di esse. Le classi sono formate da attributi e metodi. Il diagramma delle classi non è altro che un modello di dati oltre che un modello del sistema software che si vuole realizzare. Non ci dà un modello dinamico sulle specifiche, non vi è nessun algoritmo masolo una dichiarazione di metodi. Le principali relazioni fra classi sono associazione, generalizzazione e dipendenza. Studieremo in modo più specifico la relazione di associazione e quella di generalizzazione. La relazione di associazione è una relazione semantica tra due o più classi, questa relazione semantica viene in realtà trasferita tra le istanze delle classi, ossia gli oggetti, e quindi quando si implementerà quella relazione graficamente tracciata nel diagramma delle classi diventerà a livello di codice qualcosa che permetterà di legare due o più oggetti, ed è sempre bidirezionale. L’associazione può essere individuata o assegnando un nome alla relazione o individuando i ruoli per le entità che sono coinvolte. Vediamo un esempio: Azienda 0..1 1..* Persona Impiegato DatoreLavoro In questo caso abbiamo un’azienda e una persona, due classi con propri attributi e metodi. La relazione che posso avere tra queste due classi, rappresentata graficamente dalla linea che le congiunge, è che la persona lavora nell’azienda e l’azienda dà lavoro alla persona. Oppure posso leggere la relazione in termini di ruoli e quindi la persona è un impiegato o dipendente(ruolo) mentre l’azienda è il datore di lavoro. La differenza sta nel fatto che la relazione esprime un’azione che collega le due classi mentre il ruolo caratterizza la classe relativamente all’altra. Inoltre le relazioni hanno una molteplicità che si esprime sulla linea in prossimità di una o dell’altra classe. Esempio: L’azienda dà lavoro a una o più persone, la persona è impiegata in 0 o 1 aziende. Per implementare alcune relazioni, non tutti i linguaggi di programmazione hanno dei costrutti ben precisi. Per realizzare l’associazione tra due classe devo realizzare un mapping, usando in una e nell’altra classe un attributo dell’altra classe. Ad esempio in azienda metterò un attributo di Persona e in persona metto un attributo di Azienda. Se la relazione è uno a molti metterò una lista di attributi dell’altra classe. Ovviamente l’associazione si realizza a run-time. A livello statico non avrò mai un collegamento tra classi! 14 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari Ecco un esempio: IMPLEMENTAZIONE UNO A UNO (C++) USO DELL’ASSOCIAZIONE UNO A UNO IMPLEMENTAZIONE ASSOCIAZIONE UNO A MOLTI 15 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari USO DELL’ASSOCIAZIONE UNO A MOLTI(C++) L’aggregazione come la composizione è un esempio particolare di relazione di associazione, in cui sostanzialmente abbiamo diverse parti che messe insieme ci danno una classe aggregata. Una classe contenitore e classi contenute all’interno. Per l’aggregazione valgono le proprietà transitiva ( se A è parte di B, e B è parte di C, allora A è parte di C) e antisimmetrica ( se A è parte di B, allora B non è parte di A). La caratteristica della proprietà è che a run-time l’oggetto contenuto sopravvive all’oggetto contenitore. Un esempio classico è quello dell’automobile che può essere considerata una classe aggregata di carrozzeria e motore, posso avere il motore costruito da un azienda e la carrozzeria da un’altra azienda, esse unite danno vita all’automobile. Come capisco che si tratta di aggregazione? Dal fatto che entrambe le parti hanno vita propria anche separate. Se ad esempio durante un incidente la carrozzeria si distrugge e il motore rimane intatto, il motore potrà essere riutilizzato e viceversa. Parti distinte con vita propria che messe insieme danno vita a una classe con proprie caratteristiche. Come si crea l’aggregazione? Devo definire dei riferimenti alle istanze aggregate nella parte privata della classe aggregante. Definire riferimenti significa sostanzialmente definire dei puntatori o un qualcosa che mi possa far ricollegare alla parte aggregata. Bisogna poi definire delle operazioni per creare il collegamento tra le diverse classi. Prendiamo l’esempio dell’automobile, abbiamo detto che essa deriva dall’aggregazione tra carrozzeria e motore , per cui immaginiamo di avere tre classi: automobile, carrozzeria e motore e di avere nella parte privata della classe automobile i riferimenti alle classi carrozzeria e motore, e poi un metodo che permetterà di mettere in relazione tra loro le parti provenienti dalle altre classi. Bisogna poi definire motore e carrozzeria, si definisce il puntatore e si richiama il metodo che crea,attraverso i puntatori di carrozzeria e motore, l’automobile. 16 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari La differenza sostanziale tra l’aggregazione e la composizione sta nel fatto che le classi componenti non hanno vita propria al di fuori della classe composta. La classe componente è responsabile della creazione e della distruzione degli oggetti componenti, e le classi componenti sono ad uso esclusivo della classe composta. L’ultima relazione è quella di generalizzazione che rappresenta una relazione gerarchica. Quindi avremo una superclasse con delle classi figlie poste a un livello inferiore che però ereditano le proprietà delle classi superiori. Queste classi figlie però, oltre ad ereditare le proprietà della superclasse, hanno delle loro proprietà specifiche che le specializzano. La relazione si legge come “è un tipo di”(leggendo dal basso verso l’alto) o “Può essere un”(leggendo dall’alto verso il basso). Ad esempio, tra gli animali, ho i vertebrati, gli invertebrati e così via. La classe animali è la classe superclasse , mentre vertebrati, invertebrati e così via sono tutte sottoclassi con una propria peculiarità. Le classi specializzate ereditano attributi e metodi. La sottoclasse può contenere nuovi attributi e può ridefinire gli attributi della superclasse. Possiamo pensare alle figure geometriche , per tutte si calcola l’area o il perimetro ma in ogni figura geometrica(Triangolo, quadrato ecc) cambiano le variabili e i calcoli, per cui esso va ridefinito di classe in classe. La sintassi per la relazione di generalizzazione prevede l’uso della parola chiave <<extends>> . Esempio: class Cerchio extends FormaGeometrica. La classe Cerchio estende la classe FormaGeometrica. 17 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari DIAGRAMMA DI STATO Il diagramma di stato è la rappresentazione grafica di un grafo. Cos’è un grafo? E’ una coppia ordinata di insiemi, l’insieme di nodi e l’insieme di archi. E’ una funzione che ci permette di creare dei collegamenti tra i nodi mediante delle transizioni. Il diagramma di stato quindi è una formalizzazione grafica di quelle situazioni in cui l’oggetto che sto considerando, assume dei comportamenti diversi a seconda degli stati in cui si trova. Per effetto di vari eventi si passa da uno stato a un altro e quindi il comportamento dell’oggetto di studio varia. Lo stato è la condizione che un oggetto ha nel corso della sua “vita”. Nello stato l’oggetto soddisfa determinate condizioni ed è in attesa di eventi che fanno mutare il suo stato e il suo comportamento. Finché non si verifica nessun evento, l’oggetto persiste nello stesso stato. Supponiamo di essere nel contesto di un sistema che gestisce l’ingresso e l’uscita di libri in una biblioteca. Il libro può quindi essere disponibile, può essere stato prestato o può essere stato smarrito. Possiamo rappresentare graficamente la variazione di stati del libro, in seguito ad eventi innescanti, tramite il seguente diagramma. Caso 1) Il libro si trova nello stato di disponibilità(Acquisto), viene prestato e passa nello stato di “Prestito”, cambia quindi il suo stato a seguito dell’evento innescante del prestito. Supponendo che entro la data utile per la restituzione, l’utente restituisca il libro alla biblioteca, si ha un’altra transizione che fa sì che il libro ritorni nello stato di disponibilità (Acquisto). Dopo ciò vado verso lo stato finale solo quando finisce la vita del libro, se ad esempio è ormai troppo rovinato, altrimenti il libro sarà sempre nello stato disponibile. 18 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari Caso 2) Il libro viene prestato, ma non vengono rispettati i termini di consegna. Se l’utente riconsegna il libro con qualche giorno di ritardo, il libro ritornerà ad essere disponibile. Se invece, il libro non viene restituito poiché è stato ad esempio smarrito o danneggiato, e quindi non ritornerà più nella biblioteca, il libro passerà nello stato finale e verrà cancellato dalla biblioteca. DIAGRAMMA DI INTERAZIONE Il diagramma UML che ci permette di rappresentare le interazioni è il diagramma di sequenza. Esso è la notazione più leggibile delle interazioni tra più oggetti, e rappresenta la sequenza temporale delle azioni da eseguire. I diagrammi di sequenza derivano dai casi d’uso, per cui per ogni caso d’uso abbiamo un diagramma di sequenza. E’ caratterizzato da vari oggetti, i quali sono: Tempo Oggetti La linea della vita (LIFELINE) I messaggi Eventuali valori di ritorno dei messaggi Iterazioni Il tempo riferito all’interazione tra i diversi oggetti è rappresentato orizzontalmente. L’oggetto è in alto al diagramma rappresentato con un rettangolo contenente il nome. In verticale abbiamo un rettangolo che rappresenta la LIFELINE dell’oggetto, e si estende dal punto in cui l’oggetto viene istanziato fino a quando viene distrutto. La linea tratteggiata verticalmente rappresenta il tempo riferito al singolo oggetto. Ecco un semplice esempio grafico: : Cliente Istanziazione dell'oggetto Tempo Distruzione dell'oggetto Oggetto 19 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari Un esempio grafico dei messaggi scambiati tra i vari oggetti è il seguente: C’è inoltre la possibilità di descrivere il valore di ritorno di un messaggio. Se ad esempio un cliente invia la richiesta di disponibilità di un prodotto, ci si aspetta un valore di ritorno che quantifica i prodotti disponibili e viene rappresentato nella maniera seguente: : Cliente : Stampante disponibilita( ) quantità disponibile È possibile indicare il valore restituito da un messaggio (mostrato da una linea tratteggiata) Si possono rappresentare messaggi di auto delega e di iterazione. Esempi: : CorsodiLaurea : Studente * [per tutti gli studenti iscritti ] getInfo( ) L'iterazione è indicata dal simbolo * La condizione di iterazione è indicata tra parentesi quadre Ritorniamo ora all’esempio del libro di cui si è parlato nei diagrammi di stato. 20 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari Nell’esempio le classi sono stereotipate, ossia non viene usato il rettangolo con il nome come rappresentazione, ma un simbolo diverso per dare un significato a ciascuna classe relativamente al livello a cui si trova nell’architettura. Abbiamo un esempio di uno scenario in cui avviene la richiesta di prestito di un libro. L’ultimo diagramma UML che studiamo è il diagramma implementativo delle componenti. Cosa sono le componenti? Possono essere porzioni di sistema che scriviamo, parti riusabili rese già sostituibili, altri sistemi, sottosistemi esterni o parti di software. Per esempio in un’applicazione client-server le componenti sono proprio il client e il server, ossia parti distinte che interagiscono tra di loro. Poiché il sistema è complesso, bisogna quindi tracciare un modello che faccia capire quali siano le componenti, in che relazione sono e come interagiscono, e ciò viene fatto dal DIAGRAMMA DELLE COMPONENTI. I Componenti del nostro sistema sono solo entità logiche, ossia che non sono mappabili sulla macchina direttamente. Il diagramma delle componenti è perciò solo a livello logico. Vediamo un esempio: 21 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari L’altro tipo di diagramma che rappresentiamo per modellare l’architettura è il diagramma di configurazione, che raffigura gli elementi così come sono a run-time, ossia in esecuzione. Esso rappresenterà quindi, non solo le componenti, ma anche i processi. Non vengono rappresentate le componenti che non esistono a run-time, ad esempio quelle compilate separatamente. Graficamente la simboleggiatura è la seguente: Client-server a tre livelli. 22 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari SISTEMI DISTRIBUITI Cosa è un sistema distribuito? Sono dei sistemi per i quali l’elaborazione è distribuita. Non è detto che essa debba essere distribuita fisicamente, ma la cosa fondamentale è che i componenti di elaborazione siano distribuiti e quindi non uniti in un unico blocco. Che vantaggi offre? Condivisione delle risorse, che comporta maggiore efficienza, riduzione dei costi, riduzione di ridondanza, distribuzione del carico. Scalabilità, ossia possibilità di variare struttura del sistema senza stravolgere ciò che si ha già ed è vantaggioso dal punto di vista di manutenibilità. Fault tolerance,collegato all’affidabilità, perchè viene garantito che, nel caso in cui un componente venga meno, gli altri riescano comunque a lavorare. Per quanto riguarda gli svantaggi abbiamo: Maggiore complessità sia a livello di struttura sia a livello di gestione Poca protezione, poiché è un sistema aperto e bisogna lavorare per capire e progettare la sicurezza, la protezione, la riservatezza dei dati e dell’applicazione stessa; Non prevedibile, perché ho tante entità che interagiscono quindi è più facile che si inneschi un meccanismo non previsto dal sistema. Partiamo con l’analisi delle ARCHITETTURE MULTI-PROCESSORE. Un’ architettura multiprocessore è fondamentalmente una struttura hardware. Questo implica che,a differenza del modello classico, ossia a processore singolo, si hanno più processori e la possibilità di eseguire più processi sui singoli processori. L’utilizzo di tali architetture è molto comune nei sistemi REAL TIME, ossia quei sistemi in cui l’aspetto temporale è determinante, sistemi critici dal punto di vista del tempo. In un architettura multi processore i processi possono essere eseguiti su unico processore, ma ovviamente è necessario l’uso di uno scheduler, un particolare processo che pianifica l’esecuzione dei diversi processi su un processore in base ad algoritmi classici. Se si utilizzano processori multipli ovviamente l’efficienza migliora ma aumenta anche la complessità e si utilizza un dispatcher, un processo che effettua il controllo del traffico e delle allocazioni tra processi e processori. Che differenza c’è tra scheduler e dispatcher? Lo scheduler è la componente del SO che si occupa di allocare il processo al processore, dare l’ordine. Mentre il dispatcher si occupa solo di controllare il traffico, di gestire le diverse allocazioni tra processo e processori distinti. Se abbiamo un sistema formato da processi multipli non è detto che sia un sistema distribuito, come ad esempio i sistemi operativi. Nei sistemi distribuiti abbiamo componenti distribuite che interagiscono sfruttando le risorse della rete. Le architetture multiprocessore sono una prima categoria dei sistemi distribuiti. Consideriamo ora il secondo tipo di architettura distribuita,ossia il Client-Server, che è più semplice da comprendere sia per la modellazione sia per il funzionamento. Esamineremo in maniera panoramica questi 4 tipi di architetture: 23 Prof.ssa Mongiello Vinci Federica Ingegneria del software 1. 2. 3. 4. Politecnico di Bari Client-Server; A oggetti distribuiti;. Peer-to-peer; A sistemi distribuiti. In tutti e quattro questi modelli è fondamentale il ruolo del MIDDLEWARE, che è una componente software finalizzata a gestire la comunicazione delle diverse componenti del sistema stesso. Le componenti infatti, possono essere scritte con linguaggi diversi, o possono funzionare su piattaforme diverse, perciò tutte queste incompatibilità vengono gestite dal middleware. E’ standardizzato e quindi a seconda della tecnologia scelta si sceglierà un middleware piuttosto che un altro. Che cos’è una architettura Client-Server? La caratteristica di una architettura Client-Server è quella di avere appunto un Server che fornisce dei servizi e un insieme di Client che usufruirà di quei servizi. Bisogna fare attenzione però alla differenza fra aspetto logico e fisico, perché quando si parla di Client - Server ci si può riferire sia alla macchina sia al processo, ossia programma in funzione su quella macchina. Dal nostro punto di vista ci si riferisce ai processi, quindi parleremo di ClientServer sotto l’aspetto logico. La caratteristica dei Client è che non percepiscono l’esistenza di altri Client mentre conoscono il Server poiché con esso interagiscono. Diversi processi server possono essere eseguiti su uno stesso processore, infatti se più client si collegano e chiedono diversi funzionalità, di quel server vengono eseguiti processi diversi ma su una stessa macchina ed è qui che sta la difficoltà di gestione. Sappiamo che una qualunque applicazione deve essere in qualche modo strutturata, bisogna dare un impostazione al codice in modo che sia leggibile. In generale un’applicazione è organizzata in tre livelli: Presentazione; Elaborazione;. Parte dati. Dati questi tre livelli vediamo come un’architettura Client-Server si presta bene a implementare questo tipo di strutture. Se stiamo realizzando un sistema distribuito, ogni strato dell’applicazione deve avere una strutturazione in tre livelli dell’applicazione che andiamo a realizzare. Le architetture Client-server possono essere a vari livelli. Posso avere un solo livello Client e un livello server, o tre livelli: client, server e terzo livello, oppure livelli multipli: Il client ed N livelli con caratteristiche affini ai Server. Se si ha un’ architettura a due livelli essa può essere di due tipi: Thin Client ( A Client leggero) Fat Client (A Client Pesante) Se si ha un’ architettura a Client leggero, il server sarà ovviamente pesante poiché su di esso cadrà tutta l’elaborazione mentre il Client svolgerà solo l’azione di Interfaccia e di invio dei comandi. Lo svantaggio è ovviamente l’appesantimento del server e il forte utilizzo della rete che verrà richiamato ogni qual volta verrà fatta una richiesta dal Client ed inoltre la potenza del Client non verrà sfruttata al massimo. Se invece ho un’architettura a Client pesante, l’elaborazione è svolta in parte dal Client oltre al lavoro di presentazione. Il server oltre al lavoro di elaborazione gestisce le transizioni sul 24 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari DATABASE. Un esempio classico è quello del Bancomat, per un motivo di sicurezza e perché c’è una continua interazione. Lo svantaggio ovviamente sta proprio nella complessità, infatti una modifica comporterà un notevole lavoro di adeguamento. In generale un organizzazione a due livelli avrà svantaggi a livello di scalabilità, prestazioni, e complessità. L’altro tipo di architettura client-server è quella a tre livelli che si mappa perfettamente sulla struttura logica dell’applicazione: la presentazione sarà su client, l’elaborazione sarà sul livello centrale e la gestione dei dati su un terzo strato. I processi possono essere eseguiti su macchine distinte, ed alcuni livelli possono essere sulla stessa macchina ma il fatto che si abbia una maggiore strutturazione fa sì che le prestazioni aumentino. Ad esempio se ci si trova davanti a un sistema di internet banking,si avrà il database dei clienti e quindi un mainframe che si occuperà solo della gestione dei dati, poi si avrà un server web che fornisce i servizi applicativi( login, transazioni, gestione conto ecc) e infine il client dell’utente finale che grazie al browser interagisce con il resto del sistema. Vantaggi: Separando il database dal server si possono usare protocolli più veloci, si ha migliore gestione dei dati soprattutto dal punto di vista della sicurezza e della protezione dei dati perché il server in tal modo non ha accesso ai dati. Miglior bilanciamento del carico. Estendendo il modello a tre livelli posso ottenere un architettura a n livelli, quando ad esempio le applicazioni devono accedere a dati di database diversi o se si struttura il server in più livelli server. E’ necessario nel caso in cui si abbiano più database server, un server applicativo che lavori per l’integrazione dei dati, in modo da unirli in un solo formato da inviare al resto dell’applicazione. Confrontiamo quindi ora le architetture a tre livelli con quelle a due, analizzando i vantaggi e gli svantaggi dell’una e dell’altra. Abbiamo già notato come le architetture a tre livelli garantiscano una maggiore scalabilità rispetto a quelle a due, e un maggiore bilanciamento del carico. Si ha un minore traffico di rete, infatti quelle a due livelli intasano la rete perché c’è una continua richiesta al server, invece se il server è su più livelli il traffico è più distribuito. Più la struttura è a livelli, migliore sarà la manutenibilità, la facilità di aggiornamento e la modificabilità. La latenza è minore in una struttura a tre livelli, perché essendoci una divisione dei compiti la risposta arriva più rapidamente, a differenza di quella a due livelli in cui fa tutto il server. Quando è preferibile usare l’architettura a due livelli a client leggero, a due livelli a client pesante, o l’architettura a tre livelli? Per quanto riguarda le architetture a Client leggero, esse si usano preferibilmente in sistemi ereditati, di vecchio stampo,realizzati con linguaggi obsoleti. Non avendo strumenti per modificarli, si usa un interfaccia di connessione per renderli accessibili in rete, senza stravolgere tutto il funzionamento. Il server è quella che era prima l’applicazione e si pone solo un’ interfaccia di connessione. Oppure in applicazioni a calcolo intensivo,come i compilatori, in cui nel client ho solo l’interfaccia, e l’elaborazione, che essendo complessa è difficile da rendere distribuita, in unico server. Oppure ancora applicazioni in generale che hanno intensa elaborazione sui dati. E’ preferibile il Client pesante in applicazioni di calcolo grafico, matematico o in ambienti in cui è necessario che l’utente abbia interazione maggiore con il sistema. A tre livelli invece quando si hanno dati memorizzati in database o elaborazione intensiva di dati. Oppure quando bisogna integrare dati provenienti da fonti diversi. Il modello Client-Server ha però dei suoi svantaggi. Infatti esso è scarsamente flessibile,e la scalabilità e il bilanciamento del carico non sono impliciti. 25 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari Per questo esistono altre architetture, sempre basate su componenti, in cui però il vincolo che ci sia un componente che fornisce servizi e un componente che richiede servizi, decade. Si avranno più componenti in grado sia di richiedere che fornire servizi agli altri componenti. Una struttura abbastanza complessa, con tecnologie di implementazione altrettanto complesse. La caratteristica è che anche qui è presente un middleware, più complesso di quello presente su un Client-Server, che in architetture di questo tipo viene chiamato ORB , Object Request Broker. Si tratta di un’ architettura aperta, molto scalabile, flessibile. Il sistema può essere riconfigurato dinamicamente. L’aspetto progettuale importante è il fatto che le decisioni su dove devono essere collocati i diversi servizi, non devono essere stabilite a priori come nel Client-Server. Si può in corso d’opera aggiungere o modificare le funzionalità. Studiamo per esempio un’ applicazione di vendita al dettaglio. Essa può essere strutturata in base a diversi approcci: Modello logico di un’architettura a oggetti distribuiti, in cui le funzionalità sono i servizi o la combinazione di servizi (controllo merci, ordinazione, ecc). Modello Client-Server, in cui tutto va sul server e il client si occupa solo dell’interfaccia nei singoli punti vendita. Esempi in cui è preferibile usare architetture a oggetti distribuiti: catena di negozi o sistemi data mining, ossia sistemi in cui a partire da diversi insieme di dati si vogliono ricavare informazioni su particolari aspetti. Vantaggi e svantaggi: Maggiore complessità rispetto al Client-Server. Più efficiente, prestazioni migliori in determinati applicativi Una caratteristica importante dal punto di vista implementativo è data dalle tecnologie che devono essere utilizzate. Nell’architettura a oggetti distribuiti possono essere integrate componenti con linguaggi diversi. Il compito del middleware o il broker ha il compito di integrare queste differenze ed è proprio un pattern architetturale. In generale il Middleware deve garantire la comunicazione a livello logico e a livello di componenti. Passiamo ora ai modelli di calcolo distribuito inter-organizzativo, considerando i due seguenti modelli di calcolo: Peer-to-peer; Architettura orientata a servizi; Si chiama inter-organizzativo perché, a differenza delle architetture a oggetti distribuiti che solitamente vengono utilizzate per realizzare applicativi nel contesto aziendale, dove tutti i nodi è come se fossero circoscritti a livello organizzativo enon c’è un’accessibilità dall’esterno, questi due modelli sono i modelli più aperti sulla rete, come ad esempio i sistemi che permettono la prenotazione di un viaggio, in cui servizi diversi, forniti da applicativi diversi che non hanno nessuna affinità tra di loro, vengono collegati dinamicamente quando l’utente fa la sua richiesta. Non c’è uno schema predefinito dell’architettura, ma c’è il vincolo che ogni servizio deve trascrivere le proprie funzionalità in un formato dati conosciuto anche dagli altri, essere recuperabile da un elenco di tutti i servizi possibili ed esporre un interfaccia facilmente integrabile negli applicativi che si dovranno ad esso connettere. Queste sono le differenze sostanziali tra un architettura a oggetti distribuiti e un’architettura a servizi distribuiti. 26 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari Vantaggi: I servizi possono essere forniti da fornitori che sono sia dentro che fuori del sistema. Esiste una sorta di catalogo pubblico di servizi. Ciò comporta un’elevata affidabilità,perché se in quel momento richiedo un servizio da un applicativo che non è in funzione posso immediatamente rimpiazzarlo con un altro, e quindi risulterà sempre minore la situazione in cui una funzionalità non posso essere fornita, perché il sistema è in grado di individuare subito un altro applicativo che fornisce la stessa funzionalità. Il fornitore può riconoscere nuovi servizi, quindi alta adattabilità, scalabilità, riconfigurabilità. Vincolo del pagamento, talvolta i servizi sono a pagamento Le applicazioni sono reattive, quindi si adattano al contesto. Nel peer-to-peer abbiamo invece una struttura decentralizzata, dove tutte le elaborazioni possono essere svolte dai singoli nodi della rete, e lo scopo è quello di condividere le risorse di calcolo tra diversi computer presenti su una rete anche di dimensioni notevolmente ampie. I sistemi p2p sono utilizzati principalmente per sistemi personali, in cui si vogliono condividere file, messaggeria istantanea o comunicazione diretta con gli utenti. L’architettura logica della rete è la stessa del sistema. L’applicazione in genere è comunque basata su componenti che possono essere distribuiti sui singoli elementi dell’architettura hardware sottostante. L’architettura logica(software) può essere decentralizzata o semi-centralizzata. Nell’architettura decentralizzata ogni nodo può essere a conoscenza degli altri nodi e scambiare direttamente informazioni, i nodi sono organizzati in località e pertanto alcuni nodi fungono da ponte tra un nodo e un altro. Non solo perciò dei singoli elementi funzionanti ma sono anche dei commutatori nella comunicazione. Ovviamente si ha ridondanza, che può essere sia un vantaggio che uno svantaggio, poiché essa protegge il sistema dal verificarsi degli errori, e over-head del sistema perché la stessa ricerca può essere elaborata da diversi nodi e quindi si ha maggiore comunicazione e sovraccarico del sistema(Svantaggio). Nell’architettura semi-centralizzata invece, uno o più nodi svolgono funzioni tipo Server, che indirizzano la comunicazione verso altri nodi, quindi si ha un minimo di gerarchia ma l’applicazione funge sempre come se non ci fosse un server. Rispetto all’architettura a servizi distribuiti il P2P è più efficiente, però essendo in connessione 1 a 1 si ha una maggiore esposizione ai rischi di protezione. E’ preferibile non utilizzarli in ambienti lavorativi. Abbiamo quindi categorizzato i sistemi distribuiti da un punto di vista software con questi quattro modelli, che ci definiscono le caratteristiche generali secondo cui i componenti dell’architettura del sistema interagiscono. Conoscere questi quattro modelli però non ci basta perché le architetture e i sistemi software da realizzare sono molto complessi e quindi ci occorre fornire ulteriori linee guida per integrare i diversi componenti. Per questo passiamo ai Pattern architetturali. Sappiamo che ci sono dei modelli architetturali e a seconda di questi modelli posso scegliere quali pattern architetturali utilizzare. Un pattern software è la descrizione strutturata di una soluzione a un problema software ricorrente. Ad esempio se ho un’architettura a servizi, so che ci sono tante componenti distinti che comunicano, che ci deve essere un’interfaccia,che ci deve essere un catalogo con relativi servizi 27 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari forniti però l’architettura non mi dice altro e ho bisogno di scegliere pattern architetturali per capire come integrare questi servizi soprattutto perché l’integrazione avviene dinamicamente. I pattern possono essere classificati in : Pattern architetturali (livello più astratto), che modellano lo schema organizzativo di una struttura nell’ ambito di un’architettura software. Mi fa capire come i vari componenti possono interagire tra loro, come far avvenire la comunicazione con il database ecc. Pattern di design: prendo ogni componente, in ogni componente abbiamo i package in cui vi sono le singole classi. Anziché modellarle in maniera empirica posso raggrupparle utilizzando degli schemi predefiniti(design pattern) che rendono più elegante lo schema delle classe e che risolvono una determinata problematica. Pattern di implementazione(frame work), livello di minore astrazione perché siamo nella fase di scrittura del codice. Partiamo dai pattern architetturali. Ipotizziamo di modellare un sistema software, ipotizzando di utilizzare il processo unificato, abbiamo concluso la fase di ideazione e quindi abbiamo individuato i requisiti funzionali e non, che ci permettono di capire che architettura utilizzare e come organizzare i componenti. In questo ci possiamo avvalere dei pattern architetturali. I pattern non sono tutti uguali ma possono essere raggruppati a seconda dei problemi che risolvono, ad esempio abbiamo famiglia di pattern che risolvono i problemi di come il sistema si deve interfacciare al database. Oppure abbiamo l’insieme dei pattern che gestiscono la comunicazione dei componenti nel sistema distribuito, ed è l’insieme più ampio ed importante dato che la comunicazione è un aspetto molto importante nei sistemi a oggetti distribuiti. Poi abbiamo tutti i pattern che si occupano dell’interfaccia. O ancora i pattern che si occupano di come modellare i componenti sul livello client, sul livello server, e sull’application server. Altri pattern importanti sono quelli che gestiscono l’application e l’extend. E poi abbiamo i pattern che gestiscono i problemi di sincronizzazione, ossia quando si hanno delle risorse condivise bisogna sincronizzare l’accesso alla risorsa. Pattern per problemi di concorrenza,secondo cui diversi processi richiedono l’uso di un determinato processo e pertanto bisogna schedulare l’accesso alle risorse nonché la concorrenza,e poi problemi di gestione delle risorse. I pattern relativi alla decomposizione del sistemi in sottosistemi sono i più numerosi, partendo dai requisiti vedo come posso organizzare il sistema avvalendomi di alcuni pattern come ad esempio il Layer ( modello a strati, esempio: sistema operativo) ,Domain Model, Domain object. Abbiamo poi una seria di pattern che modellano la comunicazione nei sistemi distribuiti, il più importante è il PATTERN BROKER. Sul modello Broker è strutturato il middleware. L’altro gruppo importante è quello dei pattern che supportano l’interazione uomo macchina, e i più importanti sono il Model View Controller e il Presentation abstraction Control. I pattern non sono indipendenti, alcuni sono in alternativa, altri lavorano in sinergia, in più si possono mettere insieme per risolvere problemi più grandi. Se si rappresentano tutti otteniamo un enorme struttura che collega tutti i pattern in maniera diretta. Analizziamo quindi in dettaglio alcuni di essi: LAYERS E’ il modello che separa le funzionalità dei singoli componenti. Ogni livello fornisce funzionalità a livelli superiori, ma i livelli superiori sono trasparenti pertanto funziona come una macchina virtuale. Posso modificare un livello senza modificare gli altri. 28 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari DOMAIN MODEL Modello concettuale. La prima fase che si realizza è l’analisi del modello di dominio. Lavora nell’ottica della modularità DOMAIN OBJECT Permette di strutturare un modello di dominio in componenti più specifiche. MODEL VIEW CONTROL Separa l’interfaccia dall’applicazione. Ci permette di rappresentare il contenuto del Client, del Server, e dell’application Server . Quindi rappresenta delle Classi di interfaccia, delle classi di controllo che elaborano la logica di business delle applicazioni, e delle classi entity, che si definiscono nella prima fase di analisi e restano il nucleo di modellazione dei dati. Il controllore conterrà dei package delle classi di controllo, il modello sarà costituito dalle classi individuate in fase di analisi, in vista ci saranno le classi che si occuperanno dell’interfaccia. PIPE AND FILTER E’ generalmente utilizzato nel contesto dei sistemi operativi, nei sistemi real-time e in particolare per sistemi che devono elaborare flussi di dati. L’elaborazione viene scomposta in passi di decomposizione e ogni passo viene incapsulato in un filtro, un oggetto software, i dati poi sono trasferiti in filtri adiacenti detti Pipe, che sono immaginabili come tubi, dei canali virtuali dove 29 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari passano flussi di dati. E’ possibile mettere in relazione famiglie di dati correlati, collegando in maniera adeguata i filtri e i Pipe, creando cosi delle PipeLine. SHARED REPOSITOR Utilizzato per applicazioni a intensa elaborazione dati, in cui ci sono interazioni tra i componenti che non siano guidate da specifici processi, che però possono essere coordinate in base a un set di dati condivisi, un repository. Ho un repository e tutti i processi che lavorano su quel repository.Il repository è condiviso, ed è come se fosse a partire dal repository che avviene il coordinamento dei vari componenti e dei vari processi di elaborazione. L’accesso ai dati deve essere sincronizzato. DATABASE ACCESS LAYER (DAL) Serve per separare la logica dell’applicazione dal modello dei dati. Si ha uno strato di interfaccia tra il sistema e il database management system. Questo strato si occupa delle transazioni di Query. BLACKBOARD E REFLECTION Sono due pattern che permettono la modifica dei requisiti a run time. Il blackboard è utilizzato in sistemi di intelligenza artificiale, per creare una sorta di “Lavagna” comune su cui tutte l applicazioni possono comunicare. Il reflection consente i cambiare la struttura di un sistema in modo dinamico, strutture dati meccanismi di comunicazione, dividendo l’architettura in due livelli, ossia da una parte i Metadati e poi un livello base che contiene la logica applicativa stessa del meta livello BROKER E’ il pattern principale per la comunicazione nei sistemi distribuiti. Serve per strutturare sistemi distribuiti con componenti disaccoppiate che devono interagire. Rende possibile l’invocazione di servizi remoti tra i vari componenti, coordina la comunicazione, gestisce le richieste e le risposte tra i vari componenti di un sistema distribuito e pertanto è particolarmente utilizzato. 30 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari MICROKERNEL Consente di sviluppare un insieme di applicazione che siano l’una la variazione dell’altra. Il nucleo centrale è sempre lo stesso però possono variare dinamicamente a seconda dei requisiti dell’utente o in base al contesto di utilizzo di funzionamento di quell’applicazione. Si possono differenziare per l’interfaccia utente a seconda ad esempio del Profilo utente esistente. Vediamo ora un aspetto un po’ più tecnico che riguarda le tecnologie di implementazione. TECNOLOGIE DI IMPLEMENTAZIONE Per tecnologia intendiamo non solo il linguaggio di programmazione ma anche tutti i requisiti ad esso legati. Se si sceglie di usare un particolare linguaggio, si dovrà adattare la struttura del sistema, le specifiche nel codice, le piattaforme da utilizzare e tutto il contesto che c’è dietro le tecnologie di implementazione. Prima di parlare di tecnologie diamo una definizione di componente. Cosa è un componente? Per dare questa definizione,entriamo nel merito del modello di processo Component Based, che non è solo un modello di processo perché può essere usato trasversalmente agli altri.E’ un processo di definizione, implementazione o integrazione di componenti debolmente accoppiati, ossia che abbiano lasca connessione tra loro. Il Component Based Software Engineering nasce dal concetto di modularità e riutilizzo, e si basa su componenti indipendenti e standardizzate, sul middleware e sul processo di sviluppo. Secondo l’OMG un componente è una parte modulare, utilizzabile e sostituibile di un sistema che comprende l’implementazione ed espone una serie di interfacce. Sostanzialmente un componente è un elemento costitutivo del software. Un componente deve essere standardizzato, indipendente, componibile (si deve poter integrare nelle interfacce pubbliche del sistema), consegnabile e documentato. Se abbiamo un sistema basato su componenti abbiamo un middleware, che è lo strato che troviamo tra il software applicativo e il sistema operativo, e implementa un meccanismo di comunicazione tra processi. La comunicazione tra processi è generalmente nota come IPC (Inter Processing Communication). Essa avviene mediante diversi meccanismi e schemi a diversi livelli. Può avvenire o a basso livello, o a livello di linguaggio di implementazione, o a livello applicativo o mediante software. A seconda di dove avviene la comunicazione, cambieranno le primitive e i meccanismi di comunicazione. A diversi livelli possiamo utilizzare il meccanismo di Pipe (Pipe and Filter), o il socket, che è un canale di comunicazione a livello più basso di quello applicativo, oppure mediante il memory mapped file,quindi a livello hardware, oppure mediante chiamata a procedura remota (RPC), a livello applicativo e sempre a questo livello può essere utilizzato il meccanismo di scambio di messaggio. In base alla modalità secondo cui avviene IPC possiamo caratterizzare il middleware secondo tipologie diverse: Remote Procedure Control, quando abbiamo architetture Client- Server; Remote data access, simili a RPC, tipicamente utilizzate per accedere a basi di dati; Message Oriented middleware; Transaction processing monitor; 31 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari Distributed object, in cui l’invocazione dell’oggetto avviene tramite il broker. I modelli di componenti possono essere standardizzati, infatti a parte i nostri componenti, ci sono componenti standard che vengono acquistati, integrati nell’applicazione e che sono ad esempio quelli del formato Microft.net, nel formato Corba, o nel formato Java beans. Tra i componenti Microsoft troviamo i seguenti: Com, Component Object Model; Dcom, Distributed Component Object Model; Com+, Soap, protocollo a livello ancora più alto nella comunicazione tra applicazioni ed è basato su XML, ossia un linguaggio che ci permette di formattare dei dati, e creare dele strutture nell’ambito dei dati. Linguaggi di mark-up. .Net, componente più standardizzato e moderno per eterogeneità di linguaggio. L’OMG (Object management group) definisce lo standard di oggetti per esempio CORBA per oggetti distribuiti. Corba è la tecnologia per implementare architetture a oggetti distribuiti più diffusa,si basa sul broker e garantisce eterogeneità di linguaggi e di piattaforme, ossia posso realizzare applicazione in cui i vari oggetti sono realizzati con linguaggi diversi e funzionano su piattaforme diverse, il middleware Corba permette la comunicazione tra questi oggetti eterogenei e realizzare l’applicazione distribuita in cui i componenti sono tutti allo stesso livello. L’altro standard é SUN, infatti se standardizziamo i componenti secondo questo meccanismo, la comunicazione avviene tramite RMI( Remote method information), ossia gli oggetti possono essere eseguiti su macchine virtuali diverse e comunicare mediante chiamate a metodi remoti. Gli oggetti si chiamano Java beans, o enterprise java beans se rientrano in una fascia di componenti più complessi. Se invece siamo in un contesto Web e vogliamo realizzare applicazioni Client- server, dobbiamo partire dalla definizione di applicazione web, ossia “un sistema che permette ai propri utenti di eseguire la logica di business con un browser” (Conallen, 2000). In un’applicazione web troviamo una logica di business che può risiedere o sul client o sul server, un browser che è indispensabile per interpretare parte della logica che verrà eseguita sul Client, e pagine web che possono essere statiche o dinamiche. L’application server è il server applicativo che monitora lo stato dell’applicazione, importante per tenere traccia delle operazioni effettuate, dello stato degli utenti e così via. Ad esempio come monitorare mediante i cookie, rappresenta lo stato dell’utente collegato, e ci permette di avere traccia delle operazioni svolte dai diversi Client. Questo è molto utile soprattutto se il numero di utenti è molto elevato. Possiamo rendere dinamica una pagina web tramite uno script, ossia un programma interpretato dal browser. L’interprete interpreta ed esegue direttamente istruzione per istruzione, invece il compilatore è un traduttore che dato un programma sorgente genera un programma eseguibile. Sono due fasi distinte, solo che in determinati casi, come in Java, si ha un linguaggio che per metà è interpretato e per metà viene compilato. Quando invece abbiamo la pagina server, possiamo utilizzare diverse tecnologie come le java server pages o le servlet se utilizziamo una tecnologia Java. Abbiamo visto che a livello progettuale il modello logico di un applicazione su un architettura client server, si realizza con il pattern MVC (Model view Control), simile a questo pattern è il 32 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari pattern BCE, Boundary control entità, e trova anche riferimento nell’UML. Lo scopo è individuare i confini del sistema, caratterizzare i dati, progettare e implementare la logica di controllo dell’applicazione stessa. Il pattern trova una sua diretta configurazione nell’UML perché è possibile utilizzare gli stereotipi delle classi stesse: <<Boundary>>. Le classi Boundary sono classi di interfaccia tra l’attore e il sistema, e lo scopo è quello di isolare il sistema dai cambiamenti che avvengono nell’ambiente esterno e di interfacciare le classi con altri sistemi. Le classi Control sono quelle che si collocano al centro del modello, e quindi nel lato server e gestiscono tutte le operazioni di elaborazione e implementano la logica di controllo delle applicazioni (ad esempio le servlet delle applicazioni Java).sono indipendenti dall’ambiene esterno e non sopravvivono all’interazione. Infine le classi entità definiscono il modello dei dati, e nascono già in fase di analisi, sono le prime che derivano direttamente dai requisiti. Nelle fasi successive saranno sempre presenti ma saranno affiancate da altre classi (Boundary e Control). A causa del gap esistente tra il modello dei dati relazionali e il modello dei dati ad oggetto, le classi entità rappresentano la modellazione dei dati nell’ambito però del nostro sistema, e tramite esse avverrà l’accesso dai dati da elaborare che saranno presenti nel database, mediante l’utilizzo dell’ibms o mediante il DAL che implementerà la logica di accesso al database. Eventualmente si può pensare di inglobare parte di questa elaborazione nel livello entità oppure di aggiungere un ulteriore livello che nella formalizzazione del modello BCE si formalizza con una D per cui diventa BCDE, Boundary Control Entity Database ed è l’applicazione di un modello DAL che realizza solo l’implementazione delle transazione e delle query. Lo schema grafico è il seguente: Estendendo l’architettura a tre livelli a una a N-livelli vengono introdotti un livello USER e uno INTEGRATOR, il primo è molto vicino al livello di presentazione, mentre nel secondo si può prevedere l’introduzione di un broker in modo che si integrino dati provenienti da database diversi, e quindi un ulteriore livello in modo da rendere l’applicazione mappabile sull’architettura a tre livelli. Le applicazioni ENTERPRISE, sono una categoria di applicazioni il cui nome si riferisce sia alle applicazioni che le tecnologie , e sono riferite ad applicativi in un contesto ampio di grossa organizzazione, utilizzate per fornire delle funzionalità di supporto alla logica di business anche per una grossa impresa, al di là del semplice portale che si realizza si può immaginare la stessa struttura però proiettata in un contesto molto più grande e quindi si parlerà di applicazione enterprise. Il meccanismo è lo stesso ma cambia la granuralità e l’ampiezza del contesto, cambiano le tecnologie e anche i modelli architetturali. Facciamo ora un piccolo riepilogo di tutto ciò che abbiamo fatto. Ci stiamo occupando di come ingegnerizzare un processo di sviluppo di software e per fare ciò bisogna sia rendere ingegneristico il processo, ossia la sequenza delle fasi necessarie per portare a termine il prodotto, sia definire i criteri secondo cui quel prodotto deve essere realizzato. Per quanto riguarda il processo vi sono vari modelli che si possono seguire, di cui il più moderno è l’UP (Processo Unificato) che si modella bene in relazione al linguaggio di modellazione che è l’UML. Per quanto riguarda il software da realizzare, lo sviluppo non è d’impatto, ma è frutto di un lavoro più o meno approfondito che 33 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari parte dall’analizzare il dominio e il contesto di utilizzo e passo dopo passo arriva ad analizzare le funzionalità che occorre fornire, tutte quelle che possono essere realizzate, come strutturare il sistema in un modello component-based e definendo un architettura di massima, cioè come i vari componenti devono interagire. L’aspetto più complesso è quello di definire l’architettura, sia perché esistono vari modelli che dipendono dal tipo di sistema che vogliamo realizzare, infatti se ad esempio l’applicazione è orientata al web si preferisce un modello ad un altro, e poi perchè dopo aver scelto l’architettura è necessario scegliere delle tecnologie di implementazione. Dopo ciò va progettato ogni componente, quali sono i package, le classi, e va visto come il sistema deve funzionare, e quindi la sua dinamicità che deriva in parte dalla prima fase di analisi in cui vengono individuati i requisiti funzionali, che, di volta in volta, devono essere specificati nel dettaglio e faranno da tramite per scrivere il codice del sistema in maniera più agile. Una parte molto importante è tutta la fase di test, in cui il sistema che è stato realizzato, deve essere valutato per capire se è un sistema di qualità, se risponde a dei requisiti non funzionali,evidenziati nella fase di analisi, che specificano come il sistema deve realizzare quelle funzionalità, quindi se deve garantire affidabilità anziché scalabilità e cosi via. DESIGN PATTERN Si tratta di soluzioni già pronte, che permettono di realizzare il principio del riuso, uno dei principi base dell’ingegneria del software, non solo a livello di codice (librerie di classi) ma anche a livello di progettazione, perché abbiamo dei pattern(schemi vuoti) che possono essere adattati allo schema delle classi che vogliamo realizzare. Attraverso questa tabella possiamo classificare i pattern secondo lo scopo (Creazionali, Strutturali o comportamentali) e il raggio d’azione (per Classi o per Oggetti). Questi qui sotto elencati sono i più noti e più utilizzati. Raggio d’azione Classi Oggetti Scopo Creazionale Factory method Abstarct factory Builder Prototype Singleton Strutturale Adapter Comportamentale Interpreter Template Method Adapter Bridge Composite Decorator Facade Flyweight Proxy Chain of responsibility Command Iterator Mediator Memento Observer State Strategy Visitor 34 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari Partiamo dai pattern Strutturali, i quali si occupano del problema di come comporre le classi e gli oggetti per formare delle strutture più complesse. Abbiamo sia pattern basati su classi che basati su oggetti. La differenza sta nel fatto che quelli basati su classi le interfacce e le implementazioni vengono composto mediante l’ereditarietà, quelli basati su oggetti descrivono la modalità secondo cui comporre gli oggetti per realizzare nuove funzionalità, ovviamente in questo modo è possibile fornire maggiore flessibilità. L’Adapter è quello più utilizzato in assoluto poiché fornisce la funzionalità di convertire l’interfaccia di una classe nell’interfaccia di un'altra classe. Supponiamo di avere un editor grafico in grado di comporre disegni ma anche testo. L’incompatibilità sta nel fatto che testo e linee e poligoni sono classi con interfacce diverse e quindi per rendere simili uso l’adapter. Vediamo un esempio grafico: Supponiamo di avere un editor grafico in grado di disegnare sia grafici sia testo. E’ evidente dove è l’incompatibilità,infatti essa sta nel fatto che testo e poligoni sono classi con interfacce diverse ma non del tutto incompatibili. Pertanto bisogna trovare un modo per renderle compatibili. Supponiamo di avere una Shape astratta, quindi una classe che definisca le caratteristiche degli oggetti grafici, e l’applicazione definisca una sotto classe di shape per ogni oggetto specifico. Se volessi implementare una Text shape essa non può essere messa direttamente come sottoclasse di Shape (ossia della forma geometrica) poiché il testo ha caratteristiche ben diverse. Vorrei quindi riutilizzare un classe (text view) per visualizzare e modificare il testo. La tentazione sarebbe quella di modificare la classe, ma ciò è totalmente sbagliato!! E’ sbagliato concettualmente perché si sta venendo meno al principio della riusabilità,poiché se si ha qualcosa di rigidamente già definito si può utilizzare in vari contesti, adattandolo ma mai stravolgendolo! Se non è riusabile direttamente non si deve modificare, ma si deve fare in modo che essa possa essere utilizzata, adattata. Per cui bisogna utilizzare una nuova classe “Text shape” che vada ad adattare la text view e permetta di rendere compatibili la shape e la text view senza modificarle. L’editor grafico può utilizzare la classe text view come se fosse un “figlio” della classe shape. 35 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari Quali sono i vantaggi dell’adapter? Esso fornisce delle funzionalità che la classe adattata in caso contrario, non avrebbe. Quando utilizzarlo? La difficoltà dei pattern sta proprio nel capire come e quale pattern utilizzare. In linea di massima usiamo l’adapter quando abbiamo una classe esistente con interfaccia diversa con un'altra e vogliamo che esse interagiscono. Oppure quando si vuole creare una classe riusabile che si possa correlare con classi con interfacce diverse. La struttura di un adapter in sintesi è di questo tipo: Nell’Adapter abbiamo una classe Target che definisce l’interfaccia specifica del dominio utilizzato dal client, che è il resto del sistema. L’adaptee è l’interfaccia che deve essere adattata e l’adapter è la classe che si occupa appunto di rendere le due interfacce compatibili. Simili all’adapter vi sono il Bridge,il Proxy e il decorator. Il Bridge ha come obiettivo quello di separare l’interfaccia dall’implementazione. Disaccoppia l’astrazione dalla sua implementazione. Esso si usa quando non si vuole un legame permanente tra astrazione e implementazione. Se ad esempio voglio poter variare dinamicamente l’implementazione di una classe. Oppure quando voglio estendere sia l’implementazione che l’astrazione usando il meccanismo delle sottoclassi. Per quanto riguarda la struttura si ha una classe astratta con diverse implementazioni di essa, e la classe astrazione che non ha nessuna relazione di ereditarietà con le classi concrete implemenction. 36 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari L’abstraction è l’ interfaccia dell’astrazione che ha un riferimento all’implementor che è il vero e proprio bridge, e definisce le interfacce per le classi implementatrici e non corrisponde totalmente all’interfaccia dell’abstraction perchè deve permettere di eseguire una piuttosto dell’altra tra le implementazioni esistenti .Il concrete implementor definisce le varie implementazioni con la realizzazione completa di un algoritmo piuttosto che un altro. Esempio: Supponiamo di implementare un toolkit per interfacce utente in ambiente Windows che permetta di scrivere applicazioni che siano eseguite su piattaforme X o IBM. Se usassimo l’ereditarietà dovremmo stabilire staticamente una sottoclasse per ogni tipo di finestra, e quindi sarebbe piu difficile seguire le applicazioni su piattaforme distinte mentre con l’implementor scelgo se seguire l’implementazione su piattaforma X o IBM. Si spezza la gerarchia dell’ereditarietà, L’aspetto di implementazione è distinto e gestito da un ulteriore classe. Il DECORATOR permette di aggiungere dinamicamente responsabilità ad un oggetto, cioè invece di definire delle sottoclassi per estendere delle funzionalità si aggiunge un decorator che aggiunge queste funzionalità. Per aggiungere funzionalità a singoli oggetti e non all’intera classe, e ciò fa capire che si sta parlando di qualcosa che succede a run-time, modifica dell’oggetto dinamicamente. In caso contrario bisognerebbe creare una sottoclasse staticamente appesantendo una struttura di ereditarietà con qualcosa che non sempre sarebbe strettamente necessario. Il decorator ha interfaccia conforme a quella dell’oggetto da estendere in modo di renderlo trasparente al client (ossia a tutto il resto del sistema, le richieste vengono trasferite al componente decorato, l’altra caratteristica è che consente l’aggiunta di un numero “illimitato” di funzionalità e non compromette altre classi. La struttura ricorda un po’ quella del bridge poiché il decorator fa da tramite tra il componente e la sottoclasse di esso. Fa da tramite per poi selezionare una funzionalità piuttosto che un'altra, dinamicamente. 37 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari La struttura è la seguente: Il Component definisce l’interfaccia comune per gli oggetti ai quali possono essere aggiunte responsabilità dinamicamente, Il concrete Component definisce un oggetto al quale possono essere aggiunte responsabilità ulteriori, il decorator è la sottoclasse che mantiene il riferimento al component e definisce l’interfaccia conforme a quella di component che però faccia da tramite con il concrete decorator, il concrete decorator sono quelle funzionalità che posso aggiungere all’oggetto. IL Proxy ha lo scopo di fornire il surrogato o un segnaposto di un altro oggetto per controllare l’accesso a tale oggetto. Quando per esempio in documenti di testo inserisco l’immagine in realtà inserisco un proxy. La motivazione per cui utilizzare il Proxy è quello di controllare l’accesso a un oggetto. Lo si applica quando si necessita avere riferimento a un oggetto che sia più versatile e raffinato di un semplice puntatore. Il proxy è un oggetto vero e proprio che ha una sua caratterizzazione. Esso può essere remoto,quando fornisce rappresentazione locale di un oggetto in diverso spazio di indirizzamento , virtuale, quando si vuole risparmiare in termini di memoria e di tempo di esecuzione, o un proxy di protezione se voglio controllare l’accesso a un oggetto, quando ad esempio si hanno dei diritti di accesso su un oggetto. ll Subject è l’interfaccia comune mentre il real subject è l’elemento duplicato dal Proxy. 38 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari Possiamo quindi arrivare a commentare i pattern strutturali. Essi si basano su un insieme di meccanismi messi a disposizione da linguaggi di programmazione che possono risolvere problemi di implementazione di aggiunta di funzionalità e cosi via, a run time. Solo nel caso dell’adapter il problema viene risolto staticamente. I meccanismi sono quelli di composizione tra gli oggetti. La seconda categoria di pattern sono quelli COMPORTAMENTALI di cui studieremo l’Observer e lo Strategy. Essi in generale si occupano di come attribuire le responsabilità ad oggetti che comunicano tra loro, a run time, problemi simili a quelli del Bridge. Quando si hanno flussi di informazioni difficili da seguire, e quando vogliamo studiare le relazioni dinamiche tra oggetti. Quei pochi pattern basati su classi utilizzano l’ereditarietà, mentre quelli su oggetti utilizzano la composizione. Con l’uso dei pattern si riesce a comunicare, modificare e uant altro mantenendo molto basso il livello di accoppiamento. L’OBSERVER E’ un pattern di collaborazione tra oggetti, di design. Lo scopo è quello di definire una dipendenza uno a molti tra oggetti in modo tale che se un oggetto cambia il suo stato, tutti gli oggetti ad esso legati siano “avvisati” di questo cambiamento e aggiornati. La finalità è quella di mantenere un buon grado di affidabilità del progetto. Può essere usato in un’astrazione, una classe che presenta due aspetti tra loro vincolati. Vado a incapsulare gli aspetti in due oggetti distinti che potranno essere usati indipendentemente. Una modifica a un oggetto comporta la modifica di altri oggetti e quindi un elevato accoppiamento. Posso risolvere questo problema con l’Observer, svincolando i vari oggetti in modo tale che gli altri non debbano essere modificati. Oppure quando un oggetto deve notificare agli altri determinate situazionio senza conoscere l’identità precisa degli altri oggetti. La struttura è la seguente: 39 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari Il Subject è l’elemento che conosce i propri observer, può avere un numero qualunque di Observer che ne controllano i comportamento e pertanto ha un’interfaccia per ciascuno di essi. L’observer fornisce l’interfaccia di notifica per gli oggetti che devono essere informati dei cambiamenti del subject. Il concrete Subject fornisce lo stato a cui tutti gli oggetti sono interessati e il Concrete observer fornisce il collegamento con il Concrete Subject. Mentre l’observer risolve un problema di comunicazione tra oggetti, lo strategy è importante perchè ci permette di definire una famiglia di algoritmi senza definire una sottoclasse figlia di una superclasse. In maniera molto snella e dinamica incapsula in alcuni oggetti determinati algoritmi e poi sceglie se utilizzarne uno anzichè un altro. L’algoritmo incapsulato è detto strategy e si usa quando abbiamo classi quasi uguali che differiscono nel comportamento. Ho bisogno di più varianti dell’algoritmo ma la classe non cambia. METRICHE DI PRODOTTO Se si vuole parlare di qualità di un software, bisogna riferirsi a determinati parametri che non sono cosi espliciti come in altri prodotti. Il problema infatti sta nel fatto che non si ha nulla di tangibile su cui basarsi e pertanto bisogna usare delle strategie per capire se il prodotto è di qualità o meno. Come e quando possiamo dire che un software è di qualità? Quali sono gli aspetti che garantiscono la qualità? Non si può dire in maniera univoca se un software è di qualità poiché esso va sempre contestualizzato e valutato in base al contesto in cui agisce. Bisogna verificare che il sistema soddisfi sempre i requisiti. Se ad esempio un software soddisfa i requisiti utente ma non quelli non funzionali no si può dire che esso sia un sistema di qualità. I requisiti non funzionali servono proprio a capire la qualità del sistema. E quindi il problema sta proprio nel valutare e considerare i requisiti non funzionali, ossia gli attributi di qualità. Essi determinano una specifica di qualità. Si può cercare di mettere ordine tra tutti i possibili attributi di qualità in modo da cpaire quali sono i più importanti che il sistema deve necessariamente rispettare per essere un sistema di qualità. Il problema sta nel mettere in relazione i requisiti non funzionali con il sistema stesso. Ad esempio, un requisito non funzionale può essere la sicurezza. Ma come capisco dal sistema se il requisito è garantito o meno? IL sistema ha una sua compleeìssità, i requisiti sono parametri qualitativi e pertanto bisogna creare un rapporto tra questi due. Per fare ciò si utilizzano dei modelli di qualità e ne esistono diversi tipi. Diamo però prima la definizione di requisito secondo RUP : “Un requisito descrive una condizione o una capacità a cui un sistema deve conformarsi; può essere sia derivato direttamente da esigenze degli utenti, o indicato in un contratto, uno standard, in delle specifiche o altri documenti o essere formalmente imposto.” Un requisito architetturale ha importanza nella valutazione dell’architettura software e nella sua implementazione. Essi possono essere sia impliciti(attributi di qualità) che espliciti(di natura tecnica). (Vedere esempi di requisiti sulle slide) 40 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari Tra tutti i requisiti non funzionali impliciti, un valido tentativo di mettere ordine è dato dal modello FURPS+(Funzionalità, Usabilità, Affidabilità,Prestazione, Supportabilità) Il + rappresenta le diverse categorie di requisiti. Questo modello categorizza i requisiti non funzionali e li suddivide nelle seguenti quattro macro-categorie: Requisiti di progettazione; Requisiti di implementazione; Requisiti di interfaccia, Requisiti fisici. Si utilizza quindi questo modello per caratterizzare le varie fasi del ciclo di vita del software. Prendiamo quindi i 5 attributi di qualità che il FURPS+ identifica: Funzionalità ad esempio in un sistema di gestione di un magazzino i requisiti funzionali possono essere il controllo delle scorte o l’elaborazione degli ordini. Vedere tabella di requisiti funzionali sulle slide. Tra gli URPS troviamo: Usabilità, l’aspetto di qualità più soft, meno tecnica, è la caratteristica di qualità su come il sistema si presenta. L’affidabilità, sono le caratteristiche come la disponibilità,riguarda come il sistema è preciso, accurato e come è in grado di ripristinarsi. Prestazioni, riguarda aspetti come il tempo di troughput, di risposta, di recupero, di avvio ecc. Supportabilità, studia aspetti come configurabilità, adattabilità, scalabilità. L’adattabilità dipende dall’hardware Il + invece raggruppa le quattro categorie di requisiti. I requisiti di implementazione si occupano di linguaggi, di codifica e vincolano o specificano la costruzione di un sistema. I requisiti fisici, sono quelli che specificano come deve essere fatto l’hardware sottostante. Per esempio se dico che voglio un requisito di persistenza, posso tradurlo in un meccanismo in cuoi voglio utilizzare il DBMS. Se voglio un requisito di comunicazione, si traduce dicendo che voglio che la comunicazione si realizzi con un broker o con un meccanismo di messagging, a livello pratico posso usare due meccanismi. (Vedere tabelle su usabilità, reliability, performance e supportability da slide) Il FURPS+ fa parte di una categoria di modelli detti modelli strutturati, tra cui l’ultimo modello più importante è il modello ISO 9126. Questi tipi di modelli sono strutturati in livelli, al primo livello si trova sempre l’insieme delle caratteristiche, e poi nei livelli successivi le vado a dettagliare fino ad arrivare all’ultimo livello dove si trovano sempre le metriche, ossia il meccanismo dove si fanno dei calcoli su alcuni aspetti del sistema che permettono di valutare la qualità del sistema. Il modello ISO è quello più importante ed utilizzato, si compone di quattro aspetti. Nel primo vengono definite le 6 caratteristiche più importanti che poi vengono suddivise in 27 caratteristiche. Nelle altre tre parti abbiamo la definizione di metriche di qualità esterna, interna e in uso. La qualità pertanto viene scomposta in tre aspetti e anche gli attributi di qualità sono raggruppabili in una di queste categorie. La qualità in uso o percepita rappresenta l’efficacia del sistema, quella interna misura come il sistema possiede una serie di attributi che lo caratterizzano, quella esterna valuta il sistema come una black box da testare. 41 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari Dati i modelli di qualità ci poniamo l’obiettivo di valutare la qualità del sistema. Come faccio? Per esempio con un modello orientato all’obiettivo. Cominciamo a definire le metriche e a vedere cosa sono. “Una metrica è una misura quantitativa del grado in cui un sistema possiede un determinato attributo”. Un modello orientato all’obiettivo è il GQM, ossia un modello che a partire da dei GOAL da raggiungere e verificare, mediante opportune Question permette di individuare delle metriche che misurano uno o più parametri che permettono la soddisfazione di alcune specifiche di progetto.Costruisco poi un foglio detto foglio metrico, diviso in quattro parti che mi serve per organizzare la valutazione che sto facendo. Oggetto dello studio Scopo Prospettiva Punti di Vista Contesto Quality Focus Variation Factors descrive la relazione tra i fattori di contiene i quesiti e le metriche per variazione e le metriche del quality la conformità del processo o della focus caratterizzazione del prodotto Baselines Hypothesis Impact on Baseline Hypothesis contiene i quesiti e le metriche descrive la relazione tra i fattori di dei modelli di conferma e di variazione e le metriche del quality validità focus Vediamo le metriche più diffuse e conosciute: Metriche per il modello analitico Metriche per il modello progettuale: Metriche dei punti funzione. Metriche CK; Metriche Mood. Metriche per il codice sorgente. Studiamo nello specifico le metriche Ck e le metriche Mood. Essi sono due insiemi di metriche che ci misurano gli stessi aspetti, più o meno nove aspetti diversi che secondo Whitmare sono i più rilevanti per dare una caratterizzazione di un progetto orientato agli oggetti. Vediamo quali sono queste caratteristiche: 1. Dimensioni; 2. Complessità; 3. Accoppiamento; 4. Sufficienza 5. Completezza, è un parametro implicito. Insieme di caratteristiche rispetto a cui vogliamo confrontare l’astrazione o il componente. 42 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari 6. Coesione, aspetto molto importante misurabile legato in maniera inversa all’accoppiamento. Infatti quanto più due parti sono lascamente accoppiate tanto più alta sarà la coesione interna di ciascuno. Se la Classe A ha poche interazioni con la classe B, sicuramente al suo interno è altamente coesa; 7. Primitività, ci dice quanto un’operazione è atomica; 8. Similarità; 9. Volatilità: misura della probabilità che si verifichi una modifica. Di queste caratteristiche quelle più significative sono dimensione, Complessità, accoppiamento e coesione. Vediamo la categoria delle metriche CK, acronimo dei due ideatori. La prima delle metriche di questo test è quella dei metodi pesati per classe (WMC=Weighted Method per Class). La formula corrispondente è la seguente: WMC = Σ ci i=1,...,n Supponiamo di avere n metodi ciascuno di complessità c1…cn , la metrica è ottenuta come sommatoria delle ci al variare di n. Il problema quindi sta nel valutare la complessità del metodo, e per fare ciò si utilizzano altre metriche. La metrica utilizzata è quella della complessità ciclomatica (VG(c)), una misura che ci calcola sul grafo di controllo il numero di link all’interno del grafo. Posso eventualmente utilizzare anche l’insieme dei metodi implementati nella classe(Mim(c)). Il valore del WMC deve essere basso. La seconda misura è il DIT , ossia la profondità dell’albero di ereditarietà, che misura quanto un nodo dista dalla radice dell’albero di ereditarietà. E’ preferibile che la profondità dell’albero sia bassa. La terza misura è il numero di figli (NOC), ossia il numero di sottoclassi figlie di una superclasse, può essere alto ma è sempre preferibile che sia basso. Al crescere di questa misura, cresce anche il numero di casi test da effettuare. La quarta misura è data dalle risposte per classe, ossia il numero di metodi che possono essere eseguiti in risposta ad un messaggio ricevuto da un oggetto della classe. Il suo valore deve essere basso. La quinta misura è l’accoppiamento, ossia il numero di collaborazioni tra classi. Si può avere l’accoppiamento anche tra altre parti software. Più il valore è basso, più la classe è indipendente e quindi riusabile. Sesta misura: LCOM( Lack of Coesion in Methods), ossia mancanza di coesione nei metodi. Questo parametro deve essere basso. La seconda famiglia di metriche ha più o meno metriche che valutano gli stessi parametri, è stata realizzata nel 1998 e sono le metriche MOOD. LE metriche sono le seguenti: 1. MIF, fattore di ereditarietà del metodo. Mi da un’indicazione sulla manutenibilità. La sua formula è la seguente: MIF M (C ) M (C ) i i a i 43 Prof.ssa Mongiello Vinci Federica Ingegneria del software Politecnico di Bari 2. Fattore di accoppiamento : is _ client (C , C ) i CF i j j Tc Tc 2 Deve essere basso . Poi abbiamo le metriche di Laurence e Kidd (1994), basate su quattro grandi categorie: dimensione della classe (CS) in termini di numero di metodi e/o attributi, e il suo valore non deve essere né troppo alto né troppo basso; il NOA,ossia il numero di operazioni aggiunte da una sottoclasse e deve avere comunque un valore contenuto. Ultima categoria di metriche è quella delle metriche sul codice sorgente. In questa categoria si hanno misure del tipo lunghezza del programma, linguaggio, volume ecc ecc… Vedere esempio sulla valutazione di un sistema software dalle slide “TEST” dalla pagina 87 fino alla fine. La tavola di decisione è la tabularizzazione di un albero decisionale. Cosa è un albero? Un albero è un grafo orientato privo di cicli. La seconda definizione è quella che richiama il principio di induzione. Quindi l’albero è una struttura ricorsiva. Nella tavola decisionale, per ogni condizione, specifico come voglio che siano le soglie rispetto al valore atteso. 44 Prof.ssa Mongiello Vinci Federica