Operazione: Identificazione elemento progettazione
Questo compito descrive come identificare sottosistemi, classi, interfacce, eventi e segnali.
Discipline: Analisi e progettazione
Scopo
  • Analisi delle iterazioni delle classi di analisi per identificare gli elementi del modello di progettazione
Relazioni
Descrizione principale

IL Compito: Analisi del caso d'uso si traduce in classi di analisi, che rappresentano cose concettuali che possono eseguire comportamenti. In progettazione, le classi di analisi evolvono in svariati tipi di elementi di progettazione:

  • classi, per rappresentare una serie di responsabilità piuttosto dettagliate;
  • sottosistemi, per rappresentare una serie meno dettagliata di responsabilità, composti da un'ulteriore serie di sottosistemi, ma in ultima analisi anche da una serie di classi;
  • classi attive, che rappresentano thread nel sistema;
  • interfacce, che rappresentano dichiarazioni astratte di responsabilità fornite da una classe o sottosistema.

In oltre nella progettazione si devono identificare:

  • eventi, che sono specifiche di ricorrenze interessanti in spazio e tempo che solitamente (se sono ragguardevoli) richiedono una risposta dal sistema; e
  • segnali, per rappresentare meccanismi asincroni utilizzati per comunicare con determinati tipi di eventi all'interno del sistema.

Questa sottile distinzione ci permette di esaminare aspetti diversi della progettazione:

  • Gli eventi ed i segnali utilizzati per comunicarli, ci permettono di descrivere i trigger asincroni di comportamento a cui il sistema deve rispondere.
  • Le classi ed i sottosistemi consentono di raggruppare responsabilità collegate in unità che si potranno sviluppare in modo relativamente indipendente; le classi adempiono ad un insieme atomico di responsabilità correlate, mentre i sottosistemi sono blocchi di costruzione compositi che vengono rispettivamente composti di classi o altri sottosistemi. I sottosistemi sono utilizzati per rappresentare i prodotti del lavoro di un team di sviluppo come una singola unità integrale di funzionalità, e come tale vengono usati sia come unità di controllo e gestione configurazione che come elementi di progettazione logica.
  • Le classi attive vengono utilizzate per rappresentare thread di controllo nel sistema, consentendo di modellare la simultaneità. Le classi attive sono spesso utilizzate insieme ad altre classi solitamente, ma non necessariamente, passive: tale composizione di classi può quindi essere utilizzata - alla stregua di una collaborazione - per modellare comportamenti complessi.

    Nei sistemi in tempo reale, vengono utilizzate le capsule al posto delle classi attive. Queste offrono una semantica più solida per semplificare la progettazione ed aumentare l'affidabilità delle applicazioni simultanee. Le capsule condividono alcuni aspetti sia delle classi che dei sottosistemi: sono infatti collaborazioni incapsulate di classi che insieme rappresentano un thread di controllo nel sistema. Si differenziano dai sottosistemi perché una capsula è di responsabilità di un singolo sviluppatore, mentre un sottosistema è di responsabilità (solitamente) di un team di sviluppatori; un sottosistema può tuttavia contenere capsule.  

  • Le interfacce consentono di esaminare e catturare i 'punti di giunzione' del sistema, definendo in modo preciso come interagiscono le parti che lo costituiscono.
  • Nei sistemi in tempo reale si devono utilizzare i Protocolli per definire in modo preciso i messaggi che potranno essere inviati e ricevuti su una porta di una capsula.

Separando le preoccupazioni e gestendo ciascun problema rappresentato da questi concetti in modo separato, si semplifica il processo di progettazione e si chiarificano le soluzioni.

Se è necessario mantenere la tracciabilità tra modelli di sistema, lo si deve documentare durante questo compito. Per maggiori informazioni sulla documentazione della tracciabilità tra modelli di progettazione e altri modelli di sistema, consultare Guida: Modelli di progettazione.

 Rappresentazione UML 1.x

