Il 0% ha trovato utile questo documento (0 voti)
7 visualizzazioni21 pagine

Is Riassunto

L'ingegneria del software è una disciplina che si occupa dello sviluppo sistematico di software, con una storia che risale agli anni '50 e una crescente importanza tecnologica ed economica. Essa comprende fasi come analisi, progettazione, realizzazione, testing, consegna e manutenzione, e si basa su principi come modularità, astrazione e rigorosità. La qualità del software è valutata attraverso fattori interni ed esterni, e la modellazione a oggetti è fondamentale per definire requisiti e strutture attraverso classi e oggetti.

Caricato da

idd446050
Copyright
© All Rights Reserved
Per noi i diritti sui contenuti sono una cosa seria. Se sospetti che questo contenuto sia tuo, rivendicalo qui.
Formati disponibili
Scarica in formato PDF, TXT o leggi online su Scribd
Il 0% ha trovato utile questo documento (0 voti)
7 visualizzazioni21 pagine

Is Riassunto

L'ingegneria del software è una disciplina che si occupa dello sviluppo sistematico di software, con una storia che risale agli anni '50 e una crescente importanza tecnologica ed economica. Essa comprende fasi come analisi, progettazione, realizzazione, testing, consegna e manutenzione, e si basa su principi come modularità, astrazione e rigorosità. La qualità del software è valutata attraverso fattori interni ed esterni, e la modellazione a oggetti è fondamentale per definire requisiti e strutture attraverso classi e oggetti.

Caricato da

idd446050
Copyright
© All Rights Reserved
Per noi i diritti sui contenuti sono una cosa seria. Se sospetti che questo contenuto sia tuo, rivendicalo qui.
Formati disponibili
Scarica in formato PDF, TXT o leggi online su Scribd

1

L'ingegneria del software è un campo dell'informatica che si occupa della creazione


sistematica e disciplinata di software, compresi programmi, procedure, documentazione e
dati associati. Questa disciplina si basa sull'applicazione di un approccio tecnico,
tecnologico e gestionale nello sviluppo, funzionamento e manutenzione del software.

La storia dell'ingegneria del software inizia alla fine degli anni '50, quando i computer
divennero più accessibili grazie alla diminuzione dei costi. L'introduzione dei linguaggi di
alto livello semplificò la comunicazione con le macchine, portando alla nascita della
professione di "programmatore". Tuttavia, la separazione tra utente e programmatore
portò a problemi di comprensione delle intenzioni dell'utente nella creazione del software.

Negli anni '60, si tentò di sviluppare grandi sistemi software commerciali, ma ciò portò alla
"crisi del software" a causa delle difficoltà nel coordinare il lavoro di diverse squadre di
programmatori e nel gestire la documentazione. In risposta a questa situazione, durante
una conferenza della NATO nel 1968, fu coniato il termine "ingegneria del software",
sottolineando la necessità di un approccio sistematico all'attività di sviluppo.

Il ciclo di vita del software comprende diverse fasi:

• Analisi: in questa fase si raccolgono e analizzano i requisiti del software, sia


interagendo con i clienti o gli utenti, sia studiando il mercato.
• Progettazione: dopo aver definito i requisiti, si progetta l'architettura e i dettagli del
sistema software.
• Realizzazione: durante questa fase, i singoli moduli del software vengono codificati
e testati, per poi essere integrati in un unico sistema.
• Testing: si effettuano i test per verificare il funzionamento del sistema software, sia
a livello di singoli moduli che come sistema completo.
• Consegna: il software viene consegnato al cliente o reso disponibile sul mercato.
• Manutenzione: dopo la consegna, vengono apportate modifiche e miglioramenti al
software in base alle esigenze e ai feedback degli utenti.
Sebbene il modello "a cascata" descriva un'evoluzione sequenziale delle fasi, nella pratica
possono verificarsi iterazioni e interazioni tra le fasi stesse, adattando il processo alle
esigenze specifiche del progetto. L'ingegneria del software continua a crescere in
importanza sia dal punto di vista tecnologico che economico, avendo un impatto
significativo sulla società.

2
I fattori di qualità del software possono essere suddivisi in due categorie: qualità interne e
qualità esterne. Le qualità esterne sono visibili agli utenti e riguardano il prodotto software
come una "black box", mentre le qualità interne sono focalizzate sugli sviluppatori e
vedono il software come una "white box".

Diversi portatori di interesse (stakeholders) hanno interesse nel sistema software, tra cui
l'utente, il mercato, il cliente, l'organizzazione produttrice, il finanziatore, il legislatore e
altri. Ogni requisito del software dovrebbe essere associato a uno o più portatori di
interesse.
La classificazione delle qualità del software comprende sia la qualità del prodotto che la
qualità del processo. Le qualità interne influenzano quelle esterne, poiché consentono agli
sviluppatori di raggiungere gli obiettivi di qualità esterna. Allo stesso modo, la qualità del
processo, inclusa la competenza e l'esperienza delle persone coinvolte, influisce sulla
qualità del prodotto finale.

I fattori di qualità del software includono:

• Correttezza: il software è corretto se soddisfa i requisiti funzionali specificati.


Questa proprietà è una valutazione matematica che verifica l'equivalenza tra il
software e le specifiche.
• Affidabilità: rappresenta la probabilità che il software funzioni come previsto entro
un determinato periodo di tempo. È una qualità relativa, in quanto tollera
scostamenti accettabili dal comportamento desiderato.
• Disponibilità: indica la probabilità che il software funzioni correttamente in un dato
istante di tempo.
• Robustezza: il software è robusto se mantiene un comportamento accettabile
anche in situazioni impreviste o con input non corretti o non attesi.
• Prestazioni: si riferisce alla capacità del software di soddisfare i requisiti dell'utente
in modo efficiente, utilizzando in modo ottimale le risorse a sua disposizione.
• Usabilità: indica quanto il software sia facile da utilizzare e soddisfi le aspettative
degli utenti. L'interfaccia utente è un fattore cruciale per l'usabilità.
• Verificabilità: il software dovrebbe essere facilmente verificabile per garantire la
sua correttezza.
• Manutenibilità: rappresenta la facilità con cui il software può essere sottoposto a
manutenzione o modifiche, in modo economicamente conveniente.
• Riusabilità: indica la possibilità di riutilizzare parti del software in altri contesti o
progetti.
• Portabilità: il software è portabile se può essere eseguito in diversi ambienti senza
richiedere modifiche significative.
• Comprensibilità: il software dovrebbe essere comprensibile per agevolare la
verifica, il reimpiego e le modifiche.
• Interoperabilità: rappresenta la capacità del software di coesistere e cooperare
con altri sistemi.
• Produttività: misura l'efficienza e le prestazioni del processo di produzione del
software.
• Tempestività: indica la capacità di consegnare il software in modo tempestivo,
rispettando le scadenze.
Per i sistemi critici, vengono anche enfatizzati altri attributi di qualità, come la
dependability, che include la disponibilità, la manutenibilità, la sicurezza e l’integrità.

3
I principi dell'ingegneria del software includono:

• Rigore: L'ingegneria del software richiede un approccio rigoroso e sistematico per


