Utilizzo di meccanismi e modelli di progettazione
Utilizzare i pattern ed i meccanismi di progettazione adatti alla classe o funzione che si sta progettando e in accordo
alle linee guida per la realizzazione del progetto.
L'integrazione di un pattern e/o meccanismo significa effettivamente eseguire molte delle successive fasi in questa
attività (aggiunta di nuovi classi, operazioni, attributi e relazioni), ma con il rispetto delle regole definite dal
pattern o meccanismo.
Da notare che i modelli e i meccanismi vengono tipicamente integrati mentre evolve la progettazione e non solo durante
la prima fase in questa operazione. Inoltre, vengono frequentemente applicati a una serie di classi anziché soltanto ad
una singola classe.
|
Creazione di classi di progettazione iniziali
Creare una o diverse classi di progettazione iniziali per la classe di analisi fornita come input per questa operazione
e assegnare le dipendenze di traccia. Le classi di progettazione create in questa fase saranno perfezionate, regolate,
suddivise o integrate in fasi successive quando vengono assegnate varie proprietà di progettazione, come le operazioni,
i metodi e una macchina di stato, che descrivono in che modo viene progettata la classe di analisi.
A seconda del tipo di classe di analisi (boundary, entità o controllo) che si sta progettando, esistono strategie
specifiche che è possibile utilizzare per creare le classi di progettazione iniziali.
Le classi boundary rappresentano le interfacce per gli utenti o per altri sistemi.
Tipicamente, le classi boundary che rappresentano le interfacce per altri sistemi sono modellate come sottosistemi,
poiché hanno frequentemente funzionalità interne complesse. Se la funzionalità dell'interfaccia è semplice (agendo
probabilmente soltanto come passthrough verso un'API esistente di un sistema esterno), è possibile decidere di
rappresentare l'interfaccia con una o più classi di progettazione. Se si sceglie tale instradamento, utilizzare
un'unica classe di progettazione per protocollo, interfaccia o API e prendere nota dei requisiti speciali relativi agli
standard utilizzati nei requisiti speciali della classe.
Le classi boundary che rappresentano le interfacce per gli utenti seguono generalmente la regola di una classe boundary
per ciascuna finestra o una per ciascun modulo, nell'interfaccia utente. Di conseguenza le responsabilità delle classi
boundary possono essere a un livello abbastanza elevato e vanno perfezionate e dettagliate in questa fase. Ulteriori
modelli o prototipi dell'interfaccia utente possono essere un'altra fonte di input da tenere in considerazione in
questa fase.
La progettazione delle classi boundary dipende dagli strumenti di sviluppo dell'interfaccia utente (IU) disponibili per
il progetto. Utilizzando una tecnologia corrente, è normale che l'interfaccia utente sia visivamente realizzata
direttamente nello strumento di sviluppo. Ciò crea automaticamente le classi IU che devono essere associate alla
progettazione di controllo e alle classi entità. Se l'ambiente di sviluppo IU crea automaticamente le classi di
supporto, occorre che tale ambiente implementi l'IU; non è necessario considerare le classi durante la progettazione.
L'utente deve solo progettare quelle funzioni che l'ambiente di sviluppo non crea.
Durante l'analisi, le classi entità rappresentano unità manipolate di informazioni. Sono spesso passive e permanenti e
possono essere identificate e associate al meccanismo di analisi per la permanenza. I dettagli della progettazione di
un meccanismo di persistenza basato sul database sono descritti nella sezione Compito:
Progettazione del database. Le considerazioni sulle prestazioni potrebbero forzare alcune scomposizioni delle
classi permanenti, determinando modifiche nel modello di progettazione che sono discusse tra il Ruolo: Progettista database e il Ruolo:
Progettista.
Una discussione più ampia delle problematiche di progettazione per le classi persistenti è riportata successivamente
nella sezione Identificazione delle classi permanenti.
Un oggetto di controllo è responsabile per la gestione del flusso di un caso d'uso e, pertanto, coordina la maggior
parte delle sue azioni; gli oggetti di controllo incapsulano la logica che non è associata in particolare alle
problematiche dell'interfaccia utente (oggetti boundary) o alle problematiche di progettazione dei dati (oggetti
entità). Questa logica è a volte definita logica applicativa o logica aziendale.
Valutare le seguenti problematiche quando si progettano le classi di controllo:
-
Complessità - E' possibile gestire il comportamento di controllo o di coordinamento non complicato
utilizzando le classi boundary o entità. Tuttavia, all'aumentare della complessità dell'applicazione, si
evidenziano inconvenienti significativi rispetto a questo approccio, come:
-
la funzionalità di coordinazione del caso d'uso viene integrata nell'IU, rendendo più difficile modificare il
sistema
-
lo stesso IU non può essere utilizzato in diverse realizzazioni di casi di utilizzo senza difficoltà
-
l'IU viene caricata con le funzionalità aggiuntive, riducendone le prestazioni
-
gli oggetti di entità potrebbero essere caricati con una funzionalità specifica del caso d'uso, riducendone la
generalità
Per evitare questi problemi, sono state introdotte le classi di controllo per fornire una funzionalità associata al
coordinamento dei flussi di eventi.
-
Probabilità di modifica - Se la probabilità di modifica dei flussi di eventi è bassa oppure il costo è
trascurabile, la spesa aggiuntiva e la complessità di ulteriori classi di controllo potrebbero non essere
giustificate.
-
Distribuzione e prestazioni - La necessità di eseguire parti dell'applicazione su nodi diversi oppure in
diverse aree di processo, introduce il bisogno di specializzare gli elementi del modello di progettazione. Tale
specializzazione è spesso realizzata aggiungendo degli oggetti di controllo e distribuendo la funzionalità dalle
classi boundary ed entità alle classi di controllo. Con questa operazione, le classi boundary migrano verso i
servizi IU di fornitura, le classi entità si spostano verso i servizi di dati di fornitura e le classi di controllo
forniscono il resto.
-
Gestione delle transazioni - La gestione delle transazioni è una classica attività di coordinamento. Senza
l'uso di un framework che consenta la gestione delle transazioni, una o più classi del responsabile
transazioni devono interagire per essere certi che venga mantenuta l'integrità delle transazioni.
Nel secondo caso, se la classe di controllo rappresenta un thread separato di controllo, potrebbe essere più opportuno
utilizzare una classe attiva per modellare il thread di controllo. In un sistema in tempo reale, l'uso del processo Prodotto di lavoro: Capsule è l'approccio di modellazione preferito.
|
Identificazione delle classi permanenti
Le classi che devono memorizzare i relativi stati su un supporto permanente vengono indicate come persistenti.
L'esigenza di memorizzare lo stato potrebbe essere necessaria per la registrazione permanente delle informazioni sulle
classi, per il backup in caso di malfunzionamento del sistema oppure per lo scambio di informazioni. Una classe
permanente può avere sia istanze permanenti che transitori; la classificazione di una classe permanente significa
semplicemente che alcune istanze della classe potrebbero necessitare di essere permanenti.
Integrare i meccanismi di progettazione corrispondenti ai meccanismi di permanenza individuati durante l'analisi. Ad
esempio, a seconda di cosa sia richiesta dalla classe, si può realizzare il meccanismo di analisi per la permanenza
mediante uno dei seguenti meccanismi di progettazione:
-
Storage in-memory
-
Flash card
-
File binario
-
DBMS (Database Management System)
Gli oggetti permanenti potrebbero non derivare solo dalle classi entità; possono anche essere richiesti per gestire i
requisiti non funzionali in generale. Esempi sono gli oggetti permanenti richiesti per conservare le informazioni
attinenti al controllo del processo o le informazioni sullo stato tra le transazioni.
L'identificazione delle classi permanenti serve a notificare il Ruolo:
Progettista database che la classe richiede una speciale attenzione alle proprie caratteristiche di memoria fisica.
Notifica inoltra il Ruolo: Architetto di software che la classe deve essere permanente e
il responsabile Ruolo: Progettista per il meccanismo di permanenza che le istanze
della classe devono essere permanenti.
A causa della richiesta di una strategia di permanenza coordinata, il Ruolo:
Progettista database è responsabile per la mappatura delle classi permanenti nel database, utilizzando un framework
di permanenza. Se il progetto sviluppa un framework di permanenza, lo sviluppatore del framework sarà anche
responsabile per l'apprendimento dei requisiti di permanenza delle classi di progettazione. Per fornire le informazioni
richieste a queste persone, è sufficiente a questo punto indicare che la classe è permanente o, più precisamente, che
le istanze della classe sono permanenti.
|
Definizione della visibilità di classe
Per ciascuna classe, determinarne la visibilità all'interno del pacchetto in cui risiede. Si può indicare una classe
pubblica all'esterno del pacchetto che la contiene. Una classe privata (oppure una la cui visibilità è
implementazione) può essere indicata solo dalle classi all'interno dello stesso pacchetto.
|
Definizione delle operazioni
Per identificare le operazioni sulle classi di progettazione:
-
Esaminare le responsabilità di ciascuna classe di analisi corrispondente, creando un'operazione per ogni
responsabilità. Utilizzare la descrizione della responsabilità come descrizione iniziale dell'operazione.
-
Esaminare le realizzazioni del caso d'uso nella classe participates per esaminare come le operazioni vengono
utilizzate dalle realizzazioni del caso d'uso. Estendere le operazioni, una realizzazione caso d'uso alla volta,
migliorando le operazioni, le relative descrizioni, i tipi di restituzione ed i parametri. Ciascun requisito della
realizzazione caso d'uso riguardante le classi è descritto come testo nel flusso degli eventi della realizzazione
caso d'uso.
-
Esaminare il caso d'uso Requisiti speciali per essere certi che non vengano omessi requisiti impliciti
nell'operazione che potrebbe essere specificata.
Le operazioni sono richieste per supportare i messaggi che appaiono nei diagrammi sequenza poiché gli script,
specifiche temporanee di messaggi che non sono state ancora assegnate alle operazioni, descrivono la funzionalità che
la classe è prevista di eseguire. La figura 1 illustra un esempio di un diagramma sequenza.
Figura 1: I messaggi formano la base per l'identificazione delle operazioni
Le realizzazioni dei casi di utilizzo non forniscono informazioni sufficienti per identificare tutte le operazioni. Per
individuare le operazioni restanti, considerare quanto segue:
-
Esiste un modo per inizializzare una nuova istanza della classe, includendo la connessione alle istanze di altre
classi a cui è associata?
-
E' necessario eseguire un test per verificare se sono uguali le due istanze della classe?
-
E' necessario creare una copia di un'istanza di classe?
-
Sono richieste delle operazioni nella classe da meccanismi che utilizzano? Ad esempio, un meccanismo di raccolta
dati obsoleti potrebbe richiedere la cancellazione da parte di un oggetto di tutti i propri riferimenti verso
tutti gli altri oggetti in modo da rendere disponibili eventuali risorse non utilizzate.
Non definire le operazioni che ottengono ed impostano semplicemente i valori degli attributi publici (consultare Definizione degli attributi e Definizione delle
Associazioni). Di solito vengono creati da funzioni di generazione codice e non necessitano di essere definiti
esplicitamente.
Utilizzare le convenzioni di denominazione per il linguaggio di implementazione quando si denominano le operazioni, i
tipi di restituzione ed i parametri con i relativi tipi. Esse sono descritte nella sezione Linee guida specifiche del progetto.
Per ciascuna operazione, è necessario definire quanto segue:
-
Il nome dell'operazione - lasciare il nome breve e descrittivo in base al risultato ottenuto
dall'operazione.
-
I nomi delle operazioni devono seguire la sintassi del linguaggio di implementazione. Esempio:
trova_ubicazione è accettabile per C++ o Visual Basic, ma non per Smalltalk (dove non vengono
utilizzati i caratteri di sottolineatura); un nome migliore per tutti potrebbe essere
trovaUbicazione.
-
Non utilizzare nomi che sottintendono come l'operazione viene eseguita. Ad esempio, Employee.wages()
è preferito a Employee.calculateWages(), poiché il secondo sottintende l'esecuzione di un'operazione
di calcolo. L'operazione potrebbe facilmente restituire un valore in un database.
-
Il nome di un'operazione deve chiaramente indicare lo scopo pertinente. Non utilizzare nomi non specifici,
come getData, che non sono descrittivi del risultato che restituiscono. Utilizzare un nome che
indica precisamente l'evento previsto, come getAddress. Un esito migliore è assegnare semplicemente
all'operazione il nome della proprietà restituita o impostata. Se dispone di un parametro, ne imposta la
proprietà. Se non presenta alcun parametro, ne riceve la proprietà. Esempio: l'operazione address
restituisce l'indirizzo di un Cliente, mentre address(aString) imposta o modifica l'indirizzo
del Cliente. Le funzioni get (ricevi) e set (imposta) dell'operazione sono implicite
dalla firma dell'operazione.
-
Le operazioni che concettualmente sono uguali devono avere lo stesso nome anche se sono definite da diverse
classi, se vengono implementate in modi totalmente differenti o se dispongono di un diverso numero di
parametri. Ad esempio, un'operazione che crea un oggetto deve avere lo stesso nome in tutte le classi.
-
Se le operazioni presenti nelle svariate classi hanno la stessa firma, l'operazione deve restituire lo
stesso tipo di risultato appropriato per l'oggetto ricevitore. Questo è un esempio del concetto di
polimorfismo, in cui diversi oggetti dovrebbero rispondere allo stesso messaggio con modalità
analoghe. Esempio: l'operazione name deve restituire il nome dell'oggetto, indipendentemente da come
il nome è stato archiviato o derivato. Questo principio rende il modello più facile da comprendere.
-
Il tipo di restituzione - Il tipo di restituzione è la classe dell'oggetto restituita dall'operazione.
-
Una breve descrizione - Quanto più significativa possibile, il nome dell'operazione è spesso solo vagamente
utile quando si tenta di comprendere l'utilizzo dell'operazione. Fornire una breve descrizione dell'operazione
formata da un paio di frasi, scritta secondo la prospettiva dell'utente dell'operazione.
-
I parametri - Per ogni parametro creare un breve nome descrittivo, stabilirne la classe e fornire una breve
descrizione. Mentre si specificano i parametri, non dimenticare che pochi parametri significano un migliore
riutilizzo. Un numero ridotto di parametri rende più facile comprendere l'operazione e, pertanto, vi è una maggiore
probabilità di individuare operazioni simili. Potrebbe essere necessario dividere un'operazione con molti parametri
in diverse operazioni. L'operazione deve essere comprensibile per coloro che intendono utilizzarla. La breve
descrizione dovrebbe includere:
-
il significato dei parametri, se non evidente dai loro nomi
-
se il parametro è fornito per valore o per riferimento
-
i parametri che devono fornire i valori
-
i parametri che possono essere facoltativi e i loro valori predefiniti, se nessun valore è fornito
-
gli intervalli validi dei parametri, se disponibili
-
il funzionamento dell'operazione
-
quali parametri per riferimento vengono modificati dall'operazione
Una volta definite le operazioni, completare i diagrammi delle sequenze con le informazioni relative a quali operazioni
vengono richiamate per ciascun messaggio.
Fare riferimento alla sezione intitolata Linee guida per il prodotto di lavoro: Classe di progettazione per maggiori
informazioni.
Per ciascuna operazione, identificare la visibilità di esportazione dell'operazione da queste scelte:
-
Public - l'operazione è visibile agli elementi del modello all'infuori della classe stessa.
-
Implementation - l'operazione è visibile solo all'interno della classe stessa.
-
Protected - l'operazione è visibile solo alla classe stessa, alle relative classi secondarie oppure agli
associati (friends) della classe (dipendente dal linguaggio).
-
Private - l'operazione è visibile solo alla classe stessa e agli associati della classe
Scegliere la visibilità più riservata possibile che consente tuttavia di raggiungere gli obiettivi dell'operazione. Per
effettuare ciò, osservare i diagrammi sequenza e, per ciascun messaggio, determinare se il messaggio proviene da una
classe esterna al pacchetto del ricevitore (richiede visibilità public), dall'interno del pacchetto (richiede
visibilità implementation), da una classe secondaria (richiede visibilità protected oppure dalla classe
stessa o da un associato (richiede visibilità private).
Le operazioni sono, per la maggior parte, operazioni dell'istanza; cioè, vengono eseguite sulle istanze della classe.
In alcuni casi, tuttavia, un'operazione si applica a tutte le istanze della classe e, pertanto, è un'operazione
class-scope. Il ricevitore dell'operazione di classe è realmente un'istanza di un metaclass - la
descrizione della classe stessa - anziché una qualsiasi specifica istanza della classe. Esempi di operazioni di classe
includono messaggi che creano nuove istanze, le quali restituiscono il valore all Instances di una classe.
La stringa dell'operazione è sottolineata per indicare un'operazione class-scope.
|
Definizione dei metodi
Un metodo specifica l'implementazione di un'operazione. In molti casi dove il comportamento richiesto dall'operazione è
sufficientemente descritto dal nome dell'operazione, dalla descrizione e dai parametri, i metodi vengono direttamente
implementati nel linguaggio di programmazione. Laddove l'implementazione di un'operazione richiede l'utilizzo di un
algoritmo specifico o di ulteriori informazioni rispetto a quelle presentate nella descrizione dell'operazione, viene
richiesta una differente descrizione del metodo. Il metodo descrive il funzionamento dell'operazione e
non solo l'esito dell'esecuzione.
La finalità del metodo è di descrivere quanto segue:
-
l'implementazione delle operazioni
-
l'implementazione e l'utilizzo degli attributi per implementare le operazioni
-
l'implementazione e l'utilizzo delle relazionii per implementare le operazioni
I requisiti cambieranno da caso a caso, tuttavia, le specifiche del metodo per una classe dovrebbero sempre indicare:
-
cosa verrà fatto in relazione ai requisiti
-
quali altri oggetti e relative operazioni saranno utilizzati
Ulteriori specifici requisiti possono interessare:
-
come saranno implementati i parametri
-
quali speciali algoritmi saranno utilizzati se disponibili
I diagrammi delle sequenze sono una importante fonte per tale proposito. Dai diagrammi risulta chiaro di quali
operazioni saranno utilizzate in altri oggetti quando viene eseguita un'operazione. Una specifica che indica le
operazioni che saranno utilizzate in altri oggetti è fondamentale per la totale implementazione di un'operazione. La
realizzazione di una specifica del metodo completa richiede, pertanto, di identificare le operazioni per gli oggetti
coinvolti e di esaminare i diagrammi delle sequenze corrispondenti.
|
Definizione degli stati
Per alcune operazioni la relativa funzionalità dipende dallo stato in cui si trova l'oggetto ricevitore. Una macchina
di stato è uno strumento che descrive gli stati che un oggetto può assumere e gli eventi che provocano il passaggio di
un oggetto da uno stato all'altro (consultare Tecnica:
Diagramma di stato). Le macchine di stato sono più utili per la descrizione delle classi attive. L'utilizzo delle
macchine di stato è particolarmente importante per la definizione del comportamento di Prodotto di lavoro: Capsule.
Un esempio di una semplice macchina di stato è riportato in figura 2.
Figura 2: Un semplice diagramma di stato per un distributore di carburante
Ogni evento di transizione dello stato può essere associato ad un'operazione. A seconda dello stato dell'oggetto,
l'operazione potrebbe avere un diverso comportamento e gli eventi della transizione descrivono come ciò si verifica.
E' necessario aggiornare la descrizione del metodo per l'operazione associata con le informazioni specifiche
sullo stato, indicando la finalità dell'operazione per ogni relativo stato. Gli stati sono spesso rappresentati
utilizzando gli attributi; i diagrammi di stato servono come input nella fase di identificazione dell'attributo.
Per ulteriori informazioni, consultare Tecnica:
Diagramma di stato.
|
Definizione degli attributi
Durante la definizione dei metodi e l'identificazione degli stati, vengono definiti gli attributi
richiesti dalla classe per eseguire le relative operazioni. Gli attributi forniscono le informazioni per l'istanza
della classe e sono frequentemente utilizzati per rappresentare lo stato dell'istanza classe. Tutte le informazioni
della classe stessa vengono conservate mediante gli attributi associati. Per ciascun attributo, definire:
-
il relativo nome, che deve attenersi alle convenzioni di denominazione sia del linguaggio di implementazione
sia del progetto
-
il relativo tipo, che sarà un tipo di dati elementare supportato dal linguaggio di implementazione
-
il relativo valore predefinito o iniziale, a cui è inizializzato quando le nuove istanze della classe sono
create
-
la relativa visibilità, che assumerà uno dei seguenti valori:
-
Public: l'attributo è visibile sia internamente che esternamente al pacchetto contenente la classe
-
Protected: l'attributo è visibile solo alla classe stessa, alle relative sottoclassi oppure agli
associati della classe (dipendente dal linguaggio)
-
Private: l'attributo è visibile solo alla classe stessa e agli associati della classe
-
Implementation: l'attributo è visibile solo alla classe stessa
-
classi permanenti, se l'attributo è permanente (valore predefinito) o transitorio. Anche se la classe stessa
potrebbe essere permanente, non tutti gli attributi della classe devono essere permanenti.
Accertarsi che siano richiesti tutti gli attributi. Gli attributi devono essere giustificati - è facile per gli
attributi essere aggiunti in anticipo nel processo e durare a lungo anche quando non sono più richiesti. Gli attributi
aggiuntivi, moltiplicati per migliaia o milioni di istanze, possono avere un effetto dannoso sulle prestazioni e sui
requisiti di memoria di un sistema.
Per maggiori informazioni sugli attributi, fare riferimento alla sezione intitolata Attributi in Linee guida per il prodotto di lavoro: Classe di progettazione.
|
Definizione delle dipendenze
Per tutti i casi in cui è richiesta la comunicazione tra gli oggetti, effettuare le seguenti domande:
-
Il riferimento per il ricevitore è fornito come parametro all'operazione? In tal caso, stabilire una
dipendenza tra le classi mittente e ricevitore in un diagramma classe contenente le due classi. Inoltre, se
viene utilizzato il formato diagramma di comunicazione per le iterazioni, qualificare la visibilità di
collegamento e impostarla su parameter.
-
Il ricevitore è globale? In tal caso, stabilire una dipendenza tra le classi mittente e ricevitore in un
diagramma classe contenente le due classi. Inoltre, se viene utilizzato il formato diagramma di
comunicazione per le iterazioni, qualificare la visibilità di collegamento e impostarla su global.
-
Il ricevitore è un oggetto temporaneo creato e distrutto durante l'operazione stessa? In tal caso, stabilire una
dipendenza tra le classi mittente e ricevitore in un diagramma classe contenente le due classi. Inoltre, se
viene utilizzato il formato diagramma di comunicazione per le iterazioni, qualificare la visibilità di
collegamento e impostarla su local.
Si noti che i collegamenti modellati in questo modo sono transitori, presenti solo per una durata limitata nello
specifico contesto della collaborazione; in tal senso, essi sono istanze del ruolo di associazione nella
collaborazione. Tuttavia, la relazione in un modello di classe (cioè, indipendente dal contesto) deve essere una
dipendenza, come indicato precedentemente. Come specificato in [RUM98], nella
definizione del collegamento transitorio: "E' possibile modellare tutti i collegamenti come associazioni, ma poi
le condizioni sulle associazioni devono essere specificate molto ampiamente e perdono molto della loro precisione nel
vincolare la combinazioni di oggetti." In questa situazione, il modellamento di una dipendenza è meno importante di
quello della relazione nella collaborazione, poiché la dipendenza non descrive la relazione completamente, soltanto che
esiste.
|
Definizione delle associazioni
Le associazioni forniscono il meccanismo di comunicazione tra oggetti. Gli oggetti sono forniti di un passaggio
attraverso il quale i messaggi possono fluire. Inoltre, le associazioni documentano le dipendenze tra classi,
sottolineando che le modifiche apportate a una classe potrebbero essere trasmesse a molte altre classi.
Esaminare le descrizioni del metodo per ciascuna operazione per comprendere in che modo le istanze della classe
comunicano e collaborano con altri oggetti. Per inviare un messaggio ad un altro oggetto, l'oggetto deve avere un
riferimento per il ricevitore del messaggio. Un diagramma di comunicazione (una rappresentazione alternativa di un
diagramma sequenza) mostrerà la comunicazione oggetto in termini di collegamenti, come illustrato nella figura 3.
Figura 3: un esempio di diagramma di trasmissione
I restanti messaggi impiegano l'associazione oppure l'aggregazione per specificare la relazione tra le
istanze di due classi che comunicano. Consultare Tecnica:
Associazione e Tecnica: Aggregazione per informazioni sulla scelta della rappresentazione
appropriata. Per entrambe le associazioni, impostare la visibilità di collegamento su field nei diagrammi di
comunicazione. Le altre attività includono:
-
Stabilire la navigabilità delle associazioni e aggregazioni. Ciò è possibile considerando le navigabilità richieste
nelle creazioni di istanze collegate nei diagrammi di interazione. Poiché la navigabilità assume il valore
true per impostazione predefinita, è necessario soltanto individuare le associazioni (e aggregazioni) in cui
tutti gli opposti ruoli di collegamento di tutti gli oggetti di una classe nell'associazione non necessitano di una
navigabilità. In quei casi, impostare la navigabilità su false nel ruolo della classe.
-
Se sono presenti degli attributi sull'associazione stessa (rappresentati dalle classi di associazione), creare una
classe di progettazione per rappresentare la classe di associazione con gli attributi appropriati. Interporre
questa classe tra le altre due classi e stabilire le associazioni con la molteplicità appropriata tra la classe di
associazione e le altre due classi.
-
Specificare se i fine associazione devono essere ordinati o meno; ciò è il caso quando gli oggetti
associati ad un oggetto all'altra estremità dell'associazione ha un ordine che deve essere conservato.
-
Se la classe associata (oppure aggregata) è indicata solo dalla classe corrente, occorre stabilire se nidificare la
classe. I vantaggi ottenuti dalla nidificazione delle classi sono una più rapida messaggistica e un più semplice
modello di progettazione. Gli svantaggi includono l'assegnazione in modo statico dello spazio per la classe
nidificata indipendentemente da se esistono istanze della classe nidificata, una mancanza di identità dell'oggetto
diversa dalla classe di chiusura oppure un'incapacità di specificare le istanze della classe nidificata
dall'esterno della classe di chiusura.
Le associazioni ed aggregazioni sono meglio definite in un diagramma di classe che descrive le classi associate. Il
diagramma di classe deve essere di proprietà dal pacchetto che contiene le classi associate. La figura 4
illustra un esempio di un diagramma di classe, che descrive le associazioni e aggregazioni.
Figura 4: esempio di un diagramma di classe che illustra le associazioni, aggregazioni e generalizzazioni tra le classi
Si utilizzano le associazioni di sottoscrizione tra le classi di analisi per identificare le dipendenze di
eventi tra le classi. Nel modello di progettazione è necessario gestire queste dipendenze di eventi in modo esplicito,
utilizzando dei framework event-handler disponibili oppure progettando e realizzando il proprio framework
event-handler. In alcuni linguaggi di programmazione, come il Visual Basic, ciò è lineare; si dichiarano, elevano e
gestiscono gli eventi corrispondenti. In altri linguaggi, potrebbe essere necessario utilizzare delle librerie
aggiuntive di funzioni riutilizzabili per gestire le sottoscrizioni e gli eventi. Se non è possibile acquistare la
funzionalità, occorre progettarla e realizzarla. Vedere anche Tecnica:
Associazione di sottoscrizione.
|
Definizione della struttura interna
Alcune classi rappresentano astrazioni complesse con strutture complesse. Durante il modellamento di una classe, il
progettista potrebbe rappresentare gli elementi interni di partecipazione e le relative relazioni, per accertarsi che
il responsabile dell'implementazione realizzi di conseguenza le collaborazioni che si creano all'interno di quella
classe.
In UML 2.0, le classi sono definite come classi
strutturate, con la capacità di avere una struttura interna e le porte. Quindi, le classi vengono scomposte in
raccolte di parti collegate che possono essere ulteriormente scomposte. Una classe può essere incapsulata forzando le
comunicazioni dall'esterno di passare attraverso le porte che sono gestite dalle interfacce dichiarate.
Quando si individua una classe complessa con una struttura complessa, creare un diagramma di struttura composita per
quella classe. Modellare le parti che eseguiranno i ruoli per quella funzionalità di classe. Stabilire il modo in cui
le parti vengono 'collegate' utilizzando i connettori. Utilizzare le porte con interfacce dichiarate se si vuole
consentire a diversi client di quella classe di accedere a parti specifiche della funzionalità offerta da quella
classe. Inoltre, utilizzare le porte per isolare completamente le parti interne di quella classe dal relativo ambiente.
Per ulteriori informazioni su questo argomento ed esempi del diagramma di struttura composita, consultare Concetto: classe strutturata.
|
Definizione delle generalizzazioni
Le classi potrebbero essere organizzate in una gerarchia di generalizzazione per rispecchiare la funzionalità e
struttura comuni. E' possibile definire una superclasse comune, da cui le sottoclassi possono ereditare
sia la funzionalità che la struttura. La generalizzazione è una convenienza notazionalee che consente di definire una
struttura e funzionalità comuni in un'unica ubicazione e di riutilizzarle quando si individuano funzionalità e
struttura ripetute. Consultare la sezione Tecnica:
Generalizzazione per ulteriori informazioni sulle relazioni di generalizzazione.
Quando si individua una generalizzazione, creare una superclasse comune per contenere gli attributi, le associazioni,
le aggregazioni e le operazioni comuni. Eliminare la funzionalità comune dalle classi che diventeranno le sottoclassi
della superclasse comune. Definire una relazione di generalizzazione dalla sottoclasse per la superclasse.
|
Risoluzione delle collisioni del caso d'uso
Lo scopo di questa fase è di evitare conflitti di simultaneità causati quando due o più casi d'uso accedono alle
istanze della classe di progettazione contemporaneamente, in modi contrastanti.
Una delle difficoltà che si incontra procedendo caso d'uso per caso d'uso attraverso il processo di progettazione è
quella che due o più casi di utilizzo potrebbero tentare di richiamare le operazioni contemporaneamente sugli oggetti
di progettazione in modo potenzialmente di conflitto. In questi casi, i conflitti di concorrenza devono essere
assolutamente identificati e risolti.
Se si utilizza la messaggistica sincrona, l'esecuzione di un'operazione bloccherà tutte le successive chiamate verso
gli oggetti fino a quando l'operazione non è completata. La messaggistica sincrona implica un ordine di tipo first-come
(primo arrivato) first-served (primo servito) all'elaborazione dei messaggi. Ciò potrebbe risolvere il conflitto di
concorrenza, specialmente nei casi in cui tutti i messaggi hanno la stessa priorità o ciascun messaggio viene eseguito
all'interno dello stesso thread di esecuzione. Laddove si potrebbe accedere ad un oggetto da diversi thread di
esecuzione (rappresentati da classi attive), è necessario utilizzare dei meccanismi espliciti per impedire o risolvere
il conflitto di concorrenza.
Nei sistemi in tempo reale in cui i thread sono rappresentati da Prodotto di
lavoro: Capsule, questo problema non è stato ancora risolto per molteplici accessi simultanei ad oggetti passivi,
laddove le capsule stesse forniscono un meccanismo di coda e applicano le semantiche di esecuzione fino al
completamento per gestire l'accesso simultaneo. Una soluzione consigliata è di incapsulare gli oggetti passivi
all'interno delle capsule, evitando il problema dell'accesso simultaneo attraverso le semantiche della capsula stessa.
Potrebbe essere possibile richiamare contemporaneamente diverse operazioni sullo stesso oggetto da diversi thread di
esecuzione senza avere un conflitto di concorrenza; sia il nome che l'indirizzo di un cliente possono essere modificati
contemporaneamente senza conflitto. Solo quando due diversi thread di esecuzione tentano di modificare la stessa
proprietà dell'oggetto che si verifica un conflitto.
Per ogni oggetto che si potrebbe accedere contemporaneamente da diversi thread di esecuzione, identificare le sezioni
di codice che devono essere protette da accesso simultaneo. Nella fase iniziale dell'elaborazione, sarà impossibile
identificare specifici segmenti di codice; le operazioni che devono essere protette saranno sufficienti.
Successivamente, selezionare o progettare dei meccanismi appropriati di controllo accessi per impedire accessi
simultanei in conflitto. Degli esempi di questi meccanismi includono l'accodamento dei messaggi e la serializzazione
degli accessi, l'utilizzo di semafori o di token per consentire l'accesso a un thread alla volta oppure altre varianti
dei meccanismi di blocco. La scelta del meccanismo tende ad essere estremamente dipendente dall'implementazione e varia
tipicamente con il linguaggio di programmazione e l'ambiente operativo. Consultare la sezione Linee guida specifiche del progetto per una guida sulla scelta dei
meccanismi di simultaneità.
|
Gestione dei requisiti non funzionali in generale
Le classi di progettazione sono perfezionate per gestire requisiti generali non funzionali. Un input importante per
questa fase include i requisiti non funzionali su una classe di analisi che potrebbero già essere stati specificati nei
requisiti e nelle responsabilità speciali della classe. I requisiti di questo tipo sono spesso specificati in termini
di quali meccanismi strutturali (analisi) sono richiesti per realizzare la classe; in questa fase, la classe è quindi
perfezionata per integrare i meccanismi di progettazione corrispondenti a questi meccanismi di analisi.
I meccanismi di progettazione disponibili sono identificati e caratterizzati dall'architettura software. Per ciascun
meccanismo di progettazione richiesto, qualificare quante più caratteristiche possibili, fornendo dei valori di
intervalli dove opportuno. Fare riferimento alle sezioni Compito: Identificazione dei meccanismi di progettazione, Concetto: Meccanismi di analisi e Concetto: Meccanismi di progettazione e di implementazione per maggiori informazioni
sui meccanismi di progettazione.
Possono esistere diversi meccanismi e linee guida di progettazione generali che devono essere presi in considerazione
quando vengono progettate le classi, come in che modo:
-
utilizzare prodotti e componenti esistenti
-
adattarsi al linguaggio di programmazione
-
distribuire gli oggetti
-
raggiungere prestazioni accettabili
-
raggiungere determinati livelli di sicurezza
-
gestire gli errori
|
Valutazione dei propri risultati
In questa fase controllare il modello di progettazione per accertarsi che il lavoro svolto sia rivolto nella direzione
giusta. Non occorre revisionare in dettaglio il modello, ma si devono considerare i seguenti elenchi di controllo:
|
|