In UML 1.5, un sottosistema è un tipo particolare di pacchetto che contiene unicamente interfacce come elementi pubblici. Le interfacce contengono un livello di incapsulamento che consente al progetto interno del sottosistema di restare nascosto agli altri elementi del modello. Il sottosistema di concetto è utilizzato per distinguerlo dai pacchetti "ordinari", che sono contenitori di elementi di modello senza semantica; il sottosistema rappresenta un utilizzo particolare con proprietà simili alle classi (dal punto di vista comportamentale).

Nel RUP, le capsule sono rappresentate utilizzando la notazione UML 1.5. Molte di queste possono essere rappresentate in UML 2.0 utilizzando Concetto: Classi strutturate.

Fare riferimento a Differenze tra UML 1.x ed UML 2.0 per ulteriori informazioni.

Passi
Identificazione di eventi e segnali
Scopo Identificare eventi e segnali interni ed esterni a cui il sistema deve rispondere.  

Gli eventi sono delle ricorrenze interne ed esterne che causano delle azioni nel sistema. Gli eventi e le loro caratteristiche possono aiutare nell'identificazione di elementi di progettazione chiave, quali le classi attive.

Un elenco iniziale di eventi esterni può essere ottenuto dal modello caso d'uso, dalle interazioni dell'esecutore con i casi d'uso. Gli eventi interni possono essere ottenuti dal testo contenuto nel flusso del caso d'uso, o possono essere identificati durante l'evolversi del progetto.

Le caratteristiche fondamentali degli eventi sono:

  • interno o esterno - l'evento è interno o esterno?
  • priorità - l'evento necessita dell'arresto di altri processi per essere gestito?
  • frequenza - quanto spesso si verifica l'evento?
  • distribuzione della frequenza - l'evento si verifica ad intervalli regolari o ci sono dei picchi?
  • requisiti di risposta - la velocità con cui il sistema deve rispondere all'evento (potrebbe essere necessario distinguere tra la media ed il caso peggiore).
  • tipo - si tratta di un evento di chiamata, evento di tempo, evento di segnale, o evento di modifica (consultare Concetto: Eventi e segnali per le definizioni)?

Le caratteristiche degli eventi devono essere catturate per portare all'identificazione degli elementi di progettazione che li gestiscono. La cattura degli eventi è fondamentale nei sistemi reattivi (basato sugli eventi), ma può essere utile anche in altri sistemi, quali quelli con messaggistica simultanea e/o asincrona.

Gli eventi di comunicazione asincroni in generale, possono essere modellati come segnali per esprimere i dati che trasportano, o per esprimere le relazioni tra segnali. In alcuni sistemi, quali quelli reattivi è importante collegare i segnali ricevuti da unità esterne a determinati meccanismi, come interrupt o messaggi di polling specifici.

Identificazione di classi, classi attive e sottosistemi
Scopo Affinare le classi di analisi negli appropriati elementi di modello di progettazione 

Identificazione delle classi. Quando la classe di analisi è semplice e rappresenta già un astrazione logica singola, può essere associata in modo diretto, 1:1, in una classe di progettazione. Solitamente, le classi entità restano relativamente intatte all'interno di un progetto. Siccome le classi entità sono persistenti, determinare se la classe di progettazione deve essere persistente ed annotarlo nella sua descrizione.

Quando si identificano le classi queste devono essere raggruppate in Prodotto di lavoro: Pacchetti di progettazione, per scopi organizzativi e di gestione configurazione. Consultare Linea guida del prodotto di lavoro: Pacchetto di progettazione per ulteriori informazioni su come effettuare le scelte di impacchettamento.

Identificazione delle classi attive. Considerare i requisiti di simultaneità del sistema nel contesto dell'oggetto di analisi identificato: è necessario che il sistema risponda agli eventi generati dall'esterno? e se sì, quali classi di analisi sono 'attive' quando si verifica l'evento? Gli eventi esterni in un modello del caso d'uso sono rappresentati da emissioni provenienti da esecutori che interagiscono con il caso d'uso. Studiare le realizzazioni del caso d'uso corrispondenti per controllare quali oggetti interagiscono quando si verifica l'evento. Iniziare raggruppando gli oggetti in insiemi autonomi che collaborano - questi raggruppamenti rappresentano una selezione iniziale ad un gruppo che potrà in seguito andare a formare una classe attiva composita.