garantire la produzione di prodotti affidabili e controllare i costi. L'uso di metodologie
e tecniche formali può aiutare a raggiungere questo obiettivo.
• Separazione degli interessi: Per affrontare la complessità del software, è
necessario separare e affrontare separatamente i diversi aspetti del problema.
Questo principio promuove la distinzione di responsabilità e la suddivisione del
lavoro in assegnamenti specifici.
• Differimento delle decisioni: Le decisioni riguardanti il software dovrebbero
essere prese al momento giusto, senza anticiparle prima che sia effettivamente
necessario. Questo consente di prendere decisioni informate e ben ponderate.
• Astrazione: L'uso di astrazioni permette di identificare gli aspetti fondamentali di un
problema, ignorando i dettagli secondari. I modelli astratti aiutano a comprendere e
manipolare il software in modo più semplice ed efficace.
• Modularità: La suddivisione del software in moduli aiuta a organizzarlo e semplifica
il suo sviluppo. Questo principio favorisce la riusabilità delle parti del progetto e la
capacità di evoluzione del software.
• Anticipazione del cambiamento: L'ingegneria del software deve considerare la
capacità del software di adattarsi ai cambiamenti futuri. I progettisti devono
prevedere e pianificare i cambiamenti in modo che possano essere gestiti in
maniera agevole.
Questi principi, se applicati in modo adeguato, possono guidare il processo di sviluppo del
software e contribuire a garantire la qualità del prodotto finale. Tuttavia, è importante
notare che essi da soli non sono sufficienti e devono essere accompagnati da metodi,
tecniche e strumenti specifici per ottenere risultati ottimali.

4
La modularità nell'ingegneria del software è il processo di scomposizione del sistema in
parti più piccole e ben definite. Questo viene fatto per dominare la complessità del sistema
e affrontare in modo separato gli aspetti diversi del problema. La modularità ci permette di
concentrarci su ciascuna parte del sistema in maniera indipendente dalle altre,
semplificando così il processo di sviluppo.

Ci sono problemi complessi con soluzioni semplici e problemi semplici con soluzioni
complesse o addirittura senza soluzione. Prima di affrontare la complessità, occorre
comprendere il concetto di calcolabilità, che riguarda la possibilità di risolvere un problema
tramite un algoritmo realizzabile con una macchina di Turing.

Nell'ambito della modularità, vengono utilizzati concetti come algoritmi, problemi calcolabili
e non calcolabili e la codifica dei programmi. Gli algoritmi possono essere implementati
con linguaggi di programmazione, e la codifica dei programmi rappresenta il modo in cui
descriviamo la soluzione a un problema.

La modularità ci consente di realizzare componenti software chiamati moduli, che possono


realizzare diverse astrazioni, come funzionalità, servizi, gestione di risorse o
rappresentazione di classi di oggetti. Esistono due tipi principali di astrazione: astrazione
sul controllo, supportata dai sottoprogrammi, e astrazione sui dati, supportata dai tipi di
dati astratti o dalle classi.

Nell'astrazione sul controllo, i sottoprogrammi affrontano specifici problemi


indipendentemente dal loro utilizzo, consentendo una maggiore modularità e nascondendo
i dettagli implementativi. L'astrazione sui dati, d'altra parte, permette di astrarre le entità
costituenti il sistema, definendole in termini di una struttura dati e delle operazioni possibili
su di esse.
Un oggetto rappresenta una struttura dati incapsulata nelle operazioni che possono essere
eseguite su di essa. Le classi, i tipi di dato astratto generico e i template sono modi per
realizzare la genericità e la tipizzazione nella programmazione orientata agli oggetti.

La coesione e l'accoppiamento sono due concetti importanti nella progettazione modulare.


La coesione misura l'omogeneità dei componenti all'interno di un modulo, mentre
l'accoppiamento indica l'interdipendenza tra i moduli. Un alto livello di coesione e un basso
livello di accoppiamento sono obiettivi desiderabili nella progettazione del software.

Esistono due approcci principali per la progettazione modulare: il top-down e il bottom-up.


Nel top-down, si procede dall'alto verso il basso, focalizzandosi prima sulle astrazioni
funzionali o sui dati e poi assemblando i moduli. Nel bottom-up, si parte dai moduli base e
si costruiscono moduli più complessi assemblando le parti individuali. La scelta tra i due
approcci dipende dalla natura del problema e dalle esigenze di progettazione.

5
La modellazione a oggetti è una fase di analisi nel processo di ingegneria del software, in
cui l'input è una descrizione informale del problema e l'output sono i requisiti scritti in
maniera formale, utilizzando il linguaggio UML. Durante questa fase, si distinguono i
requisiti sui dati, i requisiti funzionali e quelli non funzionali (come prestazioni, sicurezza,
affidabilità e sicurezza).

I requisiti sui dati permettono di individuare le entità che fanno parte del dominio del
problema, concentrandosi su ciò che sono indipendentemente dalle operazioni possibili su
di esse. I requisiti funzionali, invece, definiscono le funzionalità del software da realizzare,
coinvolgendo più entità.

La modellazione a oggetti si basa su quattro concetti fondamentali della programmazione


orientata agli oggetti:

• Classe: Rappresenta una struttura astratta che definisce le proprietà e le operazioni


comuni agli oggetti che ne fanno parte.
• Oggetto: È un'istanza di una classe, rappresentante un'entità specifica nel sistema
software.
• Ereditarietà: È una relazione tra classi che consente di creare una gerarchia di
classi basata su relazioni di generalizzazione/specializzazione. Una classe figlia
eredita le proprietà e le operazioni della classe genitore.
• Polimorfismo: È la proprietà per cui un'entità può assumere forme diverse nel
tempo, consentendo di gestire oggetti di classi diverse in modo uniforme. Il
polimorfismo può essere statico (overloading dei nomi di funzioni) o dinamico,
realizzato tramite late binding.
La programmazione orientata agli oggetti differisce dalla programmazione mediante tipi di
dati astratti (typedef, struct) nel fatto che questa permette solo una disciplina di
programmazione, mentre con l'uso delle classi si ha il controllo da parte del compilatore
sulle operazioni consentite sugli oggetti, riducendo errori a tempo di esecuzione. Questo
rende i linguaggi tipizzati e compilati più adatti all'ingegneria del software rispetto a quelli
non tipizzati e interpretati.
L'ereditarietà è un concetto fondamentale nella modellazione a oggetti in quanto consente
di organizzare il sistema software in una struttura gerarchica, facilitando il riutilizzo del
codice. Il polimorfismo, invece, permette di scrivere codice che può essere applicato in
modo flessibile a oggetti di diverse classi.

In C++, un puntatore alla classe base può puntare legittimamente a un oggetto della
classe derivata, ma non viceversa. Il late binding, che permette il polimorfismo dinamico,
avviene tramite un puntatore all'oggetto che punta alla tabella dei metodi virtuali della
classe, consentendo una ricerca tabellare della funzione da invocare a tempo di
esecuzione.

6
Il diagramma dei casi d'uso in UML viene utilizzato per modellare le funzionalità del
sistema software. Ogni caso d'uso rappresenta un'interazione tipica tra un attore e il
sistema, al fine di svolgere un'unità di lavoro utile. Gli attori possono essere persone
fisiche, altri sistemi software, dispositivi hardware esterni o il tempo stesso.

Il diagramma dei casi d'uso è composto da quattro elementi principali:

• Attori: Sono le entità esterne al sistema che interagiscono direttamente con esso.
• Casi d'uso: Rappresentano le funzionalità del sistema, descritte da ovali con una
breve stringa che le identifica.
• Relazioni tra attori e casi d'uso: Collegano gli attori con i casi d'uso che essi
innestano.
• Confini del sistema: Definiscono cosa fa parte del sistema (all'interno dei confini) e
cosa ne è escluso (fuori dai confini).
Le relazioni tra attori e casi d'uso possono essere di diversi tipi:

• Relazioni di generalizzazione tra attori o casi d'uso, che permettono di astrarre ruoli
comuni e semplificare i diagrammi.
• Relazioni di inclusione, dove più casi d'uso racchiudono una sequenza di passi
comuni, evitando di specificarli ripetutamente.
• Relazioni d'estensione, che descrivono sequenze opzionali aggiunte al caso d'uso
in determinate circostanze.
Ogni caso d'uso è documentato con una scheda che descrive la sequenza di eventi
principali, le precondizioni, le postcondizioni e le sequenze di eventi alternativi. Gli scenari
rappresentano diversi percorsi di esecuzione del caso d'uso, incluso lo scenario principale
(senza errori o deviazioni) e, se necessario, scenari secondari per situazioni in cui
qualcosa non procede come previsto.

I diagrammi dei casi d'uso sono utili per la comunicazione tra analisti, progettisti,
committenti e altri stakeholders, poiché offrono una rappresentazione grafica delle
funzionalità del sistema, facilmente comprensibile anche per le persone non esperte.
Inoltre, aiutano nella validazione dei requisiti, permettendo di confrontarli con le aspettative
degli stakeholders e correggere eventuali errori fin dalle fasi iniziali dello sviluppo del
software.
7
Il diagramma delle classi viene utilizzato per modellare le entità e le relazioni tra di esse in
un sistema software. Durante l'analisi, il focus è sul dominio del problema, mentre durante
la progettazione, si modellano le entità del sistema. Il diagramma delle classi è uno
strumento statico che offre una vista concettuale delle entità e delle loro caratteristiche,
senza descrivere come verranno trattate o implementate.

Le classi sono rappresentate attraverso rettangoli con il nome nella parte superiore, le
proprietà (attributi) nel centro e le responsabilità (operazioni) nella parte inferiore. I legami
tra le classi possono essere di tre tipi: generalizzazione/specializzazione (è-un), tutto/parti
(o contenimento) e associazione.

Le associazioni rappresentano corrispondenze tra classi e possono essere navigabili in


varie direzioni. Esistono diversi idiomi per rappresentare le associazioni, da quello più
formale e pesante a quelli meno formali e più leggeri.

Nella fase di analisi, i legami rappresentano i fatti della realtà e non hanno direzionalità,
mentre in fase di progettazione, possono essere introdotte direzionalità per riflettere
requisiti funzionali specifici.

Le classi boundary e control sono stereotipi che indicano il confine tra l'utente e il sistema.
Le classi boundary rappresentano le interfacce tra gli attori primari e i casi d'uso, mentre le
classi control gestiscono le operazioni e le interazioni tra le entità. In alcuni casi, una
classe boundary può comunicare direttamente con le entità, mentre in situazioni più
complesse, può interagire con una classe control che gestisce le operazioni più complesse
coinvolgendo diverse classi.

Il diagramma delle classi aiuta a garantire la consistenza tra il modello delle classi e il
diagramma dei casi d'uso, poiché le operazioni delle classi boundary corrispondono ai casi
d'uso attivati dagli attori primari.

Inoltre, durante la programmazione, il diagramma delle classi può essere utilizzato come
riferimento per la codifica delle classi in linguaggio di programmazione, consentendo di
tradurre il modello in codice in modo più immediato.

In sintesi, il diagramma delle classi offre una visione statica delle entità e delle loro
relazioni nel sistema, fornendo una base solida per il processo di progettazione e sviluppo
del software.

8
Il testo fornisce una panoramica sull'importanza e il processo dell'ingegneria dei requisiti
nel ciclo di vita del software. Ecco un riassunto dei punti chiave:

L'ingegneria dei requisiti è fondamentale per lo sviluppo di software, poiché si occupa di


raccogliere, analizzare, documentare e gestire i requisiti del sistema.

I requisiti possono essere distinti in requisiti utente (che riguardano le funzionalità e i


servizi richiesti) e requisiti di sistema (una descrizione più dettagliata delle funzionalità).
I requisiti funzionali descrivono cosa il software deve fare e possono essere modellati
utilizzando diagrammi dei casi d'uso e scenari.

I requisiti non funzionali impongono vincoli e riguardano aspetti come prestazioni,


affidabilità, sicurezza e usabilità.

La fase di ingegneria dei requisiti comprende diverse attività, come l'esplicitazione dei
requisiti, l'analisi e la negoziazione, la documentazione e la validazione.

I requisiti devono essere chiari, non ambigui, testabili, completi e coerenti per evitare
problemi nelle fasi successive dello sviluppo.

Un difetto (fault) può portare a un errore (error), che a sua volta può generare una failure,
ovvero un comportamento inadeguato del sistema.

Il documento di specifica dei requisiti (SRS) è fondamentale e costituisce il punto di


riferimento per il progetto del software.

La tracciabilità dei requisiti è cruciale per gestire i cambiamenti e gli impatti sulle attività e
sul progetto.

L'ingegneria dei requisiti continua a essere importante in tutte le fasi dello sviluppo del
software, fino al collaudo e al testing.

Garantire una buona modularità è essenziale per gestire i cambiamenti nei requisiti in
modo efficiente.

L'efficienza del sistema informatico è legata al suo rendimento, che rappresenta il rapporto
tra il lavoro utile e il lavoro totale.

L'usabilità è un attributo non funzionale importante, ma spesso viene definito in modo


vago e poco testabile.

In generale, un'adeguata ingegneria dei requisiti riduce i costi, migliora la qualità del
software e soddisfa meglio le esigenze degli utenti.

9
I diagrammi di interazione sono uno dei tipi di diagrammi UML utilizzati per aggiungere la
dinamica comportamentale agli oggetti individuati nel modello delle classi. Questi
diagrammi descrivono gli scenari dei casi d'uso attraverso le interazioni tra le entità, che
sono rappresentate come istanze delle classi nel diagramma delle classi.

I principali diagrammi di interazione sono i diagrammi di sequenza e i diagrammi di


comunicazione (precedentemente noti come diagrammi di collaborazione). Entrambi
offrono una vista dinamica del sistema software, ma differiscono nella loro disposizione
spaziale e nell'enfasi sui dettagli temporali e strutturali delle interazioni.
Nel diagramma di sequenza, le interazioni sono rappresentate con linee di vita verticali
che corrispondono ai partecipanti o agli oggetti coinvolti. Il tempo scorre verso il basso
lungo l'asse verticale, e i messaggi tra gli oggetti sono rappresentati da frecce che
connettono le linee di vita. I rettangoli di attivazione indicano il periodo in cui l'oggetto è
attivo durante l'elaborazione di un messaggio. Questi diagrammi mettono in evidenza
l'ordinamento temporale degli eventi e sono particolarmente utili quando si vuole
dettagliare un modello dell'interazione partendo dai casi d'uso.

Nei diagrammi di comunicazione, invece, le linee di vita degli oggetti sono disposte
orizzontalmente, e le interazioni tra gli oggetti sono rappresentate da frecce con numeri di
sequenza che indicano l'ordine temporale degli eventi. Questi diagrammi enfatizzano gli
effetti sugli oggetti coinvolti e rendono più espliciti gli scambi di messaggi tra di essi. Sono
spesso utilizzati per validare il diagramma delle classi e comprendere le relazioni strutturali
tra le entità.

Entrambi i tipi di diagrammi di interazione consentono di modellare costrutti di sequenza,


selezione e iterazione. Ad esempio, i frammenti combinati come "opt" (option) e
"alt" (alternative) possono essere utilizzati per esprimere selezioni a due o più vie. I loop
possono essere rappresentati con il simbolo "*", specificando il numero minimo e massimo
di iterazioni insieme a una guardia facoltativa.

L'obiettivo dei diagrammi di interazione è catturare gli scenari dei casi d'uso e definire
come gli oggetti comunicano tra loro per soddisfare i requisiti del sistema. Questa
visualizzazione dinamica aiuta a comprendere il comportamento del software e può
rivelare la necessità di ulteriori classi non ancora specificate nel diagramma delle classi,
portando a eventuali modifiche nel modello.

In generale, i diagrammi di interazione sono una componente essenziale della


modellazione software e supportano lo sviluppo guidato dai modelli (MDD), in cui i modelli
vengono utilizzati per generare automaticamente il codice, promuovendo una
progettazione più strutturata e coerente.

10
I diagrammi di stato sono un tipo di diagrammi UML utilizzati per descrivere il
comportamento dinamico di un sistema o di un oggetto. Introducono il concetto di stato
come una condizione di funzionamento, in cui l'oggetto risponderà con una specifica
uscita in base all'ingresso ricevuto e allo stato in cui si trova.

Esistono tre tipi fondamentali di macchine a stati finite: automi di Mealy, automi di Moore e
automi a stati finiti non deterministici. Gli automi di Mealy hanno un'uscita che dipende sia
dallo stato corrente sia dall'ingresso ricevuto, mentre gli automi di Moore hanno un'uscita
che dipende solo dallo stato corrente. Entrambi sono considerati automi deterministici e
sono equivalenti, in quanto è possibile trasformare un automa di Mealy in uno di Moore
che produce lo stesso risultato terminale.

Un diagramma di stato è definito dalla sestupla, che include un insieme finito e non vuoto
di stati, un insieme di ingressi, un insieme di uscite, una funzione di trasformazione per
passare da uno stato all'altro, una funzione di uscita e uno stato iniziale.
Questi diagrammi possono essere rappresentati graficamente tramite un grafo orientato,
noto come diagramma degli stati, o in forma tabellare. Gli stati sono rappresentati come
rettangoli smussati e le transizioni come frecce. È possibile etichettare le transizioni con
eventi, azioni e condizioni di guardia per specificare i comportamenti delle transizioni.

Gli eventi possono essere di diversi tipi, come eventi di chiamata, eventi di segnale, eventi
di variazione e eventi temporali. Le azioni possono essere istantanee o richiedere un
intervallo di tempo finito e possono essere associate a transizioni interne o agli eventi
speciali di ingresso e uscita.

I diagrammi di stato possono includere stati composti, che consentono di modellare


sottostati concorrenti o mutuamente esclusivi. Inoltre, possono essere utilizzati per
modellare il ciclo di vita di un'entità reattiva, come una classe, e per definire il protocollo di
funzionamento di un oggetto, specificando l'ordine corretto di invocazione delle operazioni
consentite sull'oggetto.

Questi diagrammi sono strumenti potenti per la modellazione del comportamento dinamico
di un sistema e aiutano a specificare in modo preciso le regole di utilizzo dell'oggetto,
fornendo anche una base per la generazione dei casi di test e per la validazione del
comportamento del sistema.

11

I diagrammi di attività sono strumenti di modellazione UML utilizzati per descrivere il flusso
di lavoro e i processi, in particolare i processi di business. Consistono in un insieme di
attività connesse da transizioni che definiscono il passaggio da una fase all'altra del
processo.

Nelle attività, i nodi azione rappresentano unità di lavoro atomiche, istantanee e non
interrompibili. Altri nodi, come decisioni, fusioni, biforcazioni e ricongiunzioni, consentono
di modellare il flusso del processo. Le decisioni permettono di specificare precorsi
alternativi basati su condizioni di guardia, mentre le biforcazioni consentono di modellare
flussi concorrenti o attività parallele.

Il diagramma di attività può essere utilizzato per esprimere la sequenza, le attività


concorrenti, l'iterazione e la selezione, rendendolo adatto anche per descrivere algoritmi.
Può essere utile per comprendere quali processi possono essere automatizzati e quali no.

Per facilitare la lettura e la comprensione, le attività possono essere partizionate in corsie


(swimlanes), che mostrano chi è responsabile di ciascuna attività. Queste corsie possono
rappresentare unità organizzative, componenti fisici del sistema, ruoli o distribuzioni
temporali, a discrezione del modellatore.

Il diagramma di attività può anche includere nodi oggetti, che rappresentano istanze delle
entità coinvolte nel processo. I flussi di oggetti descrivono il passaggio di oggetti tra le
attività, consentendo di specificare l'output di un'attività e il suo impatto sullo stato degli
oggetti coinvolti. In questo modo, è possibile mantenere coerenza con il diagramma degli
stati, descrivendo le azioni che modificano lo stato di un oggetto.

12
Il diagramma di deployment, o di allocazione, è uno strumento di modellazione UML
utilizzato per descrivere come i componenti software (sottosistemi, driver, etc.) vengono
allocati sulle unità fisiche di elaborazione, sia hardware che software. Non è necessario
modellare ogni dettaglio del sistema, ma solo gli elementi architetturalmente significativi.

Il diagramma comprende nodi, che rappresentano tipi di risorse computazionali, come


dispositivi fisici (es. PC, server) o ambienti di esecuzione software (es. Java Virtual
Machine). Le associazioni tra i nodi rappresentano canali di comunicazione attraverso cui
possono passare informazioni.

Un'istanza di nodo rappresenta un hardware specifico e identificabile, mentre un'istanza di


manufatto è un'istanza specifica di un tipo software. Il processo di assegnazione di
manufatti a nodi, o di istanze di manufatti a istanze di nodi, è chiamato deployment ed è la
messa in esercizio del sistema.

Il diagramma di deployment può essere nella forma di descrittore, contenente nodi e


relazioni tra nodi e manufatti, che è utile per decidere l'architettura hardware del sistema.
Oppure può essere nella forma di istanza, contenente istanze di nodo, relazioni tra istanze
di nodo e istanze di manufatti, che permette di mostrare la configurazione del sistema a
runtime e dove il software sarà effettivamente eseguito.

Tramite il diagramma di deployment, è possibile specificare quali componenti software


vengono eseguiti su quali unità fisiche e fornire informazioni dettagliate per l'installazione
del software.

13
La progettazione è una fase chiave nello sviluppo del software, poiché si tratta di una
transizione dalla specifica dei requisiti all'implementazione effettiva del sistema. L'obiettivo
principale della progettazione è definire l'architettura del sistema software, cercando di
risolvere il problema specificato durante l'analisi dei requisiti.

Durante la progettazione, il progettista deve affrontare diverse sfide e prendere decisioni


riguardo a diverse alternative di design. La progettazione può essere suddivisa in due fasi:
progettazione ad alto livello (HLD) e progettazione a basso livello (LLD).

La progettazione ad alto livello è indipendente dalle scelte tecnologiche e definisce


l'architettura del sistema, evidenziando le parti del sistema e le responsabilità assegnate a
ciascuna di esse. Si effettua l'allocazione dei requisiti alle componenti del sistema,
specificando quali moduli sono responsabili di una determinata funzionalità.

La progettazione a basso livello, invece, affronta le scelte tecnologiche, come il linguaggio


di programmazione o l'uso di specifiche librerie. In questa fase, si approfondiscono i
dettagli di implementazione del sistema.
Un principio fondamentale in fase di progettazione è il "design for change", che implica
prevedere e progettare il sistema in modo da poter affrontare cambiamenti futuri in modo
efficiente e ridurre i costi di manutenzione.

Il progettista deve scomporre il sistema in moduli con alta coesione e basso


accoppiamento. La coesione riguarda il raggruppamento di funzionalità simili nello stesso
modulo, mentre l'accoppiamento si riferisce alla dipendenza tra moduli. L'obiettivo è
trovare un punto ottimale in cui i costi di realizzazione dei moduli siano minimi, ma anche i
costi di interazione tra di essi.

Un altro principio importante è la separazione degli interessi, che consiste nell'organizzare


il sistema in modo tale che le parti siano indipendenti tra loro e possano essere accessibili
da diverse logiche di presentazione o modalità di utilizzo.

Durante la progettazione, il progettista produce diversi artefatti, come la definizione


dell'architettura del software, le interfacce tra i componenti, le interfacce uomo-macchina
(HMI), l'accesso ai dati e gli algoritmi. La documentazione delle interfacce dei componenti
è un elemento cruciale per garantire una chiara comunicazione tra i membri del team di
sviluppo e facilitare l'integrazione dei diversi moduli del sistema.

14
I diagrammi dei package sono strumenti importanti nella fase di progettazione del
software. Essi permettono di organizzare logicamente gli elementi del sistema, inclusi altri
package e diagrammi, all'interno di uno spazio di nomi incapsulato. Un package viene
rappresentato graficamente come una cartella.

I package consentono di creare un modello ben strutturato e navigabile, raggruppando


elementi che hanno una forte correlazione semantica. Ad esempio, le classi possono
essere organizzate in diversi package, e per ciascuno di essi, può essere fornito un
diagramma delle classi che contiene solo le classi del package, rendendo il modello più
leggibile.

Un package può contenere altri package, creando una gerarchia di package annidati. I
package annidati vedono tutti gli elementi pubblici del package ospite, ma il viceversa non
è vero, a meno che non vi sia una relazione di accesso tra i package.

Misuriamo la coesione interna di un package utilizzando la metrica CCRC (Class Category


Relational Cohesion), che calcola il rapporto tra il numero di link tra package e il numero di
classi contenute. La coesione interna di un package dovrebbe rientrare nel range
nominale tra 150% e 350%.

Tra i package, possiamo avere diverse tipologie di dipendenze:

• Dipendenza d'uso: indica che il cliente utilizza alcuni servizi forniti dal package.
• Dipendenza di astrazione: modella dipendenze tra elementi del modello UML a
diversi livelli di astrazione, come tra analisi e progettazione.
• Dipendenza di permesso: concerne la capacità di un elemento del modello di
accedere a un altro elemento, come le dipendenze di import e access.
Le relazioni di import e access tra package possono influenzare l'analisi di impatto per
eventuali manutenzioni, poiché un cambiamento in un package importato può avere
ripercussioni su altri package. La dipendenza di import è transitiva, mentre la dipendenza
di access non lo è. Questi aspetti sono importanti per comprendere l'impatto dei
cambiamenti durante la fase di manutenzione del software.

15
La progettazione di alto livello si basa sulla progettazione dei componenti e delle loro
interconnessioni. Per garantire una progettazione accurata, si definiscono le interfacce,
che rappresentano un insieme di funzionalità denominate da un nome. Un'interfaccia è
simile a una classe, ma non contiene attributi, solo operazioni. L'obiettivo è separare le
specifiche delle funzionalità dalla loro implementazione. Le interfacce servono come
contratti tra chi le implementa e chi le utilizza.

Per rappresentare le interfacce in UML, si utilizzano due notazioni: il simbolo della classe
con lo stereotipo <<interface>> o la notazione a palloncino (lollipop). Le interfacce sono
collegate alle classi che le implementano attraverso una relazione di realizzazione.

Nella progettazione per componenti, si definiscono i moduli del sistema e le interfacce che
offrono e richiedono servizi. Queste interfacce vengono accoppiate attraverso connettori di
assemblaggio. Le interfacce possono essere raggruppate in porte, che rappresentano
insiemi semanticamente coesi di interfacce.

Un componente è un'unità a livello architetturale con una struttura interna, composta da


parti che a loro volta possono essere connesse internamente. Un componente si comporta
come una black-box, il cui comportamento esterno è definito dalle interfacce fornite e
richieste. I componenti sono sostituibili nel loro ambiente e possono essere istanziati a
tempo di esecuzione o assemblaggio.

Il diagramma dei componenti illustra la struttura interna di ciascun componente e le


interfacce che offre e richiede. Ogni parte di un componente ha un ruolo specifico e
responsabilità definite.

UML prevede anche i sottosistemi, che sono parti di un sistema che a loro volta formano
un sistema più piccolo. Un sottosistema è un'unità di scomposizione per un sistema più
grande e può essere istanziato in virtù delle parti che lo compongono.

La Component-Based Software Engineering (CBSE) è l'ingegneria dei componenti, che


richiede la progettazione per interfacce e una visione d'insieme dell'architettura del
sistema. Inoltre, l'uso di librerie dinamiche e caricamento dinamico delle classi permette di
caricare componenti solo quando necessario, consentendo maggiore flessibilità e
dinamicità nel sistema.

16
I pattern architetturali sono soluzioni predefinite per problemi ricorrenti nello sviluppo del
software, proposti da Christopher Alexander. Esistono diversi tipi di pattern, e tra questi, i
pattern architetturali e i design pattern.

• Pattern Architetturali:
• I pattern architetturali sono ampi schemi strutturali che definiscono l'organizzazione
e la distribuzione del software. Essi facilitano il riuso e la comprensione delle
componenti, fornendo un vocabolario comune. Tra i principali pattern architetturali vi
sono:
• Model-View-Controller (MVC): Un archetipo ampiamente utilizzato, in particolare
nelle tecnologie Java. Divide l'applicazione in tre componenti: Model
(rappresentazione dei dati), View (presentazione) e Controller (logica di gestione
degli eventi). MVC consente di separare i dati dalla presentazione, migliorando la
manutenibilità e la flessibilità dell'applicazione.
• Boundary-Control-Entity (BCE): Utilizzato in contesti object-oriented, separa
l'applicazione in tre gruppi di classi o package: Boundary (interfacciamento con
l'utente), Control (logica di business) e Entity (rappresentazione dei dati). Questo
pattern aiuta a assegnare responsabilità diverse agli oggetti e facilita la gestione dei
dati.
• Broker: Implementa un meccanismo di comunicazione publish/subscribe tra client e
server, disaccoppiando i due e consentendo un'interazione indiretta tramite un
intermediario (il broker).
• Pipe & Filter: Organizza le applicazioni come una catena di processi, in cui i dati
fluiscono attraverso filtri consecutivi, ognuno responsabile di un'operazione
specifica. Questo pattern promuove la modularità e la riusabilità delle componenti.
• Client/Server: Definisce un modello di interazione tra client e server, in cui il server
offre servizi passivamente e il client richiede attivamente tali servizi. Questo pattern
è comunemente utilizzato in applicazioni distribuite.
• Design Pattern:
• I design pattern sono soluzioni specifiche per problemi di progettazione di dettaglio
e sono spesso implementati utilizzando il paradigma di programmazione orientato
agli oggetti. Tra i design pattern più noti vi sono:
• Singleton: Garantisce che una classe abbia una sola istanza e fornisce un punto di
accesso globale a tale istanza.
• Factory Method: Definisce un'interfaccia per la creazione di oggetti, consentendo
alle sottoclassi di decidere quale classe istanziare.
• Observer: Consente a un oggetto (observable) di notificare automaticamente i suoi
osservatori quando cambia il suo stato.
• Strategy: Consente di definire una famiglia di algoritmi e renderli intercambiabili
durante l'esecuzione.
• Decorator: Aggiunge dinamicamente funzionalità aggiuntive a un oggetto senza
modificare la sua struttura di base.
Questi pattern aiutano a scrivere codice pulito, mantenibile e flessibile, consentendo la
risoluzione efficiente di problemi comuni durante lo sviluppo del software.

17
Il testo presenta cinque pattern di design: Singleton, Façade, Adapter, Composite e
Observer.

• Singleton:
• Il pattern Singleton è un pattern creazionale che assicura l'esistenza di una sola
istanza di una classe. Viene utilizzato quando è necessario avere un unico oggetto
di una determinata classe nel sistema. Il pattern impedisce la creazione di istanze
aggiuntive della classe e fornisce un metodo statico per accedere all'unica istanza
esistente.
• Façade:
• Il pattern Façade è un pattern strutturale che semplifica l'interazione con un
sottosistema complesso fornendo un'interfaccia unificata e di alto livello. Questo
permette al client di accedere a funzionalità più astratte senza dover conoscere i
dettagli interni del sottosistema. Il Façade nasconde la complessità del sistema,
promuovendo un basso accoppiamento tra client e sottosistema.
• Adapter:
• Il pattern Adapter è un altro pattern strutturale utilizzato per convertire l'interfaccia di
una classe esistente in un'interfaccia desiderata dal client. Viene impiegato quando
si desidera utilizzare una classe esistente che non corrisponde direttamente alle
esigenze del client. L'Adapter agisce come un ponte tra il client e l'oggetto adattato,
consentendo loro di collaborare in modo compatibile.
• Composite:
• Il pattern Composite è un pattern strutturale che permette di trattare un insieme di
oggetti simili come un'unica entità. Un oggetto Composite può essere composto da
altri oggetti, chiamati Componenti, che possono essere sia foglie (Leaf) che altri
oggetti composti. Il pattern consente di manipolare gli oggetti composti in modo
uniforme come gli oggetti individuali, facilitando la gestione di strutture ad albero
complesse.
• Observer:
• Il pattern Observer è un pattern comportamentale che stabilisce una dipendenza
"uno a molti" tra oggetti. Quando un oggetto, chiamato Subject, cambia stato,
notifica automaticamente tutti gli oggetti dipendenti, chiamati Observer,
consentendo loro di essere aggiornati con il nuovo stato. Il pattern è utile per
implementare il modello publish/subscribe e permette una comunicazione asincrona
tra i soggetti e gli osservatori.
Ogni pattern di design è pensato per risolvere specifici problemi di progettazione del
software e promuovere principi come la riusabilità del codice, la separazione delle
responsabilità e la facilità di manutenzione. L'utilizzo appropriato di questi pattern può
migliorare la struttura, la flessibilità e l'efficienza di un sistema software.

17
Il processo di verifica e validazione del software mira a garantire che il software soddisfi le
esigenze dell'utente e rispetti i requisiti specificati durante la fase di analisi e specifica dei
requisiti. La validazione risponde alla domanda "stiamo costruendo il software giusto?"
mentre la verifica risponde a "stiamo costruendo correttamente il software?"

Un software è corretto se rispetta i requisiti specificati, ma ciò non garantisce che soddisfi
le reali esigenze dell'utente e superi la validazione. Il processo di testing è un'attività di
esecuzione del software con lo scopo di scoprire eventuali malfunzionamenti o difetti.
Esso si basa sulla catena "fault-error-failure", dove un difetto nel codice, se attivato,
diventa un errore, e se si manifesta, provoca un malfunzionamento.

Il processo di testing fa emergere i malfunzionamenti, dietro ai quali si trovano difetti che


devono essere rimossi. Per eseguire il testing, il sistema deve essere disponibile ed
eseguibile. Talvolta, è necessario effettuare verifiche statiche prima dell'esecuzione, come
l'analisi statica del codice, che si concentra sulla verifica delle istruzioni del programma
senza eseguirle, individuando violazioni delle regole di programmazione che possono
indicare la presenza di difetti.
Il debugging è il processo di individuazione e correzione dei difetti nel software dopo aver
identificato il malfunzionamento. Esso differisce dal testing, così come il personale
coinvolto.

Un test case è un insieme di input, condizioni di esecuzione e criteri di successo per


stabilire se il test è passato o fallito. Il test case specification è un requisito che deve
essere soddisfatto da uno o più casi di test. Una test suite è un insieme di casi di test.

La test execution è l'esecuzione effettiva di un caso di test dopo l'implementazione.


L'oracolo rappresenta la descrizione del comportamento atteso utilizzata per valutare se il
comportamento osservato coincide con quello atteso.

I criteri di adeguatezza sono predicati che valutano la copertura dei test. Un test è buono
se individua nuovi malfunzionamenti e, di conseguenza, i test case devono essere definiti
con questo obiettivo.

Secondo la tesi di Dijkstra, non è possibile dimostrare l'assenza totale di difetti in un


software, poiché il numero di test necessari sarebbe troppo elevato. I difetti esistenti in un
software esistono, ma non sono stati ancora scoperti.

Il testing è importante poiché dimostra indirettamente che il software rispetta le specifiche


e fornisce informazioni sull'affidabilità e la qualità complessiva del prodotto software.
Tuttavia, il testing deve essere eseguito con le risorse disponibili, come tempo, costo e
numero di test case, poiché l'esecuzione di tutti i possibili test è impraticabile.

Esistono diverse tecniche di testing, tra cui black-box e white-box. Nel black-box, i casi di
test sono definiti solo dalle specifiche funzionali, mentre nel white-box, sono definiti sulla
base della conoscenza del codice sorgente.

Le fasi di testing includono il test di unità, che mira a individuare malfunzionamenti


all'interno dei singoli moduli, il test di integrazione, che verifica l'interazione tra i moduli, e il
test di sistema, che valuta il sistema nelle reali o realistiche condizioni d'esercizio. Infine, il
test di accettazione viene effettuato con la presenza del cliente per verificare che il
software soddisfi le sue esigenze.

18
Il test strutturale nel contesto del software è un processo di verifica e validazione che si
concentra sull'analisi delle strutture interne del programma per identificare potenziali errori
e problemi. Esso comprende due approcci principali: il black-box testing e il white-box
testing.

• Black-box testing: Questo tipo di test si concentra sulla valutazione delle


funzionalità del software senza considerare la sua implementazione interna. Gli
input vengono forniti al software, e i risultati vengono confrontati con i requisiti
funzionali per verificare che il software funzioni correttamente. Il black-box testing è
utile per identificare problemi legati alle specifiche e alle interazioni tra le diverse
funzionalità del software.
• White-box testing: In questo approccio, invece, si esamina il codice sorgente del
software e si creano casi di test in base alla sua struttura interna. Si punta a coprire
tutte le istruzioni, i rami decisionali e i cicli nel codice per garantire una maggiore
copertura e rilevare errori di programmazione. Il white-box testing è particolarmente
efficace per identificare errori logici e problemi nel flusso di controllo del software.
Un aspetto importante del test strutturale è la complessità del software. Questa può
essere misurata utilizzando il numero ciclomatico o numero di Mc Cabe, che conta i
cammini indipendenti presenti nel programma. Una complessità elevata può aumentare la
probabilità di errori e quindi richiede una maggiore attenzione durante il testing.

Per gestire la vastità dei casi di test possibili, si utilizzano modelli come il Control Flow
Graph (CFG) e il Call Graph (CG) per identificare le parti più complesse del codice e
focalizzare i test su quelle aree critiche.

Per selezionare i casi di test, si utilizzano diversi criteri di copertura, tra cui:

• Criterio delle istruzioni: Si assicura che ogni istruzione del codice sia eseguita
almeno una volta durante i test.
• Criterio delle decisioni: Si verifica che tutte le possibili decisioni, come le condizioni
"if" e "switch", siano esaminate durante i test.
• Criterio modificato MC/DC: Questo criterio si concentra su decisioni composte,
cercando di testare solo le condizioni rilevanti e ridurre così il numero di casi di test
necessari.
Infine, il test di robustezza, che coinvolge l'uso di dati scorretti o inattesi come input, aiuta
a verificare come il software reagisce in situazioni impreviste.

Complessivamente, il test strutturale è un elemento cruciale nella garanzia della qualità


del software, poiché aiuta a individuare difetti e vulnerabilità interne, consentendo agli
sviluppatori di correggerli prima che il software sia rilasciato al pubblico.

19
Il test funzionale è una tecnica di verifica e validazione del software che si basa sulle
specifiche funzionali, ovvero le descrizioni informali di ciò che il software dovrebbe fare.
Questo tipo di test è solitamente implementato come black-box testing o specification-
based testing, il che significa che non richiede la conoscenza interna del codice sorgente.

Il test funzionale si concentra sulla struttura delle specifiche stesse, cercando di


individuare errori che potrebbero sfuggire al test white-box, che invece si focalizza
sull'analisi del codice. Gli errori individuati da questo tipo di test possono riguardare parti di
codice mancanti o incomplete, relative alle funzionalità specificate, che non sarebbero
individuabili con il white-box testing.

Un vantaggio del test funzionale è che può essere condotto in modo economico, poiché
non richiede la conoscenza interna del codice. Inoltre, può essere applicato a diversi livelli
di test, dal test di unità al test di sistema, rendendolo un'ottima tecnica di raffinamento
delle specifiche.

Il processo di test funzionale inizia con l'analista che specifica i requisiti funzionali e li
passa al team di progettazione dei casi di test. Questa fase può essere seguita subito
dopo la specifica dei requisiti o rimandata alla fase di codifica. È fondamentale che le
specifiche siano accurate e testabili per ottenere test suite efficaci. Talvolta, l'analista
potrebbe non considerare il testing durante la stesura delle specifiche, ma attraverso il
feedback del team di progettazione dei casi di test, le specifiche possono essere
migliorate.

Individuare un difetto nel software è paragonato a "trovare un ago in un pagliaio", dove


l'ago rappresenta il difetto e il pagliaio è l'insieme di tutti i percorsi del software. Per
aumentare la probabilità di trovare difetti, sono necessari test più sistematici. Uno dei
metodi è il partizionamento degli input, dove l'insieme degli input viene suddiviso in
partizioni, ciascuna rappresentante un insieme di stati validi o non validi per le condizioni
di ingresso. Questo approccio consente di individuare i difetti concentrando i test su
specifiche classi di input e combinazioni significative, riducendo il numero totale di casi di
test necessari.

Il category partition testing è una tecnica di test combinatoriale che permette di strutturare
le specifiche di test in un insieme di proprietà o attributi variabili in insiemi di valori. Questo
metodo aiuta a selezionare un sottoinsieme rappresentativo dei casi di test da eseguire,
riducendo il numero totale di test necessari.

Il pairwise combinatorial testing è un altro tipo di testing combinatoriale che considera tutte
le possibili combinazioni di input a coppie (o n-uple). Poiché spesso i maggiori casi di
fallimento sono causati da combinazioni di pochi input, questa tecnica riduce notevolmente
il numero di casi di test necessari, testando ogni coppia di input solo una volta.

Infine, i vincoli (error, single, property) possono essere introdotti per eliminare
combinazioni invalide o ridondanti di valori delle categorie, ottimizzando ulteriormente il
numero di casi di test.

Complessivamente, il test funzionale è uno strumento potente per individuare difetti nel
software e per garantire che le specifiche funzionali siano soddisfatte correttamente,
contribuendo a migliorare la qualità del prodotto software.

20
Il test model-based è un metodo di derivazione dei casi di test che si basa sulla verifica del
comportamento previsto del programma tramite un modello formale. In questo approccio, il
software viene eseguito non rispetto alle specifiche, ma in base alle predizioni fatte
utilizzando il modello. Un esempio di modello può essere una macchina a stati che
rappresenta il comportamento di un'applicazione, come un orologio che visualizza l'ora in
formato digitale o analogico.

Esistono diversi criteri per selezionare i casi di test da eseguire usando il modello. Un
criterio può richiedere di testare tutti gli stati del modello, un altro può prevedere di
eseguire tutte le transizioni. Altri criteri possono essere basati su random walk, ovvero
percorrere in modo casuale il diagramma di stato, simile all'approccio del random testing.
Tuttavia, è importante notare che i difetti nel software non sono sempre equiprobabili,
quindi l'efficacia di questo approccio può variare.
La generazione dei casi di test da un modello è relativamente semplice, il costo è ridotto e
può essere automatizzata facilmente. Un aspetto cruciale è garantire che il modello
rappresenti correttamente il comportamento del sistema reale. Per fare ciò, è necessario
eseguire un test di conformità, verificando che il modello catturi accuratamente il
comportamento del sistema. La validazione del modello può essere fatta confrontando il
suo comportamento con il comportamento del sistema reale in casi noti. Tuttavia, per i casi
non noti, dove non si conosce il comportamento del sistema reale, è più difficile
determinare se il modello sia adeguato o meno.

I diagrammi di stato sono uno strumento comune per rappresentare i modelli a stati e
possono essere utilizzati per generare automaticamente i casi di test, basandosi sul
comportamento descritto dalla macchina a stati.

In sintesi, il test model-based è un approccio che si basa sulla verifica del comportamento
del software tramite un modello formale, ma è importante assicurarsi che il modello sia
accurato rispetto al sistema reale per ottenere risultati validi.

21
I modelli di qualità del software sono strumenti che definiscono un insieme di attributi
esaustivi e privi di sovrapposizioni, relativi a un prodotto software, e le dipendenze logiche
tra di essi. Questi modelli sono gerarchici e possono essere differenziati a seconda che
interessino chi compra o chi produce il software, sebbene gli attributi di qualità del
processo di sviluppo influenzino anche quelli rilevanti per gli utilizzatori del software.

L'obiettivo di produrre in qualità è ottenuto controllando il processo produttivo, avendo il


personale con le competenze adeguate e definendo regole per gestire il controllo e le non
conformità. La qualità del software è standardizzata dall'ISO 9000, una famiglia di
standard che riguarda la produzione di beni e servizi di ogni settore. Esiste anche una
sotto-famiglia specifica per il software, data la sua natura immateriale.

I modelli di qualità del software hanno diversi livelli. Il primo livello descrive le
caratteristiche di qualità del prodotto da diversi punti di vista, come le qualità esterne,
interne e in uso. Le qualità esterne riguardano il comportamento dinamico del software
nell'ambiente d'uso, mentre le qualità in uso esprimono l'efficacia ed efficienza del
software rispetto alle esigenze degli utenti. Le qualità interne, invece, sono attributi statici
del prodotto, indipendenti dall'esecuzione.

Al secondo livello, vengono descritte le sottocaratteristiche quantitative e misurabili, che


corrispondono ai principali attributi di qualità considerati nei tre punti di vista.

La norma ISO/IEC 9126-1 definisce sei caratteristiche principali di qualità del software,
ognuna con diverse sottocaratteristiche misurabili tramite metriche specificate nelle parti 2,
3 e 4 dello standard.

Esistono anche altre norme come ISO/IEC 9241, che definisce le caratteristiche
dell'usabilità, e ISO 12119, che riguarda le caratteristiche di qualità dei software
"Commercial off-the-shelf" (COTS).
Le metriche del software sono strumenti utilizzati per misurare quantitativamente le
caratteristiche del prodotto o del processo. Esistono diverse metriche dimensionali e di
complessità che possono essere applicate per misurare il software, ad esempio il numero
di linee di codice (LOC), il numero ciclomatico e il flusso di informazione.

Nel contesto dell'ingegneria del software, le metriche sono utilizzate per valutare la
produttività, la qualità e la complessità del software. Possono essere utilizzate sia a
posteriori per misurare le caratteristiche del prodotto software, sia a priori per stimare
costi, tempi o risorse necessarie per lo sviluppo.

Le metriche Object-Oriented (OO) sono specifiche per i programmi OO e si concentrano


sulla struttura interna o sulla complessità esterna degli oggetti. Esse includono, ad
esempio, il numero di metodi per classe, la response for a class, la lack of cohesion, il
coupling between object classes, la depth of inheritance tree e il number of children.
Queste metriche aiutano a valutare l'accoppiamento, la coesione e il riuso del software
orientato agli oggetti.

In conclusione, i modelli di qualità del software e le metriche sono strumenti fondamentali


per valutare e migliorare la qualità del software prodotto e del processo di sviluppo.

22
La stima dei costi è una fase cruciale nella pianificazione di un progetto poiché permette di
determinare il prezzo del prodotto finale, dimensionare il team di sviluppo e gestire il
processo di produzione. Tale stima è altresì applicabile ai prodotti software già esistenti,
per valutare se una manutenzione (correttiva, adattativa o perfettiva) risulta più
conveniente rispetto alla realizzazione ex-novo.

Tra i principali metodi di stima dei costi, basati su principi empirici, spicca l'analisi dei punti
di funzione (function points). Questa metrica misura la quantità di funzionalità del software,
e i punti funzione corrispondono al valore delle funzionalità prodotte. Calcolando i punti
funzione, si può stimare la quantità di sforzo richiesta per sviluppare il software,
indipendentemente dalla tecnologia impiegata.

Il metodo degli "unadjusted function points" (UFPM) è standardizzato dall'ISO e dal gruppo
IFPUG (International Function Point User Group) e si suddivide in due stati:

• Calcolo delle funzionalità senza applicare correzioni.


• Applicazione di valori correttivi in base all'esperienza o alla letteratura
dell'ingegneria del software.
Per calcolare gli "unadjusted function points" (UFP), occorre individuare il tipo di conteggio
(sviluppo di progetto, manutenzione evolutiva o applicativo), definire i confini applicativi e
identificare le funzioni dati e transazionali. Gli UFP vengono poi pesati in base alla loro
complessità (semplice, media o complessa).

Successivamente, si applicano i fattori correttivi, valutando 14 parametri che variano da 0


a 5 in base all'incidenza degli aspetti considerati. Questi fattori includono prestazioni,
efficienza per l'utente finale, riusabilità e altri.
Il valore finale del conteggio dei function points (FP) si ottiene moltiplicando gli UFP per
l'aliquota introdotta dai fattori correttivi. Questo valore permette di calcolare il costo del
progetto, ad esempio, moltiplicando il numero di FP per il costo di ciascun function point
(che dipende da vari fattori, come il linguaggio di programmazione e il livello di astrazione).

In conclusione, l'analisi dei punti di funzione offre un metodo vantaggioso e applicabile fin
dalla fase iniziale del ciclo di vita del progetto, permettendo una stima dei costi basata sui
requisiti funzionali del software. Tale processo di stima è fondamentale per una
pianificazione accurata e una gestione efficace del progetto software.

23
Il ciclo di vita del software è il processo seguito per costruire, consegnare ai clienti e far
evolvere il prodotto software, dalla sua ideazione fino al ritiro quando diventa obsoleto.
Esistono diversi modelli che cercano di catturare l'essenza di questo processo, ma tutti
condividono l'obiettivo di stabilire l'ordine delle attività e i criteri di transizione tra le diverse
fasi.

Uno dei primi e più primitivi modelli è il "code and fix", tipico di programmatori singoli, in cui
il codice viene scritto e corretto a mano mano che vengono identificati errori o richieste di
nuove funzionalità. Tuttavia, questo modello è inadeguato per lo sviluppo moderno del
software a causa della mancanza di una gestione pianificata della complessità, la difficoltà
nel gestire il personale e il processo non prevedibile con qualità non misurabile.

Le attività principali nella produzione del software includono:

• Studio di fattibilità: Valutazione preliminare per decidere se intraprendere il progetto,


valutando i costi, i benefici e le risorse necessarie.
• Acquisizione, analisi e specifica dei requisiti: Raccolta, documentazione e analisi
dei requisiti del software dal committente o utilizzatore finale.
• Progettazione: Strutturazione dell'applicazione a diversi livelli di dettaglio, definendo
l'architettura e le interfacce dei componenti.
• Codifica, test e rilascio: Produzione del codice sorgente, test del software (test di
unità, integrazione, sistema e accettazione) e consegna ai clienti.
• Manutenzione: Attività svolte per modificare il sistema dopo il rilascio per adattarlo,
correggerlo o migliorarlo.
Tutte le attività includono la documentazione, la verifica (validazione e verifica) e la
gestione per controllare il processo in modo adeguato.

Il processo di produzione del software è complesso, e il suo successo dipende dalla


pianificazione, dall'organizzazione del team e dalla qualità delle decisioni prese durante il
ciclo di vita del software.
Il modello a cascata è una metodologia di sviluppo del software in cui le attività sono
strutturate come una cascata lineare di fasi, in cui l'output di una fase diventa l'input della
fase successiva. Ogni fase è suddivisa in sottoattività che possono essere svolte in
parallelo da diverse persone. Le fasi del modello a cascata includono analisi dei requisiti,
progettazione, implementazione, test e manutenzione.
Il modello a cascata ha fornito importanti contributi alla comprensione dei processi
software, come la necessità di disciplina, pianificazione e gestione nello sviluppo del
software e il rinvio dell'implementazione fino a quando gli obiettivi non sono chiari.

Tuttavia, il modello a cascata ha delle limitazioni e punti deboli. Si basa sull'assunzione


della linearità dello sviluppo del software, ma nella pratica, possono verificarsi iterazioni e
cicli di feedback per adattarsi ai cambiamenti. Inoltre, il modello a cascata assume rigidità
nelle fasi, ma i requisiti possono essere soggetti a cambiamenti durante il ciclo di vita del
progetto. Inoltre, il modello a cascata è monolitico, il che significa che la pianificazione è
orientata a una singola data di rilascio, ma questo può portare a rilasci tardivi e
adattamenti necessari dopo che i requisiti sono cambiati.

Per superare le limitazioni del modello a cascata, sono state sviluppate varie alternative,
come il modello a cascata con retroazione (feedback), che introduce un feedback in ogni
fase per rilevare errori prima del rilascio, ma non anticipa i possibili cambiamenti. I modelli
evolutivi, come il modello a rilasci incrementali e il modello trasformazionale, permettono
una maggiore flessibilità e adattabilità rispetto ai cambiamenti dei requisiti.

Infine, le metodologie agili, come Extreme Programming (XP) e Scrum, sono modelli di
sviluppo leggeri e flessibili che si concentrano sulla soddisfazione del cliente, adattandosi
ai cambiamenti e incoraggiando la collaborazione tra team e committenti. Il modello
DevOps è un'evoluzione delle metodologie agili, focalizzato sull'integrazione tra sviluppo e
operazioni per migliorare l'efficienza e la sicurezza nella messa in esercizio del software.

Potrebbero piacerti anche