MokaByte
Numero 22 - Settembre 1998
|
|||
|
|
||
|
|||
Un "racconto" sul formalismo per la progettazione a oggetti ormai più famoso e (si spera) più usato. Questo articolo vuole fornire una introduzione a UML (Unified Modeling Language) per invogliare gli sviluppatori che ancora non usano nessuno di questi formalismi (come anche il "vecchio" OMT o quello di Booch) a documentarsi e a iniziare il viaggio verso una produzione di software razionalizzata e professionale, corredata della giusta documentazione.
Scopo
dell'articolo
Intendiamoci,
dato che l'intenzione è quella di invogliare qualche programmatore
ad avvicinarsi a questo mondo, il tono di questo articolo NON SARA' FORMALE.
Nel senso che non tedierò i lettori con tecnicismi (che a volte
non so neanch'io, ma non l'ho detto ;-) astrusi e noiosi, ma vorrei trasmettere
i concetti base e una vista di insieme in modo da permettere una valutazione
semplice: "a me programmatore interessa o no?" (io dico di sì).
Parlerò delle caratteristiche fondamentali di un linguaggio come
UML, i dettagli potrete trovarli nei vari libri (e siti) elencati in fondo
al testo.
E' importante
avvisare che per avvicinarsi a questi formalismi sono necessarie almeno
delle conoscenze di base sui linguaggi a oggetti. UML, infatti, come gli
altri formalismi che lo hanno preceduto, permette di rappresentare un sistema
in termini di "oggetti", "relazioni fra oggetti", dinamiche (comportamenti)
e stati. Per cui dovreste avere almeno un'idea di cosa siano termini come
metodo, oggetto, classe, istanza, interfaccia,
etc.
So che chi sta
leggendo questo articolo è un javista (neologismo che indica
un programmatore allergico al C++... scherzo, intendo un appassionato di
Java) e dovrebbe pertanto possedere quei concetti forti della programmazione
object oriented. Purtroppo nella mia decennale esperienza con Java (what?
un premio a chi mi spiega perchè non è possibile) ho visto
usare troppe volte questo linguaggio come se fosse un C semplificato (perchè
ad esempio non ci sono i puntatori, anche se per qualcuno è una
complicazione!!! pensate che ho conosciuto studenti che mi hanno detto
di trovarsi molto più a loro agio col C che non con Java!!!) per
non sollevare l'argomento delle basi OO.
Usare un formalismo
come UML (o un altro di vostra preferenza) evita la produzione del classico
spaguetti-code, che nel caso dei vecchi linguaggi poteva essere
l'introduzione dei GOTO, mentre oggi, nei linguaggi a oggetti, vuol dire
ad esempio avere "troppi reference" (magari incrociati), vuol dire creare
oggetti senza criterio (di solito andrebbe usata, almeno per certe classi
di oggettti, una classe istanziatrice centralizzata), vuol dire avere duplicazioni
e ridondanze nei metodi e negli attributi e chi più ne ha più
ne metta.
Perchè
usare un formalismo, non posso scrivere direttamente il codice?
Nessuno vi vieta
di scrivere direttamente codice. Svariati prodotti "visuali" (per molti
ambienti di sviluppo considero questo termine in senso spregiativo) incoraggiano
questo comportamento e forniscono spesso un class browser (un modulo
che vi dovrebbe dare una visione di insieme, facendovi vedere tutte le
classi che avete generato, con i loro metodi e attributi) che ha un difetto
fondamentale: NON FA VEDERE TUTTE LE RELAZIONI FRA LE CLASSI. Si vedono
(e anche raramente!) solo le relazioni di ereditarietà, cioè
forse nemmeno quelle più importanti.
Invece è
molto importante sapere quali sono TUTTE le relazioni fra le varie classi,
quali sono i messaggi che si scambiano (cioè di fatto i metodi che
vengono chiamati vicendevolmente), che transizioni (cambiamenti di stato)
avvengono e perchè (in base a quali eventi). Un linguaggio come
UML e simili (che in realtà sono solo modi grafici di rappresentare
componenti) vi dà questa possibilità, ed è sicuramente
più espressivo e potente di ua descrizione testuale ("vale più
una bitmap di mille parole", antico proverbio cinese del 95 A.M., Ante
Microsoft).
L'applicazione
come sistema di "oggetti"
Un programma
di oggi, a meno che non sia estremamente semplice, è di solito composto
da molte parti: una volta (coi linguaggi procedurali) le componenti di
un sistema venivano modellate in moduli (procedure/funzioni/algoritmi)
e strutture dati, oggi si potrebbe parafrasare il titolo di un libro famoso
parlando di "stato + comportamento = oggetto (o classe)". Si è semplicemente
pensato di tenere le strutture dati "vicine" alle procedure/funzioni/algoritmi
che devono manipolarle.
In Java (come
in altri linguaggi OO) una classe (da cui poi si istanzieranno gli oggetti)
è un "qualcosa" dotato di comportamento e stato allo
stesso tempo, un agglomerato di metodi (procedure/funzioni/algoritmi) che
lavorano su un insieme di dati che possono essere interni (e allora parliamo
di attributi) o esterni (singoli attributi di altri oggetti o oggetti interi).
E' ancora possibile ricondursi alla situazione precedente (di linguaggio
procedurale), anche se non è generalmente consigliabile (i casi
ci sono, ma sono rari), infatti se costruite una classe di soli attributi
avete l'analogo di una struct del C, mentre se costruite una classe
di soli metodi statici avete l'analogo di una libreria di procedure/funzioni/algoritmi.
Un esempio di "libreria" esiste in Java nella classe java.lang.Math,
ha solo metodi statici e non è altro che una libreria matematica.
Classi/Oggetti
e relazioni (~ER) - i componenti del sistema
Nella progettazione
a oggetti (ma in fondo anche nella vita quotidiana), quando si descrive
un sistema complesso, spesso lo si fa descrivendo le sue sottoparti. In
termini OO le sottoparti sono le classi, il sistema globale è
composto dall'insieme delle classi collegate tramite RELAZIONI.
Le relazioni sono il collante che permette alle classi (o meglio agli oggetti
istanziati dalle classi) di interagire e di far sì che il Sistema
(stavolta con la S maiuscola, ad indicare la globalità del nostro
prodotto) si comporti come noi vogliamo che faccia (in realtà non
succede mai, come direbbe Murphy ;-).
La relazione
che tutti nominano (perchè è quella più evidente)
parlando di linguaggi a oggetti è quella di ereditarietà:
una classe che estende un'altra classe EREDITA una parte dei campi (attributi
e metodi) e non ha bisogno di implementarli a sua volta, anche se può
ridefinirli, ovviamente.
Nella figura
che segue vediamo il primo esempio di diagramma UML per rappresentare la
relazione di ereditarietà fra una ClassePadre e una ClasseFiglia,
il simbolo usato è una specie di freccia con punta grossa, tra parentesi
quadre ho messo la keyword che in Java descrive l'ereditarietà (extends).
Ogni classe viene
disegnata con un rettangolo diviso in tre sezioni: in alto il nome, poi
gli attributi (l'icona accanto al nome mostra la sua "accessibilità")
e infine i metodi. Nell'esempio riportato abbiamo la ClasseFiglia che ridefinisce
(overriding) il metodo_protetto() ereditato da ClassePadre.
Questo per quanto
riguarda l'ereditarietà fra classi, ma prima ho affermato che esistono
altre relazioni oltre a questa, ne è un esempio la classe Altra,
che non eredita da nessuno, nè ha figli, eppure è collegata
a ClassePadre da un segmento con degli attributi... Spesso infatti ci si
dimentica che quella di ereditarietà non è l'unica relazione
possibile fra classi/oggetti, ma prima di vedere che relazione ha Altra
con ClassePadre vediamo qualcosa di più semplice.
Cominciamo con
una relazione generica (nel senso di non specializzata), cioè quella
di "utilizzo" o "conoscenza", si guardi questo frammento di codice:
class A{ |
In generale questi
diagrammi ci dicono solo che la classe A conosce/usa la classe B, NON specificano
(salvo indicazioni esplicite) come verrà implementata effettivamente
questa relazione. Normalmente l'mplementazione standard è quella
di mettere un reference (dichiarando un attributo, "+refToB" è un
esempio) in ognuna delle due classi. Infatti se facciamo generare il codice
a Rose (il programma da cui sono tratte queste videate) l'output è
proprio come lo avete visto nel tratto di codice poco sopra.
E se volessimo
tornare al caso della classe Altra di prima? Vediamo che il segmento che
la unisce a ClassePadre ha un nome ("contiene") e due altri attributi (invece
delle etichette "+ref...") un numero ("1") e un asterisco ("*"). Che vorranno
dire? Stiamo introducendo il concetto di "molteplicità" di una relazione,
infatti non sempre è sufficiente dire che una classe è collegata
ad un'altra (come nel caso A <-> B), ma si vuole anche specificare QUANTE
istanze di una classe sono collegate a QUANTE dell'altra. Nel caso di Altra
abbiamo che UNA istanza di ClassePadre "contiene" UN NUMERO A PIACERE di
istanze di Altra. In questo caso l'implementazione standard è:
public class ClassePadre{ |
In questo caso,
essendo la classe Table a fare da tramite fra AA e BB, in AA e in BB non
troveremo array come nei casi precedenti, ma solo un reference ad un'istanza
di Table, cioè una implementazione simile a questa:
public class
AA{
... private Table tbl; ... } |
Comportamento
Finora abbiamo
parlato di classi e istanze in maniera statica, cioè le abbiamo
descritte in termini di attributi (le informazioni che contengono), di
metodi (cosa sanno fare) e di relazioni ("chi conosce chi"). Non abbiamo
ancora detto nulla sul funzionamento del nostro prodotto, non abbiamo ancora
introdotto il "fattore tempo" nei nostri disegni. Fino a che ci si limita
a disegnare diagrammi di classi infatti, si sta descrivendo COME E' FATTO
un sistema, NON COME SI COMPORTA. Abbiamo detto "chi può chiamare
chi", non abbiamo ancora mai detto QUANDO, o in che ordine. A queste esigenze
rispondono i diagrammi delle interazioni e degli stati.
In questa figura vedete un micro esempio di diagramma delle interazioni.
Il tempo (finalmente introdotto) scorre verso il basso, il diagramma si
legge dall'alto verso il basso e da sinistra verso destra.
In questo caso abbiamo un'istanza di ClassePadre che chiama il metodo_pubblico() di un'istanza di ClasseFiglia, che risponde ritornando un qualche valore. La caratteristica importante di uno schema di questo tipo è che mette in SEQUENZA una serie di eventi, cioè mi dice in che ordine avvengono gli eventi. Non che questo sia un esempio completo, i diagrammi reali hanno di solito tante righe verticali (gli attori di una interazione) e parecchi eventi; guardate questo esempio (un classico), la telefonata:
Qui gli attori
sono tre: chi chiama, il centralino e chi riceve. Si legge così:
il chiamante alza la cornetta, il centralino risponde col tono di centrale,
allora il chiamante digita il numero, la chiamata viene instradata,
il centralino fa squillare il telefono del ricevente e (contemporaneità)
fornisce il segnale di libero al chiamante, quando il chiamato alza
a sua volta la cornetta il centralino smette di far squillare (e di dare
il tono di libero) e permette la conversazione.
L'altro modo
di rappresentare la sequenzialità è attraverso gli schemi
a stati, qui abbiamo un esempio di un oggetto che potrebbe assomigliare
ad un Thread (non è completo):
I vari stati sono rappresentati da rettangoli dai vertici arrotondati, le frecce indicano i passaggi di stato e le etichette delle frecce ci dicono qual'è l'evento scatenante la transizione. Di solito l'evento (almeno in Java) non è altro che la chiamata di un metodo.
Un
esempio classico
Quando si introducono
i concetti OO, un esempio classico è quello dei veicoli (l'altro
è quello delle specie animali). Qui sotto, nella figura, c'è
un abbozzo di diagramma delle classi per rappresentare i veicoli a motore.
Una classe Veicolo (probabilmente astratta) che si specializza nei vari
tipi concreti. Ogni veicolo a motore ha un solo motore (ma va!?), anche
se nella realtà potrebbe averne di più (non pensiate di riuscire
a rappresentare correttamente un sistema al primo colpo). Inoltre possiede
da una ruota a infinite. Così va letto il diagramma sottostante,
ammetterete che è una rappresentazione potente, o no?
Prodotti
e Riferimenti
Qui di seguito
ho messo un po' di riferimenti interessanti, non pretendo di essere stato
esaustivo, nè con la descrizione di UML (moooolto introduttiva!),
nè con l'elenco dei siti. Se navigate un po' in Internet troverete
moltissimo materiale sulla progettazione OO e sui Patterns, il problema
sarà fermarsi...
ambiente
di progettazione, genera codice, fa reverse engineering di codice esistente.
analogo al
precedente, forse un po' più leggero.
libro molto
succinto ma denso di informazioni, è una specie di quick reference
sull'UML, è anche uno di quelli che costano meno ;-).
l'organismo
di riferimento per quello che riguarda l'OO (un po' come www.w3.org per
il web).
rappresentante
per l'Italia di Rose, trovate un po' di documentazione da scaricare.
libro/bibbia
sull'OMT, la versione più recente dovrebbe essere aggiornata con
UML, spiega come si fa la progettazione OO dai primi rudimenti alle finezze
progettuali.
che fine
ha fatto? - tempo fa (navigando in rete) ero capitato in un sito che parlava
di JG, un formalismo stile UML/OMT dedicato esclusivamente a Java (per
cui più semplice da capire e usare), però non ho idea della
fine che questo formalismo abbia fatto...
bel libro
(uno dei primi, ora sta diventando una moda) sulla analisi e progettazione
di pattern (insiemi di classi e relazioni che modellano sistemi
usati frequentemente, ad esempio un iteratore o una coppia osservatore/osservato),
lo consiglio a chi voglia avere una meta-visione sulla progettazione/programmazione
OO.
il nostro
sito - non c'entra direttamente con UML, anche se noi progettiamo tutto
con quello, però venite a trovarci ;-)
MokaByte Web 1998 - www.mokabyte.it MokaByte ricerca nuovi collaboratori. Chi volesse mettersi in contatto con noi può farlo scrivendo a mokainfo@mokabyte.it |