Se gli eventi hanno attributi importanti che devono essere catturati, considerare la possibilità di modellarli in classi, <<segnali>> stereotipati. Nei sistemi in tempo reale, questi insiemi di oggetti identificati devono essere raggruppati in capsule, che hanno una forte semantica di incapsulamento.

Le istanze di classi attive rappresentano thread 'logici' indipendenti di esecuzione. Questi thread 'logici' di esecuzione non devono essere confusi o associati con i thread di esecuzione del sistema operativo (per quanto ad un certo punto verranno associati con essi). Al contrario, essi rappresentano thread di esecuzione concettualmente indipendenti nell'ambito delle soluzioni. Lo scopo di identificarli in questa fase della progettazione è quello di poter separare la soluzione in unità indipendenti basate sui 'punti di giunzione di simultaneità' naturali nel sistema. Questo modo di dividere il lavoro rende la gestione della simultaneità concettualmente più semplice, poiché i thread indipendenti di esecuzione potranno essere considerati separatamente tranne che per il fatto che condividono le sottostanti classi passive.

In generale si dovrebbe prendere in considerazione l'utilizzo di una classe attiva ogni volta vi siano simultaneità e conflitti di simultaneità nel dominio del problema. Una classe attiva deve essere utilizzata per rappresentare oggetti simultanei esterni o attività simultanee interne al computer. Questo consente il controllo delle attività simultanee.

Un'altra possibilità è quella di utilizzare le classi attive come rappresentazione interna di periferiche fisiche esterne connesse al computer, siccome queste entità fisiche sono simultanee in modo inerente. Queste classi, che fungono da "driver di periferica", non servono unicamente per controllare la periferica corrispondente, ma anche per isolare il sistema dalle sue specifiche. Ciò significa che il sistema potrebbe non essere influenzato anche se la tecnologia della periferica dovesse evolvere.

Un altro esempio classico di utilizzo delle classi attive consiste nella rappresentazione di attività simultanee logiche. Un'attività logica rappresenta un "oggetto" simultaneo concettuale, quale, una transazione finanziaria o una telefonata. Per quanto esse non si manifestino direttamente come entità fisiche (anche se si verificano in un mondo fisico), vi sono spesso ragioni per gestirle come tali. Ad esempio potremmo dover trattenere una determinata transazione finanziaria per evitare un conflitto di simultaneità oppure potrebbe essere necessario interromperla a causa di errori nel sistema. Siccome questi oggetti concettuali devono essere gestiti come unità, è utile rappresentarli come oggetti con interfacce proprie che forniscono le funzionalità appropriate.

Un esempio particolare di tale tipo di oggetto concettuale è l'unità di controllo dell'oggetto attivo. Il suo ruolo è quello di gestire in modo continuo uno o più oggetti attivi. Questo compito normalmente consiste nel portare un oggetto ad un determinato stato operativo e nel mantenerlo in tale stato anche in presenza di errori parziali, e sincronizzando le sue operazioni con quelle di altri oggetti. Queste unità di controllo degli oggetti attivi spesso evolvono partendo da oggetti di controllo identificati durante Compito: Analisi del caso d'uso.

Grazie alla loro capacità di risolvere semplicemente i conflitti di simultaneità, le classi attive tornano utili anche come guardiani delle risorse condivise. In questo caso, una o più risorse richieste da più compiti concorrenti vengono incapsulate in una classe attiva. In virtù della loro semantica di esclusione reciproca incorporata, tali guardiani proteggono automaticamente queste risorse dai conflitti di simultaneità.

Nei sistemi in tempo reale, si devono utilizzare le capsule al posto delle classi attive: ovunque si richieda una classe attiva, come mostrato nelle simulazioni precedenti, è necessario sostituirle con le capsule.

