Sebbene il software sia intangibile possiamo attribuirgli delle qualità:
collapse: open
- Gli utenti vogliono che il software sia _affidabile_
- Gli sviluppatori vogliono che il software sia _verificabile_
Entrambe le qualità dipendono l'una dall'altra in quanto per ottenere affidabilità il software deve poter essere verificato.
Il prodotto dell’ingegneria del software è il sistema consegnato al committente (software, manuali,…). Tale prodotto viene realizzato mediante un processo mediante il quale vengono creati diversi prodotti intermedi che devono però rispettare gli stessi requisiti del prodotto finale.
Un programma è corretto se si comporta secondo le specifiche funzionali date:
Un software è affidabile quanto è alta la probabilità che funzioni come atteso entro un certo intervallo di tempo. I software [[#Correttezza | corretti]] sono anche affidabili |
Un programma è robusto se si comporta in modo accettabile anche in circostanze non previste nella specifica dei requisiti.
collapse: open
Input non corretto o fallimenti hardware
Un programma [[#Correttezza | corretto]] può non essere robusto. |
title: Legge di Postel
Sii conservatore in ciò che fai, sii liberale in ciò che riguardare 01-03-2023
Le prestazioni di un software dipendono dall’efficienza con cui il software utilizza le risorse interne del computer (memoria, potenza di calcolo, rete). Le prestazioni possono essere valutate mediante misure, analisi e simulazioni.
Le prestazioni influenzano l’usabilità e la scalabilità di un software
Un software è usabile quando gli utenti lo reputano semplice da utilizzare (user friendly). L’interfaccia grafica influisce molto sull’esperienza che l’utente percepisce nell’usare il software.
Un software è verificabile quando è facile verificarne la correttezza e le prestazioni.
La manutenibilità di un software indica la facilità con cui tale software può evolvere, al fine di rimuovere errori, implementare nuove funzionalità, migliorare funzionalità esistenti, ecc..
è un software che è presente in un’organizzazione da più tempo e molto spesso utilizza della tecnologia non più usata che è quindi difficile modificare. In questi casi bisogna ricorrere al reverse engineering o reengineering per ricostruire i vari passaggi del suo funzionamento.
La facilità con cui si eliminano i difetti del software
Facilità con cui si possono apportare cambiamenti
Alcuni elementi del software possono essere riutilizzati per dare vita ad altri prodotti differenti. Ci sono dei produttori di software che sono specializzati alla produzione di queste librerie aggiuntive che devono essere altamente riutilizzabili.
Un software è portabile se può essere eseguito in ambienti diversi (sia hardware che software)
Più il software è complesso più è probabile che sia difficilmente comprensibile
title: Legge di Eagleson
qualsiasi codice sorgente che hai scritto e che non è più stato guardato da sei o più mesi potrebbe benissimo essere stato scritto da qualcunaltro.
Un software comprensibile internamente è facile da verificare e mantenere, mentre un software prevedibile esternamente è facile da comprendere per l’utente
Un programma è interoperabile con un altro se può cooperare e coesistere con questo.
Un elaboratore di testo come word può aggiungere immagini o diagrammi di altre applicazioni o anche aprire documenti di openoffice
è la qualità del processo di produzione di che ne indica l’efficienza con cui viene scritto e le sue prestazioni in conseguenza.
Il linguaggio C è molto performante ma allo stesso tempo poco produttivo.
è la capacità di rendere disponibile un prodotto al momento giusto, è necessaria un’attenta pianificazione del processo per poter sviluppare in un tempo sufficiente il software
Un processo di sviluppo del software è visibile, o trasparente, se tutti i suoi passaggi sono documentati in modo chiaro. Un processo visibile consente ai vari attori di avere chiaro lo stato del progetto, potendo così soppesare le loro scelte, lavorando tutti nella stessa direzione.
Il software deve automatizzare una specifica applicazione ed è caratterizzato sulla base della sua area applicativa (ovvero l’area di lavoro su cui deve lavorare)
Devono gestire le informazioni di un’organizzazione, non eseguono calcoli estremamente complessi ma si occupano della gestione, la modifica, l’aggiunta e l’eliminazione di una grande quantità di informazioni.
Molti di questi sistemi informativi hanno un’interfaccia web, e il loro principale lavoro è quello di gestire i dati, quindi alla loro base hanno sempre un database.
Questi sistemi possono essere caratterizzati in base al modo con cui elaborano questi dati:
Devono essere in grado di rispondere a determinati eventi entro un tempo limitato, per fare questo si basano su uno scheduler che si occupa di ordinare le azioni del sistema anche in base alla priorità o al tempo di esecuzione.
Il tempo di risposta di questi sistemi deve quindi essere una qualità caratterizzante, devono essere inoltre affidabili e devono evitare rischi inaccettabili.
I sistemi distribuiti sono composti da più macchine indipendenti o semi-indipendenti che sono collegate attraverso una rete. Devono essere quindi sviluppati per poter essere eseguiti contemporaneamente su più computer e gli strumenti di sviluppo devono supportare queste necessità.
Nei sistemi embedded il software di solito è solo uno dei tanti componenti che si occupa di interfacciarsi soltanto ai componenti del sistema che controlla
Descrivono la logica procedurale e i processi di business, aiutando a esprimere gli aspetti dinamici dei casi d’uso.
Un’attività è un’insieme di più azioni.
I token vengono prodotti e consumati:
![[Pasted image 20230509153129.png | center]] |
Servono a passare parametri tra azioni diverse.
I pin corrispondono ai box dei parametri nel business risorse prodotte e consumate
![[Pasted image 20230509153334.png | center]] |
Forniscono una responsabilità all’esecuzione delle azioni![[Pasted image 20230509153504.png | center]] |
Eventi provenienti da un processo esterno
![[Pasted image 20230509153542.png | center]] |
Un evento con un arco entrante è un timeout
Un evento senza archi entranti è un evento ripetuto
Descrivono la ripetizione della attività su una collezione ![[Pasted image 20230509155412.png | center]] |
Non termina l’intera attività, ma consuma un token
![[Pasted image 20230509163311.png | center]] |
Non si cercano gli errori di compilazione, ma l’assenza di difetti:
Software senza difetti sono impossibili da avere, serve una continua e attenta verifica su ogni aspetto (specialmente i documenti, design, dati di test, ecc), anche le verifiche devono essere verificate e queste verifiche devono essere fatte durante tutto il processo di sviluppo, non solo alla fine.
title: Bridge Design
Un test assicura infinite situazioni corrette, i programmi non hanno un comportamento continuo e quindi verificare la funzione in un punto solo non ci dice niente circa gli altri punti.
$$a = ... / (x+20)$$
Per ogni valore di $x$ va bene, tranne che per $x = -20$
Per molte qualità le informazioni non sono valori binari (si o no) ma sono soggettive e determinate implicitamente
Consiste nello sperimentare il comportamento del prodotto e fare degli esempi con l’obiettivo di trovare degli errori.
I test dovrebbero identificare la presenza di errori, che devono essere localizzati ed eliminati attraverso il debugging. Ogni test deve essere ripetuto per vedere se l’errore è stato effettivamente eliminato.
title: Test case e Test set
- test case: un elemento di D
- test set: un sottoinsieme finito di D (un insieme di test case)
- test set ideale: se P non è corretto allora esiste un elemento in T tale che P è incorretto per quel set
Il test $t$ ha successo se $P(t)$ è corretto, il set di test $T$ ha successo se $P$ è corretto in ogni $t \in T$
C è completo e consistente se identifica un test set ideale e permette alla correttezza di essere provata
Non esiste un algoritmo che genera un test-set che possa provare la correttezza di un programma, non c'è un criterio di costruzione che sia costintente e completo.
Per alcuni programmi andrebbero eseguiti veramente troppi test, per questo si cerca di dividere $D$ in tanti sottodomini $D_i$ dove ogni elemento dovrebbe avere comportamento simile.
Successivamente si seleziona un test per ogni sottodominio, se $D_j \cap D_k \neq 0$ si prende uno degli elementi dell’intersezione per poter ridurre i test.
Ci sono due approcci:
black box o [[12. Test Funzionali | test funzionali]]: partizionano i criteri in base a delle specifiche senza conoscere i dettagli interni. |
Studio analitico delle proprietà, è una tecnica statica e formale.
Un programma è una funzione che mappa D in R.
Queste definizioni non sono standardizzate
Idea generale dei test strutturali:
Il criterio di copertura dei comandi comprende la selezione di un test set tale che ogni comando o statement di $P$ sia eseguito da un qualche test case. Se ogni $D_i$ è l’insieme di dati che esegue il comando $i$ allora bisogna tentare di minimizzare l’insieme dei $D_i$ in modo da avere una partizione.
Il criterio di copertura degli archi_ comprende la selezione di un test set tale che ogni arco o branch del control flow sia attraversato almeno una volta da un qualche test case, bisogna ovviamente minimizzare la dimensione del test set.
Il criterio delle condizioni composte comprende la selezione di un test set tale che tutti i possibili valori costituenti le condizioni composte siano testate almeno una volta.
Considera tutti i possibili modi di rendere vera o falsa una condizione composta, __solo se__ la condizione non è composta corrisponde all' [[#Edge Coverage]].
Se la condizione ha $n$ componenti booleani si possono avere sino a $2^n$ possibili assegnazioni ai componenti
Il criterio di copertura dei cammini comprende la selezione di un test set che attraversa tutti i cammini dal nodo iniziale al nodo finale del diagramma di flusso.
$n$ punti di decisione in sequenza possono dar luogo (non necessariamente) a $2^n$ possibili cammini distinti
un ciclo iterativo while o chiamate ricorsive possono dar luogo a infiniti cammini distinti, si pongono dei limiti di copertura dei possibili path.
Ad esempio nel caso di cicli while i tipici path testati sono almeno tre: 0 cicli, 1 ciclo iterativo, 2 (o più) cicli iterativi.
Il criterio di data flow coverage comprende la selezione di un test set che copre il più possibile i cammini def-use delle variabili
Un processo software è un insieme di attività che porta alla creazione di un prodotto software
Esistono diversi tipi di software ma non esiste un modello che vada bene per tutti, il modello dipende dal tipo di software e dalle richieste e capacità.
Modello iniziale utilizzato quando al lavoro c’era uno sviluppatore singolo, molto improvvisato e consiste semplicemente nel programmare e sistemare il codice mal funzionante.
Oggi è necessaria una pianificazione per cui questo sistema è obsoleto, anche se esistono processi di produzione agili la cui pianificazione avviene continuamente al fine di modificare il processo in caso di cambiamenti.
è un’attività preliminare che viene eseguita per verificare se è fattibile lo sviluppo del progetto, serve anche a trovare soluzioni alternative, a discutere eventuali compromessi e serve a decidere se sviluppare da 0, comprare una base di partenza o abbandonare direttamente il progetto.
Bisogna comprendere gli obiettivi e documentare i requisiti che deve soddisfare, bisogna inoltre specificare le qualità che il software deve rispettare.
Il progetto viene prima progettato a diversi livelli di dettaglio, ![[Pasted image 20230308114722.png|center]]
La produzione del codice e i vari test devono seguire degli standard sulla loro struttura, sui commenti, sui nomi ecc.
title: Definizione
Famiglia di notazioni grafiche, basate su un singolo metamodello, che aiutano a descrivere e progettare sistemi software
title: Meta-modello
Insieme di regole, vincoli e teorie utilizzate per la modellazione di una classe di problemi
title: UML notation guide
Regole attraverso le quali gli elementi di un linguaggio sono assemblati in espressioni come elementi di modellazione, Relazioni e diagrammi.
title: UML semantics
Regole attraverso le quali alle espressioni sintattiche viene attribuito un significato
title: Perché un modeling language?
Perché il linguaggio naturale è troppo astratto e dispersivo, mentre i linguaggi di programmazione sono troppo concreti.
Questo è una via di mezzo
UML (Unified Modeling Language) è un linguaggio di modellazione unificato che si avvale di diagrammi per specificare, visualizzare e documentare modelli di sistemi software.
UML __non__ è un metodo di sviluppo poiché __non definisce__ una metodologia di progetto ma serve per __descrivere e visualizzare un progetto__.
UML viene prevalentemente impiegato nella progettazione di software ad oggetti (OOP) e prevede due tipi (complementari) di rappresentazione di un sistema.
descrive la struttura di un sistema, le parti che lo compongono e relative relazioni;
descrive il comportamento del sistema; come lo stato del sistema si modifica durante il funzionamento.
UML viene utilizzato principalmente come sketch di un progetto, per documentare e descrivere le porzioni di un sistema, aiutando alla progettazione dell’architettura del prodotto.
Il diagramma definisce il sistema (tutto o in parte) prima della stesura del codice
Il diagramma viene costruito a partire dal codice, magari per costruire una documentazione a posteriori.
![[Sketch.png | center]] |
![[Progetto.png | center]] |
Utilizzare UML come progetto richiede un approccio più ingegneristico che permette la formazione di diversi elementi:
Descrive formalmente il sistema per modelli con un elevato grado di dettagli non lasciando nessuna decisione o interpretazione da parte del programmatore.
Approccio più comune rispetto alla descrizione di tutto il sistema in cui i programmatori “progettano” e sviluppano le componenti dei sistemi in autonomamente
CASE (Computer-Aided Software Engineeging)
Genera automaticamente il codice a partire dai diagrammi, è un approccio ottimale ma non fattibile, quindi genera soltanto la struttura di base
Gli use case sono tecniche per individuare i requisiti di un progetto descrivendo interazioni tra il sistema e gli utenti/elementi esterni.
È richiesto lo sviluppo di un’applicazione che permetta la gestione di un semplice blog. In particolare devono essere disponibili almeno tutte le funzionalità base di un blog:
- inserire un nuovo post
- commentarlo.
Queste due operazioni devono essere disponibili _unicamente agli utenti registrati_ all’interno del sistema. La registrazione avviene scegliendo una _username_ e una _password_. La username deve essere _univoca_ all’interno del sistema
Un caso d’uso è un insieme di scenari che hanno in comune uno scopo finale (obiettivo) per un utente, descrivendo così come sono percepite dagli utenti le funzionalità del sistema.
Sequenze di passi che descrivono le interazioni degli attori (utenti e sistema). Possono anche rappresentare una possibilità (scenari alternativi).
title: Scopo
Tutti gli scenari (sia principali che alternativi) condividono uno __scopo__
Il ruolo dell’utente nell’interazione con il sistema e svolgono il caso d’uso per raggiungere l’obiettivo.
Un buon mezzo di individuazione dei casi d’uso consiste nell’individuare la lista degli attori e comprendere i loro obiettivi e come interagiscono con il sistema
Gli use case sono puro testo, e il valore aggiunto sta proprio nel suo contenuto:
Bisogna prima definire il contesto:
Bisogna anche capire a che livello di dettagli spingersi nella realizzazione
Sono una rappresentazione grafica dei casi d’uso, mettono in evidenza attori e servizi del sistema.
Sono rappresentati da un grafo, i cui nodi sono gli attori e gli use case, gli archi invece sono la comunicazione tra questi e i legami/relazioni tra use case diversi.
Il diagramma individua i confini del sistema nello scenario
un caso d’uso incluso esprime una funzionalità comune fra più use case.
Se un caso A include B, vuol dire che ogni volta che si va nel caso A viene eseguito incondizionatamente anche il caso B:
Questo serve ad evitare la ripetizione di azioni e aumenta la possibilità di riutilizzo.
Se un caso B estende un caso A, vuol dire che il caso B aumenta le funzionalità già appartenenti ad A
Non è la stessa cosa dell'ereditarietà nella programmazione a oggetti, quella è la [[#Generalizzazione/Specializzazione (ereditarietà)]]
La condizione determina quando questa estensione deve essere utilizzata, deve essere specificata tramite descrizione e/o commento associate allo use case.
Esiste indipendentemente dagli use case estesi, può estendere più use case base.
Il perimetro del caso d’uso esteso corrisponde di solito alla modifica dello scenario principale o a una post condizione
Gestione di eccezioni ed errori
Sia l’inclusione che l’estensione fattorizzano comportamenti comuni a più use case e aumentano il comportamento dei casi base, con la differenza che con l’estensione l’attore non può eseguire tutto sempre in quanto deve rispettare delle condizioni, che non sono presenti invece con l’inclusione.
Nell’inclusione una stessa funzionalità si ripete in più casi, mentre l’estensione serve a descrivere variazioni di una funzionalità standard.
Serve ad aggiungere o modificare caratteristiche base, si possono generalizzare sia gli attori che gli use case:
I diagrammi di classe mostrano le diverse classi che costituiscono un sistema e come si relazionano una all’altra.
Sono diagrammi statici:
Una classe è il descrittore di un insieme di oggetti o (istanze della classe) che condividono gli stessi attributi, operazioni, metodi, relazioni e comportamento.
Viene rappresentata da un rettangolo contenente il suo nome in una sottosezione rettangolare.
![[Classi.png | center]] |
Il nome è l’unica sottosezione obbligatoria. Facoltativamente, si possono mostrare gli attributi (campi) e le operazioni (metodi) della classe in due altre sottosezioni del rettangolo.
Gli attributi sono mostrati con almeno il loro nome, ma si possono includere anche il loro tipo, il valore iniziale e altre proprietà.
Le operazioni (metodi) sono mostrate con almeno il loro nome, ma si possono includere anche i loro parametri e i tipi restituiti.
Anche loro hanno diverse [[#Visibilità]]
![[Operazioni.png | center]] |
Le azioni che la classe sa eseguire descrivono i suoi aspetti comportamentali e offrono un servizio che può essere richiesto ad ogni istanza della classe.
Diverse classi si possono relazionare tra loro in modi diversi:
[[#Generalizzazione/Specializzazione (ereditarietà) | Generalizzazione (ereditarietà)]] |
[[#Interfacce e realizzazioni | Realizzazione]] |
Permettono di descrivere relazioni logiche tra le classi nel dominio applicativo
Un’associazione tra due classi indica che si può avere un collegamento tra una coppia di oggetti appartenenti alle due classi.
Le associazioni si indicano con:
Un’associazione è rappresentata da una linea che connette le due classi. Nome, ruoli e molteplicità (cardinalità) possono essere inseriti sulla linea nelle vicinanze della classe a cui si riferiscono (oppure possono anche essere omessi).
![[Associazioni.png | center big]] |
![[Molteplicità.png | center]] |
![[Molt2.png | center]] |
Nelle relazioni molti a molti non sempre è possibile assegnare un attributo ad una delle due classi.
collapse: open
- Ogni oggetto Persona può essere dipendente di molti oggetti Azienda.
- Ogni oggetto Azienda può impiegare molti oggetti Persona
- Si assuma che ogni Persona percepisce uno stipendio da ogni Azienda in cui lavora.
Dove collochiamo l’attributo stipendio: nella classe Persona o nella classe Azienda?
Per ogni associazione “impiega” tra un oggetto Persona e un oggetto Azienda, c’è uno specifico stipendio per uno specifico rapporto di lavoro. L’UML consente di modellare questa situazione con una classe associazione.
Una classe associazione è un’associazione che è anche una classe. Oltre a connettere due classi, definisce un insieme di caratteristiche proprie dell’associazione.
Una classe associazione è rappresentata:
![[classi_associazioni.png | center]] |
Il nome di una associazione, se possibile non dovrebbe esprimere una direzione, oppure esprime la direzione/navigazione prevalente
collapse: open
![[navigabilita.png|center]]
Le aggregazioni sono un tipo speciale di associazione nel quale le due classi partecipanti non hanno un rango uguale, ma hanno una relazione di tipo tutto-parte.
Un’aggregazione descrive come la classe che ha il ruolo del tutto è composta di altre classi, che hanno il ruolo di parti.
Le aggregazioni sono rappresentate da un’associazione che mostra un rombo sul lato dell’aggregato
![[aggregazione.png | center big]] |
La composizione è una forma più forte di aggregazione, con la differenza che in una composizione la parte non può esistere al di fuori del tutto:
Graficamente indicata da rombo solido/pieno a lato «classe tutto» diretta verso «classe parte»
![[composizione.png | center big]] |
Ogni parte appartiene ad un unico composito, dunque possono esistere gerarchie di composizione, ma non possono esistere reti di composizione (albero).
l’ereditarietà serve a derivare una nuova classe (sottoclasse) da una classe esistente (superclasse) in modo tale che la sottoclasse:
In UML si usa il termine generalizzazione per indicare che una classe è una superclasse di un’altra classe.
La generalizzazione (specializzazione) è rappresentata da una linea che connette le due classi con una freccia (triangolo vuoto) dalla classe derivata (sottoclasse) alla classe base (superclasse).
In UML è possibile rappresentare l’ereditarietà multipla
![[ereditarieta.png | center big]] |
Una classe che non può essere instanziata, non ha implementazione.
Una dipendenza è una relazione tra due o più elementi del modello, dove un cambiamento ad uno di essi (fornitore) può influenzare o fornire delle informazioni necessarie all’altro (cliente).
title: Definizione
Si ha dipendenza tra due elementi di un diagramma se la modifica alla definizione del primo (server) può cambiare la definizione del secondo (client)
Le dipendenze vanno minimizzate (Loose coupling), perché troppe dipendenze creano confunsione nel diagramma
Il cliente usa alcuni servizi della classe fornitore (come parametro, valore restituito o nella sua implementazione) per implementare il proprio comportamento (invoca metodi).
Le dipendenze sono indicate con frecce tratteggiate dal cliente verso il fornitore
![[dipend.png | center big]] |
![[tipi_dip.png | center]] |
Un’interfaccia è un insieme di funzionalità pubbliche identificate da un nome (quindi soltanto metodi, no attributi).
Una realizzazione è una relazione tra una classe e un’interfaccia; indica che la classe implementa le operazioni dell’interfaccia.
Un’interfaccia è una classe priva di implementazione, una classe realizza un’interfaccia se ne implementa le operazioni
![[interfacce.png | center big]] |
La relazione di realizzazione è visualizzata da una linea tratteggiata con una freccia (triangolo vuoto) che punta dalle realizzazioni all’interfaccia
Sono applicabili alla classe non instanziata, quindi non all’oggetto.
Sottolineati sul diagramma
Estendono la semantica dell’UML, sono formate da costrutto simile + parola chiave
Funzionalità offerte (commenti)
Possono essere calcolate a partire da altri valori, si indicano con “/” prima del nome
Come dice il nome, le readonly non offrono servizi di scrittura e le frozen non possono variare il loro ciclo di vita
Insiemi di valori che non hanno altre proprietà oltre il valore simbolico
Segnaposti (es: genetics in java)
Eseguono e controllano il proprio thread
Rappresenta le istanze comprese di associazioni e valori delle proprietà, è utile per esempi illustrativi e oggetti particolati (Singleton).
Si basano sul comportamento ingresso-uscita che il software presenta nel suo ambiente operativo, le tecniche di progettazione si basano sul ricavare un certo numero di test case e permettono anche di verificare il mancato soddisfacimento di requisiti non funzionali. Sono complementari ai test strutturali
I casi di test devono essere definiti prendendo in considerazione le condizioni che corrispondono a classi di input/output non valide e valide. Ciascun caso di test deve essere rappresentativo di una classe in modo da minimizzare il numero totale di test da effettuare
Le tecniche di test principali per definire i casi di prova sono:
title: Classe di equivalenza
Un sottoinsieme dei dati in input tale che il test di ogni elemento abbia lo stesso risultato dal punto di vista del comportamento ingresso-uscita
La tecnica prevede 2 passi:
A partire dalle specifiche funzionali possono essere identificate diverse classi (valide o non valide)
Se una condizione di ingresso specifica un intervallo di valori ammissibili per un determinato parametro di input si identificano:
Se una condizione di ingresso specifica un numero/quantità di valori ammissibili per un determinato parametro di input si identificano:
Se una condizione di ingresso specifica un insieme di valori ammissibili per un determinato parametro di input si identificano:
A partire dalle classi identificate occorre progettare un numero di casi di test sufficiente a coprire tutte le classi di equivalenza valide, facendo in modo che ciascun caso di test copra il maggior numero possibile di classi valide. Bisogna inoltre creare tanti casi di test quante sono le classi di equivalenza non valide in modo tale che ciascun caso di test copra una ed una sola classe non valida
Le condizioni sui valori estremi sono quelle condizioni che si trovano direttamente su un valore estremo di una classe di equivalenza di ingresso o di uscita, immediatamente al di sopra di esso oppure immediatamente al di sotto
title: Differenze con le classi di equivalenza
- Sono scelti come rappresentativi della classe di equivalenza uno o più valori in un intorno di ciascun estremo
- I casi di test sono progettati considerando anche l’output (classi di equivalenza di uscita)
I criteri per l’identificazione delle classi per estremi sono analoghi ai precedenti.
Per ciascun intervallo di valori ammissibili in ingresso ed in uscita occorre progettare:
Per ciascun numero di valori ammissibili in ingresso ed in uscita progettare
Richiede di:
Per verificare la completa copertura si definisce una matrice di test
Tecniche per la progettazione dei casi di test funzionali input/output
Fasi dell’attività di test
Descrivono la collaborazione di un gruppo di oggetti ( non classi!!!) che devono implementare collettivamente un comportamento solitamente relativo a uno scenario di un caso d’uso
Entità che detengono il flusso del caso d’uso, possono essere oggetti o altri concetti più ampi
Indica in quale momento un partecipante è attivo. Il tempo scorre dall’alto al basso.
Dati e operazioni scambiati tra i partecipanti, sono chiamate a metodi degli oggetti da parte di altri oggetti
![[tipi_messaggi.png | center]] |
Non esiste una tecnica di modellazione standard, ma ci sono 2 rappresentazioni utilizzate in pratica:
Metodo classico ![[dati_classici.png | center]] |
Girini dei dati (data tadpoles) ![[data_tadpoles.png | center]] |
Il chiamante rimane in attesa della risposta ![[messaggi_sincroni.png | center]] |
Il chiamante non rimane in attesa della risposta ![[messaggi_asincroni.png | center]] |
Sono da utilizzare solo se necessario, per chiarezza
![[messaggi_ritorno.png | center]] |
Solitamente è trascurabile, se non lo è si può annotare la durata o etichettare i messaggi aggiungendo una nota.
![[tempo_trasmissione.png | center]] |
Le condizioni sono solitamente limitate a uno scenario con condizioni guardia o un ramo else.
I cicli solitamente sono limitati da iterazioni indicate con * e/o condizioni guardia.
![[frame_interazione.png | center]] |
I diagrammi di sequenza sono ottimi per modellare le collaborazioni tra oggetti, ma non sono adeguati per modellare cicli e condizioni (meglio i diagrammi di attività)
Nel diagramma di SEQUENZA compaiono oggetti con nome (es. X: NomeClasse) o oggetti anonimi (es. :NomeClasse) tutti appartenenti a classi, mentre NON compaiono MAI CLASSI ISOLATE (es. NomeClasse)
Tutte le classi cui appartengono gli oggetti DEVONO essere dichiarate nel diagramma di classe
Tutti i metodi (segnali, messaggi) chiamatisugli oggetti DEVONO essere definiti nella classe dell’oggetto su cui il metodo è chiamato (quello sulla punta della freccia) e NON sulla classe dell’oggetto chiamante (quello da cui parte la freccia)
I diagrammi di collaborazione o anche diagrammi di comunicazione mostrano una particolare sequenza di messaggi scambiata tra un certo numero di oggetti, esattamente come i diagrammi di sequenza, con la differeza che i diagrammi di sequenza seguono l’ordinamento temporale mentre quelli di collaborazione modellano l’organizzazione del flusso di controllo, mostrando collegamenti tra gli oggetti considerando una particolare sequenza di messaggi alla volta.
I diagrammi di Collaborazione sono COMPLEMENTARI ai diagrammi di Sequenza
![[diag_collaborazione.png | center]] |
I diagrammi di stato descrivono il comportamento di singoli oggetti in più use case.
Vengono utilizzati per mostrare il comportamento di un singolo oggetto, per oggetti multipli si utilizzano i diagrammi di sequenza e di attività.
Non vanno scritti per ogni classe ma solo per casi significativi o poco chiari.
![[diag_stato_elem.png | center big]] |
Sono il cambiamento tra uno stato all’altro e presentano i seguenti elementi:
![[transizioni.png | center big]] |
Hanno durata istantanea, e sono associate alle [[#Transizioni]]
![[azioni.png | center big]] |
Hanno durata prolungata e sono associate agli stati
![[attivita.png | center big]] |
C’è un solo stato attivo
esecuzione parallela
Genera due flussi paralleli
Chiede la terminazione di entrambi i flussi
Si generano eventi per sincronizzare processi paralleli
![[sinc_eventi.png | center]] |
![[superstati.png | center]] |
![[Pasted image 20230509151721.png | center]] |