Operazione: Progettazione della classe
Questa attività definisce in che modo progettare la struttura della classe di un sottosistema o componente.
Scopo
  • Per accertarsi che la classe fornisca il comportamento richiesto dalle realizzazioni del caso d'uso.
  • Per accertarsi che vengano fornite sufficienti informazioni per implementare in modo determinato la classe
  • Per gestire requisiti non funzionali relativi alla classe
  • Per integrare i meccanismi di progettazione utilizzati dalla classe
Relazioni
Descrizione principale

Le classi sono i punti di forza dell'impegno progettuale: eseguono effettivamente il lavoro reale del sistema. Altri elementi di progettazione, quali i sottosistemi, i pacchetti e le collaborazioni, descrivono in che modo le classi sono raggruppate o come interoperano.

Le capsule sono anche classi stereotipe, utilizzate per rappresentare thread simultanei di esecuzione in sistemi in tempo reale. In questi casi, le altre classi di progettazione sono classi passive, utilizzate all'interno del contesto di esecuzione fornito dalle capsule attive. Quando l'architetto di software e il progettista decidono di non utilizzare una strategia di progettazione basata sulle capsule, è ancora possibile modellare il comportamento simultaneo utilizzando le classi attive.

Le classi attive sono classi di progettazione che coordinano e gestiscono il comportamento delle classi passive - una classe attiva è una classe le cui istanze sono oggetti attivi, che possiedono il proprio thread di controllo.

Passi
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.

Progettazione di classi boundary

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.

Progettazione di classi entità

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.

Progettazione delle classi di controllo

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

Identificazione 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.

Diagramma descritto nel testo di accompagnamento.

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.

Denominazione e descrizione delle operazioni

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.

Definizione della visibilità dell'operazione

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).

Definizione delle operazioni di classe

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.

Diagramma descritto nel testo di accompagnamento.

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.

Diagramma descritto nel testo di accompagnamento.

Figura 3: un esempio di diagramma di trasmissione

Definizione delle associazioni e aggregazioni

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.

Diagramma descritto nel testo di accompagnamento.

Figura 4: esempio di un diagramma di classe che illustra le associazioni, aggregazioni e generalizzazioni tra le classi

Gestione delle associazioni di sottoscrizione tra le classi di analisi

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:



Proprietà
Ricorrenze multiple
Attivato da evento
In corso
Facoltativo
Pianificato
Ripetibile
Ulteriori informazioni