Identificazione dei sottosistemi. Quando la classe di analisi è più complessa, al punto da contenere comportamenti che non possono essere responsabilità di una classe che agisca da sola, deve essere associata ad un sottosistema di progettazione. Il sottosistema di progettazione viene utilizzato per incapsulare queste collaborazioni in modo che i clienti del sottosistema possano rimane allo scuro della sua progettazione interna.

Un sottosistema viene modellato come un componente UML, che ha come elementi pubblici le sole interfacce. Le interfacce contengono un livello di incapsulamento che consente al progetto interno del sottosistema di restare nascosto agli altri elementi del modello. Il sottosistema di concetto è utilizzato per distinguerlo dai pacchetti, che sono contenitori di elementi di modello senza semantica.

La decisione di creare un sottosistema da una serie di classi di analisi che collaborano, viene basata sulla possibilità che la collaborazione possa o venga sviluppata in modo indipendente da un team di progettazione separato. Se la collaborazione può essere completamente contenuta in un pacchetto assieme alle classi di collaborazione, un sottosistema fornisce una maggiore forma di incapsulamento rispetto a quella fornita da un semplice pacchetto. I contenuti e le collaborazioni interne ad un sottosistema sono completamente isolate dietro una o più interfacce, così che il cliente del sottosistema dipende solo dalle interfacce. Il progettista del sottosistema non dovrà quindi preoccuparsi delle dipendenze esterne; egli (o il team di progettazione) deve definire come viene realizzata l'interfaccia, ma sarà libero di modificare la progettazione del sottosistema interno senza influenzare le dipendenze esterne. Nei grandi sistemi con team largamente indipendenti, questo grado di separazione combinato con le migliorie architetturali fornite dalle normali interfacce, rappresenta un solido argomento per la scelta dei sottosistemi rispetto ai semplici pacchetti. Consultare Linea guida del prodotto di lavoro: Sottosistema di progettazione per ulteriori informazioni sui fattori che influenzano la scelta di utilizzare i sottosistemi come elementi di progettazione.

Identificazione delle interfacce di sottosistema
Scopo Identificazione degli elementi di progettazione che formano i punti di giunzione nel sistema. 

