Torna alla pagina di Ingegneria del Software
:: Ingegneria del Software - Appunti del 21 Aprile 2009 ::
OOA e OOP
LA OOA è la fase di progettazione che arriva fino al tracciamento dei diagrammi delle classi, di sequenza ed eventualmente quelli di stati/transizioni. Siamo ancora in fase di analisi, e il nostro lavoro è language independent.
Dal momento in cui dobbiamo andare a specificare gli attributi di relazione, invece, entriamo nella fase di OOP, e questa fase è purtroppo language dependent. Vedremo in lezioni future come implementare gli attributi di relazioni con diversi linguaggi.
I diagrammi che vedremo in questa lezione sono anch'essi language-dependent, o meglio, dipendono dall'architettura software che abbiamo deciso di utilizzare.
Diagramma dei componenti
L'idea di questo diagramma è quella di passare da una visione monolitica, in cui l'unica suddivisione è quella tra le classi, ma tutte le classi sono infilate nello stesso eseguibile, ad un software diviso in componenti comunicanti.
Il vantaggio di dividere il software in componenti è che si possono installare su una macchina solamente i componenti necessari, ed è possibile riutilizzare gli stessi componenti per programmi diversi.
Gli svantaggi invece risiedono nel fatto che il componente installato su di una macchina potrebbe non essere esattamente quello di cui ho bisogno, vanificando quindi l'utilità di cui sopra. Inoltre, se un componente condiviso apre una falla di sicurezza, questa verrebbe automaticamente estesa anche al mio software, e spesso non c'è modo di verificare l'affidabilità di componenti realizzati da terzi.
Ricapitolando, l'unità di progetto del software sono le classi, ma l'unità di rilascio sono i componenti, ovvero insiemi di più classi omogenee dal punto di vista degli obiettivi o di altre caratteristiche funzionali.
Gli stereotipi sono in un certo senso dei componenti, dato che rappresentano categorie di classi che realizzano un certo aspetto applicativo.
Interfaccia di un componente
L'interfaccia di una classe sappiamo tutti che cos'è: è l'insieme dei metodi che la classe rende pubblici.
L'interfaccia di un componente sarà quindi l'insieme delle funzioni che il componente esporrà, e questo insieme deve essere un sottinsieme di tutte le interfacce delle classi contenute nel componente.
Se vediamo un componente come un contenitore di classe, è lecito aspettarci che tutte i metodi pubblici di queste classi siano visibili anche all'esterno del componente. In realtà ciò non è desiderabile: se ho riunito queste classi in un unico componente, è perché voglio che il componente esponga una sua interfaccia, e poi sia lui a giostrarsi con le classi che contiene in modo a me trasparente.
Dobbiamo quindi introdurre il grado di coesione di un componente: è il numero di metodi pubblici delle classi interne che sono visibili all'esterno del componente. Quanto più questo numero è basso, tanto più il mio componente è coeso; l'ideale sarebbe avere un unico metodo pubblico di interfaccia a cui poi collaborano tutti gli altri metodi interni.
Quando si raggruppano classi in componenti, pertanto, si persegue l'obiettivo di avere una grande coesione, cioè far sì che sia visibile il numero più basso possibile di metodi pubblici delle classi.
Il motivo è che, in teoria, le classi vengono raggruppate in un componente secondo un principio di collaborazione: le classi che collaborano le metto in un componente, e io dall'esterno faccio "partire" la loro complessa collaborazione con la semplice chiamata di un metodo. Se alla fine salta fuori che il mio componente è poco coeso, allora scoprirei che in realtà le classi che ci ho infilato dentro non stanno affatto collaborando.
Criteri di raggruppamento
Come faccio a distinguere le mie classi in componenti?
Posso utilizzare gli stereotipi. Se riesco a dare a tutte le mie classi un'etichetta di uno stereotipo, allora potrebbe essere sensato infilare tutte le classi con lo stesso stereotipo in un componente separato.
Oppure, come dicevamo sopra, posso utilizzare un criterio basato sulla coesione: me ne infischio dello stereotipo e delle funzionalità della classe, e metto nello stesso componente le classi che collaborano spesso. Ovviamente il grado di coesione non potrà mai essere al 100%, ma ci si può avvicinare.
Pertanto, per ogni componente dobbiamo essere in grado di dire:
- che classi contiene
- la metrica di coesione
- la sua interfaccia pubblica
Il come saranno realizzati sti componenti dipende poi dall'architettura che abbiamo scelto (dll, EJB, etc.).
Dipendenza
Quando un componente chiama un altro componente, si crea una relazione di dipendenza del primo rispetto al secondo.
Nel diagramma dei componenti la dipendenza viene indicata con una freccia che parte dal primo ed arriva al secondo. Ovviamente questa dipendenza deve essere anche possibile: se chiamo un certo metodo di una classe di un componente, devo anche garantire che quel metodo faccia parte dell'interfaccia pubblica del componente stesso.
Diagramma della messa in opera (deployment)
Giunge infine l'ora di dover installare il nostro software nel mondo fisico. Questo diagramma rappresenta un mapping tra l'architettura software e quella hardware. Nel caso più semplice, non ce n'è bisogno: copio l'eseguibile e via.
Ma in generale è possibile anche avere diversi diagrammi di deployment. Infatti, è pienamente possibile avere i componenti del nostro software divisi su diverse macchine e accessibili in rete, e pertanto il diagramma di deployment deve tenere in conto questa distribuzione dei compiti tra diverse macchine. A seconda poi della configurazione di rete dove andremo ad installare, dovremo fare scelte diverse rispetto al deployment. Di solito si parla con il responsabile della rete, si sentono i suoi pareri relativi all'aprire servizi qui e porte là, e poi si decide come installare.
Possiamo vedere questo diagramma come una sorta di istruzioni da dare all'installatore finale, a differenza del diagramma dei componenti che può essere visto come l'insieme delle istruzioni da dare a chi realizza il CD di installazione del nostro software o la cartella di download.
Nel diagramma di deployment i cubi rappresentano nodi della rete, e non componenti software; sono quindi le locazioni su cui andrò ad installare il mio programma. Le linee tra cubi sono semplicemente delle possibilità di comunicazione tra quei due nodi. Si vede anche qui che la configurazione della rete è un limite alle scelte di deployment: non si può deployare in un modo che non è supportato da quella rete.
Torna alla pagina di Ingegneria del Software