Una
classe di progettazione rappresenta un'astrazione di una o più classi nell'implementazione del sistema; a cosa
corrisponda esattamente dipende dal linguaggio dell'implementazione. Ad esempio, in un linguaggio orientato sugli
oggetti come C++, una classe corrisponde ad una semplice classe. Oppure in Ada, una classe corrisponde ad un tipo con
tag definito nella parte visibile di un pacchetto.
Le classi definiscono degli oggetti, che a loro volta realizzano (implementano) i casi d'uso. Una classe ha
origine dai requisiti che che le realizzazioni dei casi d'uso crea sugli oggetti necessari nel sistema, oltre che da
qualunque modello oggetto sviluppato precedentemente.
Se una classe è valida o meno dipende in larga misura dall'ambiente di implementazione. La dimensione appropriata alla
classe e ai suoi oggetti dipende dal linguaggio di programmazione, ad esempio. Quello che si ritiene giusto quando si
utilizza Ada potrebbe essere sbagliato con Smalltalk. Le classi devono essere associate ad un particolare fenomeno nel
linguaggio di implementazione e devono essere strutturate in modo tale che la mappatura risulti in un codice valido.
Anche se le peculiarità del linguaggio di implementazione influenzano il modello di progettazione, è necessario che la
struttura della classe sia facile da capire e da modificare. È necessario progettare come se si disponesse di classi ed
incapsulazione, anche se il linguaggio di implementazione non le supporta.
L'unico modo in cui altri oggetti possono accedere o influenzare gli attributi e le relazioni di un oggetto è tramite
le sue operazioni. Le operazioni di un oggetto vengono definite dalla sua classe. Un comportamento specifico può
essere eseguito tramite le operazioni, che possono influire sugli attributi e sulle relazioni di cui l'oggetto dispone,
e provocare l'esecuzione di altre operazioni. Un'operazione corrisponde ad una funzione membro in C++ o ad una funzione
o procedura in Ada. Quale comportamento viene assegnato ad un oggetto dipende dal ruolo che svolge nelle realizzazione
di casi d'uso.
Nella specifica di un'operazione, i parametri costituiscono i parametri formali. Ogni parametro ha un nome ed un
tipo. È possibile utilizzare la sintassi e la semantica del linguaggio di implementazione, per specificare le
operazioni ed i loro parametri, in modo tale che saranno già specificati nel linguaggio di implementazione quando viene
avviata la codifica.
Esempio:
Nel Sistema della macchina per il riciclaggio, gli oggetti di una classe Base di ricezione tengono
traccia di quanti articoli di deposito di un certo tipo il cliente ha restituito. Il comportamento di un oggetto
Base di ricezione include l'aumento del numero di oggetti restituiti. L'operazione insertItem, che riceve
un riferimento per l'articolo restituito, serve allo scopo.
Utilizzare la sintassi e la semantica del linguaggio di implementazione quando si specificano le operazioni.
Un'operazione denota quasi sempre il comportamento di un oggetto. Anche un'operazione può denotare il comportamento di
una classe, nel qual caso si tratta di un'operazione di classe. Può essere modellato in UML definendo un tipo
per l'operazione.
Sono possibili le seguenti visibilità su un'operazione:
-
Pubblica: l'operazione è visibile per modellare gli elementi tranne la classe stessa.
-
Protetta: l'operazione è visibile solo alla classe stessa, alle sottoclassi o per gli amici della
classe (dipende dal linguaggio)
-
Privata: l'operazione è visibile alla classe stessa e agli amici della classe
-
Implementazione: l'operazione è visibile solo all'interno della classe stessa.
La visibilità Pubblica deve essere utilizzata molto di rado, solo quando un'operazione è
necessaria ad un'altra classe.
La visibilità Protetta deve essere l'impostazione predefinita; protegge l'operazione dall'utilizzo da
parte di classi esterne, che promuove l'accoppiamento libero e l'incapsulamento del comportamento.
La visibilità Privata deve essere utilizzata nei casi in cui si desidera impedire alle sottoclassi di
ereditare l'operazione. Ciò fornisce un modo per annullare l'accoppiamento delle sottoclassi con la superclasse e di
ridurre la necessità di rimuovere o escludere le operazioni ereditate non utilizzate.
La visibilità Implementazione è la più restrittiva; viene utilizzata nei casi in cui solo la classe
stessa è in grado di utilizzare l'operazione. E' una variante della visibilità Privata, che per la maggior parte
dei casi è adatta.
Un oggetto può reagire in modi diversi ad uno specifico messaggio a seconda dello stato in cui si trova; il
comportamento di un oggetto in base allo stato è definito da un diagramma di stati associato. Per ciascun stato in cui
l'oggetto può trovarsi, il diagramma di stati descrive quali messaggi può ricevere, quali operazioni verranno eseguite
e in quale stato si troverà l'oggetto da quel momento in poi. Per ulteriori informazioni fare riferimento a Tecnica: Diagramma di stati.
Una collaborazione è un insieme di interazioni di oggetti dinamico in cui un insieme di oggetti che comunicano inviando
messaggi reciprocamente. L'invio di messaggi è diretto in Smalltalk; in Ada viene effettuato come chiamata di un
programma secondario. Un messaggio viene inviato ad un oggetto destinatario che richiama un'operazione all'interno
dell'oggetto. Il messaggio indica il nome dell'operazione da eseguire, insieme ai parametri richiesti. Quando i
messaggi vengono inviati, vengono forniti i parametri effettivi (i valori dei parametri formali) per
tutti i parametri.
Le trasmissioni di messaggi fra gli oggetti in una realizzazione di caso d'uso ed il punto focale di controllo che gli
oggetti seguono quando vengono richiamate le operazioni, sono descritti nei diagrammi di interazioni. Per informazioni
su questi diagrammi consultare Tecnica:
Diagramma di sequenza e Tecnica:
Diagrammi di comunicazione.
Un attributo è una proprietà denominata di un oggetto. Il nome di un attributo è un sostantivo che descrive il ruolo
dell'attributo in relazione all'oggetto. Un attributo può avere un valore iniziale quando viene creato l'oggetto.
Modellare gli attributi solo se questo rende un oggetto più comprensibile. Modellare la proprietà di un oggetto come
attributo solo se si tratta di una proprietà relativa unicamente a quell'oggetto. Altrimenti modellare la
proprietà con una relazione di associazione o di aggregazione ad una classe i cui oggetti rappresentano la proprietà.
Esempio:
Un esempio di come viene modellato un attributo. Ogni membro di una famiglia ha un nome e un indirizzo. Qui gli
attributi mio nome e indirizzo di casa di tipo Nome e Indirizzo sono stati identificati
rispettivamente:
In questo esempio, viene utilizzata un'associazione invece di un attributo. La proprietà mio nome probabilmente
è univoca per ogni membro di una famiglia. Quindi è possibile modellarla come attributo con tipo attributo Nome.
Un indirizzo, però, è condiviso da tutti i membri di una famiglia, quindi viene modellato meglio da un'associazione fra
la classe Membro famiglia e la classe Indirizzo.
Non è sempre facile decidere immediatamente se modellare un concetto come oggetto separato o come attributo di un altro
oggetto. Avere nel modello di oggetti degli oggetti non necessari porta ad un eccesso di documentazione e di sviluppo
inutili. Quindi è necessario stabilire determinati criteri per determinare l'importanza di un concetto per il sistema.
-
Accessibilità. Ciò che governa la scelta fra oggetto e attributo non è l'importanza del concetto nella vita
reale ma l'esigenza di accedervi durante il caso d'uso. Se si accede spesso all'unità, modellarla come oggetto.
-
Separazione durante l'esecuzione. Concetti del modello gestiti separatamente come oggetti durante
l'esecuzione del caso d'uso.
-
Legami ad altri concetti. Concetti del modello strettamente legati a determinati altri concetti e mai
utilizzati separatamente, ma sempre tramite un oggetto come attributo dell'oggetto.
-
Richieste da relazioni. Se, per qualche motivo, è necessario correlare un'unità da due direzioni,
riesaminare l'unità per vedere se dovrebbe essere un oggetto separato. Due oggetti non possono associare la stessa
istanza di un tipo di attributo.
-
Frequenza di ricorrenza. Se un unità esiste solo durante un caso d'uso, non modellarla come oggetto.
Modellarla invece come attributo dell'oggetto che esegue il comportamento in questione o semplicemente menzionarlo
nella descrizione dell'oggetto interessato.
-
Complessità. Se un oggetto diventa troppo complicato a causa dei suoi attributi, è possibile estrarre alcuni
degli attributi in oggetti separati. Utilizzarlo questo metodo con moderazione, in modo da non avere troppi
oggetti. D'altro canto le unità potrebbero essere molto dirette. Ad esempio, classificate come attributi si
considerino le (1) unità sufficientemente semplici da essere supportate direttamente da tipi primitivi nel
linguaggio di implementazione, ad esempio i numeri interi in C++, e (2) unità abbastanza semplici per essere
implementate utilizzando i componenti, indipendenti dall'applicazione, dell'ambiente di implementazione, ad esempio
Stringa in C++ e Smalltalk-80.
Probabilmente un concetto si modella in modi diversi per sistemi diversi. In un sistema, il concetto può essere così
vitale da modellarlo come oggetto. In un altro sistema potrebbe essere di minore importanza e quindi verrebbe modellato
come attributo di un oggetto.
Esempio:
Ad esempio, per una compagnia aerea si svilupperebbe un sistema che supporta le partenze.
Un sistema che supporta le partenze. Si supponga che il personale all'aeroporto desideri un sistema che supporti le
partenze. Per ogni partenza è necessario definire l'ora della partenza, la compagnia aerea e la destinazione. È
possibile modellarlo come oggetto della classe Partenza, con attributi ora della partenza, compagnia
aerea e destinazione.
Se invece il sistema viene sviluppato per un'agenzia di viaggi, la situazione potrebbe essere in qualche modo diversa.
'Destinazioni di volo' forma il proprio oggetto Destinazione.
L'ora della partenza, la compagnia aerea e la destinazione naturalmente saranno ancora necessarie. Tuttavia vi sono
degli altri requisiti, poiché l'agenzia di viaggi è interessata a individuare una partenza con una specifica
destinazione. È necessario quindi creare un oggetto separato per Destinazione. Gli oggetti Partenza e
Destinazione naturalmente devono essere a conoscenza l'uno dell'altro, cosa che viene abilitata con
un'associazione fra le loro classi.
L'argomento per l'importanza di determinati concetti è valido anche per determinare quali attributi devono essere
definiti in una classe. La classe Auto senza dubbio definirà attributi diversi se i suoi oggetti fanno parte di
un sistema di registrazione della motorizzazione o se fanno parte di un sistema di fabbricazione di automobili.
Infine, le regole su cosa rappresentare come oggetti e cosa come attributi non sono assolute. In teoria è possibile
modellare tutto come oggetti ma è scomodo. Una regola pratica è di visualizzare un oggetto come qualcosa che ad un
certo momento viene utilizzato a prescindere da altri oggetti. Inoltre non è necessario modellare ogni proprietà di
oggetto con un attributo, solo le proprietà necessarie per capire l'oggetto. Non modellare dei dettagli che sono
talmente specifici per l'implementazione da essere gestiti meglio direttamente dall'implementatore.
Attributi della classe
Un attributo quasi sempre denota le proprietà dell'oggetto. Un attributo può anche denotare le proprietà di una classe,
nel qual caso si tratta di un attributo della classe. Può essere modellato in UML definendo un tipo per
l'attributo.
Un oggetto può incapsulare qualcosa il cui valore può cambiare senza che l'oggetto esegua alcun comportamento. Potrebbe
trattarsi di qualcosa che in realtà è un'unità esterna ma che non è stata modellata come attore. Ad esempio, potrebbero
essere stati scelti dei margini di sistema e all'interno esservi incluso qualche genere di sensore. Il sensore può
essere quindi incapsulato all'interno dell'oggetto in modo che il valore che misura costituisce un attributo. Questo
valore può cambiare continuamente o a determinati intervalli senza che l'oggetto venga influenzato da nessun altro
oggetto del sistema.
Esempio:
È possibile modellare un termometro come oggetto; l'oggetto ha un attributo che rappresenta la temperatura e cambia il
il valore in risposta alle variazioni di temperatura dell'ambiente. Altri oggetti possono chiedere la temperatura
corrente eseguendo un'operazione sull'oggetto termometro.
Il valore dell'attributo temperatura cambia spontaneamente nell'oggetto Termometro.
È anche possibile modellare un valore incapsulato che cambia in questo modo come un normale attributo, si deve
descrivere nella classe dell'oggetto che cambia spontaneamente.
La visibilità dell'attributo assume uno dei seguenti valori:
-
Pubblico: l'attributo è visibile sia all'interno che all'esterno del pacchetto che contiene la classe.
-
Protetto: l'attributo è visibile solo alla classe stessa, alle sue classi secondarie o agli amici
della classe (dipende dal linguaggio)
-
Privato: l'attributo è visibile solo alla classe stessa e agli amici della classe
-
Implementazione: l'attributo è visibile alla classe stessa.
La visibilità Pubblica deve essere utilizzata molto di rado, solo quando un attributo è
accessibile direttamente un'altra classe. La definizione di visibilità pubblica è una notazione stenografata per
definire la visibilità dell'attributo come protetto, privato o implementazione, con operazioni pubbliche associate per
ottenere ed impostare il valore dell'attributo. La visibilità pubblica può essere utilizzata come dichiarazione per un
generatore di codice che le operazioni get/set devono essere generate automaticamente, risparmiando tempo durante la
definizione della classe.
La visibilità Protetta deve essere l'impostazione predefinita; protegge l'attributo dall'utilizzo da
parte di classi esterne, che promuove l'accoppiamento libero e l'incapsulamento del comportamento.
La visibilità Privata deve essere utilizzata nei casi in cui si desidera impedire alle sottoclassi di
ereditare l'attributo. Ciò fornisce un modo per annullare l'accoppiamento delle sottoclassi con la superclasse e di
ridurre la necessità di rimuovere o escludere gli attributi ereditati non utilizzati.
La visibilità Implementazione è la più restrittiva; viene utilizzata nei casi in cui solo la classe
stessa è in grado di utilizzare l'attributo. E' una variante della visibilità Privata, che per la maggior parte
dei casi è adatta.
Alcune classi possono rappresentare delle astrazioni complesse e avere una struttura complessa. Durante la modellazione
di una classe, il progettista potrebbe voler rappresentare i suoi elementi interni e le loro relazioni, per far sì che
l'implementatore implementi di conseguenza le collaborazioni che si verificano all'interno della classe.
In UML 2.0, le classi vengono definite come classi
strutturate, con la capacità di avere una struttura interna e delle porte. Le classi possono essere scomposte in
raccolte di parti connesse che possono ulteriormente essere scomposte a loro volta. Una classe può essere incapsulata
forzando le comunicazioni dall'esterno a passare attraverso le porte che obbediscono alle interfacce dichiarate.
Quindi, oltre ad utilizzare i diagrammi di classe per rappresentare le relazioni di classe (ad es. associazioni,
composizioni ed aggregazioni) e gli attributi, il progettista può utilizzare un diagramma di struttura composita.
Questo diagramma fornisce al progettista un meccanismo per visualizzare come le istanze delle parti interne svolgono il
loro ruolo in una istanza di una data classe.
Per ulteriori informazioni su questo argomento e degli esempi di diagrammi di struttura composita, consultare Concetto: Classe strutturata.
|