Le interfacce definiscono una serie di operazioni che vengono realizzate da alcuni programmi di classificazione. Nel modello di progettazione le interfacce sono utilizzate principalmente per definire le interfacce per i sottosistemi. Questo non significa che non possono essere utilizzate anche per le classi, ma per le classi singole è solitamente sufficiente definire delle operazioni pubbliche sulla classe che definisce l'interfaccia. Le interfacce sono importanti per i sottosistemi poiché consentono la separazione delle dichiarazioni di comportamento (l'interfaccia) dalla sua realizzazione (le classi specifiche interne al sottosistema che realizzano l'interfaccia). Questa separazione consente di incrementare l'indipendenza dei team di sviluppo che lavorano su parti diverse del sistema, mantenendo però definizioni precise dei 'contratti' tra le diverse parti.

Per ciascun sottosistema identificare una serie di possibili interfacce. Utilizzando le collaborazioni raggruppate create nel passo precedente, identificare la responsabilità che viene 'attivata' quando si avvia la collaborazione. Questa viene quindi raffinata determinando quali informazioni devono essere fornite dal 'cliente' e quali devono essere restituite al termine della collaborazione; questa serie di informazioni diventano il prototipo dei parametri di input, output e dei valori restituiti per l'operazione che il sistema realizzerà. Definire il nome di questa operazione, utilizzando le convenzioni di denominazione definite in Prodotto di lavoro: Linee guida specifiche del progetto. Ripetere finché tutte le operazioni realizzate dal sottosistema non saranno definite.

Raggruppare poi tutte le operazioni sulla base delle loro responsabilità. Sono preferibili gruppi piccoli, siccome è più probabile ottenere una serie aderente di responsabilità comuni se le operazioni nel gruppo sono minori. Tenere sempre presente il riutilizzo - creare delle similitudini che possano rendere più semplice l'identificazione di funzionalità riutilizzabili collegate tra loro. Allo stesso tempo, però, è bene non dedicare troppo tempo alla ricerca del raggruppamento di responsabilità ideale; ricordarsi che si tratta di un primo raggruppamento e che il raffinamento proseguirà in modo iterativo durante tutta la fase di elaborazione.

Ricerca delle similitudini tra le interfacce. Nell'insieme di interfacce candidate, cercare nomi simili, responsabilità simili ed operazioni simili. Nel caso vi siano operazioni uguali in più interfacce, riorganizzarle, estraendo le operazioni comuni ed inserendole in una nuova interfaccia. Considerare anche le interfacce esistenti, riutilizzandole dove possibile. Lo scopo è quello di mantenere la coesione delle interfacce rimuovendo le operazioni ridondanti. Ciò le renderà più semplici da comprendere e ne faciliterà l'evoluzione nel tempo.

Definizione delle dipendenze dell'interfaccia. I parametri ed i valori restituiti da ciascuna operazione dell'interfaccia sono di diversi tipi: essi devono realizzare una particolare interfaccia, oppure devono essere istanze di un semplice tipo di dati. Nel caso in cui i parametri siano oggetti che realizzano una determinata interfaccia, si devono definire le relazioni di dipendenza tra di essa e l'interfaccia da cui essa dipende. La definizione delle dipendenze fornisce delle informazioni utili per l'accoppiamento all'Architetto del software, siccome le dipendenze tra le interfacce determinano le dipendenze primarie tra gli elementi del modello di progettazione.

Associazione delle interfacce ai sottosistemi. Una volta identificate le interfacce, creare delle associazioni di realizzazione tra il sottosistema e l'interfaccia che esso realizza. Una realizzazione da un sottosistema ad un interfaccia indica che esistono uno o più elementi all'interno del sottosistema che realizzano le operazioni dell'interfaccia. Successivamente, quando il sottosistema verrà progettato, verrà raffinata la realizzazione dell'interfaccia di sottosistema, il progettista del sottosistema definirà quali elementi specifici realizzeranno le operazioni dell'interfaccia. Queste realizzazioni raffinate sono visibili solo al progettista del sottosistema; al cliente del sottosistema saranno visibili unicamente le realizzazioni delle sue interfacce.

Definizione dei comportamenti specificati dalle interfacce. Le interfacce spesso definiscono una macchina a stati implicita per gli elementi che le realizzano. Se le operazioni devono essere richiamate in un ordine specifico (ad esempio, è necessario avviare una connessione al database prima di utilizzarlo), si deve definire una macchina a stati che illustri gli stati visibili (o intuibili) pubblicamente che ciascun elemento di progettazione che realizza l'interfaccia deve supportare. Questa macchina a stati aiuta l'utente di un interfaccia a comprenderla con più semplicità, ed aiuterà il progettista degli elementi che realizzano l'interfaccia a fornire loro il comportamento corretto.

Impacchettamento delle interfacce. Le interfacce sono in possesso dell'Architetto del software; la loro modifica è sempre rilevante dal punto di vista architetturale. Per gestire questa eventualità, esse devono essere raggruppate in uno o più pacchetti in possesso dell'Architetto del software. Se ciascuna interfaccia è realizzata da un singolo sottosistema, la si può impacchettare assieme ad esso. Se invece è realizzata da più sottosistemi, deve essere impacchettata in pacchetti separati in possesso dell'Architetto del software. Ciò consente di gestirla e controllarla indipendentemente dal sottosistema.

Identificazione dei protocolli delle capsule

Scopo

Identificazione degli elementi di progettazione che formano i punti di giunzione nel sistema (solo per la progettazione del RT).

I protocolli sono simili alle interfacce nei sistemi basati sugli eventi: identificano il 'contratto' tra le capsule definendo una serie corrispondente di segnali utilizzati per le comunicazioni tra thread di controllo indipendenti. Mentre le interfacce sono utilizzate principalmente per la definizione di una messaggistica sincrona utilizzando un modello di richiamo a chiamata di funzione, i protocolli sono utilizzati per la definizione di comunicazioni asincrone utilizzando una messaggistica basata su segnali. Questi consentono una separazione tra la dichiarazione di comportamento (l'insieme dei segnali) e la sua realizzazione (gli elementi del sottosistema che realizzano l'interfaccia). Questa separazione consente di incrementare l'indipendenza dei team di sviluppo che lavorano su parti diverse del sistema, mantenendo però definizioni precise dei 'contratti' tra le diverse parti.

Per ciascuna capsula identificare una serie di segnali in ingresso ed uscita. Utilizzando le collaborazioni raggruppate create nei precedenti passi, identificare la responsabilità che viene 'attivata' quando si avvia la collaborazione. Questa viene quindi raffinata determinando quali informazioni devono essere fornite dal 'cliente' e quali devono essere restituite al termine della collaborazione; questa serie di informazioni diventano il prototipo dei parametri di input per un segnale che la capsula realizzerà tramite una delle sue porte. Definire il nome di questo segnale, utilizzando le convenzioni di denominazione definite in Prodotto di lavoro: Linee guida specifiche del progetto. Ripetere finché tutti i segnali realizzati dalla capsula non saranno definiti.

Raggruppare poi tutti i segnali sulla base delle loro responsabilità. Sono preferibili gruppi piccoli, siccome è più probabile ottenere una serie aderente di responsabilità comuni se i segnali nel gruppo sono minori. Tenere sempre presente il riutilizzo - creare delle similitudini che possano rendere più semplice l'identificazione di funzionalità riutilizzabili collegate tra loro. Allo stesso tempo, però, è bene non dedicare troppo tempo alla ricerca del raggruppamento di responsabilità ideale; ricordarsi che si tratta di un primo raggruppamento e che il raffinamento proseguirà in modo iterativo durante tutta la fase di elaborazione. Assegnare al protocollo un nome significativo che descriva il suo ruolo nella collaborazione della capsula.

Ricerca delle similitudini tra i protocolli. Nell'insieme di protocolli candidati, cercare nomi simili, responsabilità simili e segnali simili. Nel caso vi siano segnali uguali in più protocolli, riorganizzarli, estraendo i segnali comuni ed inserendoli in una nuova interfaccia. Considerare anche i protocolli esistenti, riutilizzandoli dove possibile. Lo scopo è quello di mantenere la coesione dei protocolli rimuovendo le operazioni ridondanti. Ciò li renderà più semplici da comprendere e ne faciliterà l'evoluzione nel tempo.

Associazione dei protocolli alle capsule. Una volta identificati i protocolli, creare le porte sulle capsule che li realizzano. Le porte delle capsule ne definiscono le 'interfacce', il comportamento che può essere richiesto dalla capsula. Successivamente, una volta progettata la capsula, il comportamento specificato dalle porte verrà descritto dalla sua macchina a stati.

Definizione dei comportamenti specificati dai protocolli. I protocolli spesso definiscono una macchina a stati implicita per gli elementi che le realizzano. Se i segnali di input devono essere ricevuti in un ordine specifico (ad esempio, un segnale 'sistema-pronto' deve essere ricevuto prima di un determinato segnale di errore), si deve definire una macchina a stati che illustri gli stati visibili (o intuibili) pubblicamente che ciascun elemento di progettazione che realizza il protocollo deve supportare. Questa macchina a stati aiuta l'utente della capsula che realizza il protocollo a comprenderne meglio il comportamento, ed aiuterà il progettista delle capsule a fornire loro il comportamento corretto.

Impacchettamento dei protocolli. I protocolli sono in possesso dell'Architetto del software; la loro modifica è sempre rilevante dal punto di vista architetturale. Per gestire questa eventualità, essi devono essere raggruppati in uno o più pacchetti in possesso dell'Architetto del software. In questo modo sarà possibile gestire e controllare i protocolli indipendentemente dalle capsule che li realizzano.


Ulteriori informazioni