Linea guida: Pacchetto di progettazione
Un pacchetto di progettazione è un costrutto utilizzato per suddividere il modello di progettazione. Questa linea guida spiega come identificare e specificare i pacchetti di progettazione.
Relazioni
Descrizione principale

Introduzione

Il modello di progettazione può essere strutturato in piccole unità per renderne più semplice la comprensione. Raggruppando gli elementi del modello di progettazione in pacchetti e sottosistemi e mostrando come quei raggruppamenti siano correlati gli uni con gli altri, è più facile comprendere la struttura globale del modello. Un sottosistema di progettazione viene modellato come componente che realizza una o più interfacce; per ulteriori informazioni, consultare il Prodotto di lavoro: Sottosistema di progettazione e la Linea guida: Sottosistema di progettazione. I pacchetti di progettazione, d'altra parte, sono solo per il raggruppamento.

Visibilità del contenuto del pacchetto

Una classe contenuta in un pacchetto può essere pubblica o privata. Una classe pubblica può essere associata da qualsiasi altra classe. Una classe privata può essere associata solo dalle classi contenute nel pacchetto.

Un'interfaccia di pacchetto è composta dalle classi pubbliche di un pacchetto. L'interfaccia del pacchetto (classi pubbliche) isola ed implementa le dipendenze su altri pacchetti. In questo modo viene semplificato lo sviluppo parallelo poiché è possibile stabilire delle interfacce in precedenza e gli sviluppatori devono conoscere solo le modifiche nelle interfacce degli altri pacchetti.

Criteri di partizionamento del pacchetto

È possibile suddividere il modello di progettazione per una serie di motivi:

  • Si possono utilizzare i pacchetti ed i sottosistemi come unità d'ordine, di configurazione o di distribuzione quando il sistema finito.
  • L'assegnazione delle risorse e la competenza dei diversi team di sviluppo potrebbe richiedere che il progetto venga suddiviso in gruppi differenti in sedi diverse. I sottosistemi, con le interfacce ben definite, forniscono un metodo per suddividere il lavoro fra i team in modo controllato e coordinato, consentendo alla progettazione e all'implementazione di procedere in parallelo.
  • i sottosistemi possono essere utilizzati per strutturare il modello di progettazione in modo da riflettere i tipi di utenti. Molte delle richieste di modifica hanno origine dagli utenti: i sottosistemi fanno in modo che le modifiche provenienti da un particolare tipo di utente vengano applicate solo sulle parti del sistema che corrispondono a quel tipo di utente.
  • In alcune applicazioni, determinate informazioni devono essere accessibili solo a poche persone. I sottosistemi consentono di mantenere la riservatezza nelle aree in cui è necessaria.
  • Se si sta creando un sistema di supporto, è possibile farlo utilizzando i sottosistemi ed i pacchetti per fornirlo di una struttura del tutto simile a quella del sistema da supportare. In questo modo è possibile sincronizzare la manutenzione dei due sistemi.
  • I sottosistemi vengono utilizzati per rappresentare i prodotti ed i servizi esistenti che il sistema usa (ad esempio, prodotti COTS e librerie), come illustrato nelle prossime sezioni.

Creazione di pacchetti di classi boundary

Quando le classi boundary vengono distribuite ai pacchetti, possono essere applicate due diverse strategie; la scelta dipende se in futuro si prevede o meno di modificare in modo significativo le interfacce di sistema.

  • Se è probabile che l'interfaccia di sistema verrà sostituita o subirà delle modifiche importanti, deve essere separata dal resto del modello di progettazione. Quando viene modificata l'interfaccia utente, verranno implicati solo quei pacchetti. Un esempio di modifica importante è il passaggio da interfaccia orientata sulle righe a interfaccia orientata sulle finestre.

Diagramma descritto nel testo di accompagnamento.

Se lo scopo principale è di semplificare delle modifiche significative all'interfaccia, le classi boundary devono essere collocate in uno o più pacchetti separati.

  • Se non sono previste grosse modifiche all'interfaccia, le modifiche ai servizi di sistema dovrebbero essere il principio guida, piuttosto che le modifiche all'interfaccia. Le classi boundary allora devono essere collocate insieme alle classi di entità e di controllo con le quali sono correlate a livello funzionale. In questo modo sarà facile constatare che le classi boundary verranno implicate, quando viene modificata una determinata classe di entità o di controllo.

Diagramma descritto nel testo di accompagnamento.

Per semplificare le modifiche ai servizi del sistema, le classi boundary vengono impacchettate insieme alle classi alle quali sono correlate a livello funzionale.

Le classi boundary obbligatorie che non sono correlate a livello funzionale ad alcuna classe di entità, o di controllo, devono essere collocate in pacchetti separati, insieme alle classi boundary che appartengono alla stessa interfaccia.

Se una classe boundary è correlata ad un servizio facoltativo, raggrupparla con le classi che collaborano a fornire il servizio, in un sottosistema separato. Il sottosistema verrà messo in corrispondenza con un altro componente opzionale, che viene fornito quando viene ordinata la funzionalità opzionale.

Creazione di pacchetti delle classi correlate a livello funzionale

Per ogni gruppo di classi correlate a livello funzionale deve essere identificato un pacchetto. Esistono diversi criteri pratici che possono essere applicati quando si valuta se due classi sono correlate a livello funzionale. Essi sono, in ordine decrescente di importanza:

  • Se le modifiche alla funzionalità e/o alla struttura di una classe implicano delle modifiche ad un'altra classe, le due classi sono correlate a livello funzionale.

Esempio

Se viene aggiunto un nuovo attributo alla classe di entità Ordine, con tutta probabilità sarà necessario aggiornare la classe di controllo Amministratore ordini. Quindi appartengono allo stesso pacchetto Gestione ordini.

  • E' possibile individuare se una classe è funzionalmente correlata ad un'altra iniziando da una classe, ad esempio una classe di entità, ed esaminando l'impatto della sua rimozione dal sistema. Qualsiasi classe risulti superflua dopo la rimozione della classe, è in qualche modo collegata alla classe rimossa. Per superflua si intende che la classe viene utilizzata solo dalla classe rimossa o che dipende da essa.

Esempio

Nel Sistema di gestione magazzino esiste un pacchetto Gestione ordini che contiene le due classi di controllo Amministratore ordini e Registro ordini. Entrambe le classi di controllo modellano dei servizi relativi alla gestione degli ordini nel magazzino. Tutti gli attributi e le relazioni degli ordini sono memorizzate dalla classe di entità Ordine, che esiste solo per la gestione degli ordini. Se la classe di entità viene rimossa, non saranno più necessarie Amministratore ordini o Registro ordini perché sono utili solo se è presente anche Ordine. Quindi la classe di entità Ordine deve essere inclusa nello stesso pacchetto delle due classi di controllo.

Diagramma descritto nel testo di accompagnamento.

Amministratore ordini e Registro ordini appartengono allo stesso pacchetto di Ordine, poiché divengono superflue se Ordine viene rimosso dal sistema.

  • Due oggetti possono essere funzionalmente correlati se interagiscono con un grande numero di messaggi o dispongono di intercomunicazioni altrimenti complicate.

Esempio

La classe di controllo Esecutore compito invia e riceve molti messaggi da e verso l'Interfaccia trasportatore. Questa è un'altra indicazione che devono essere incluse nello stesso pacchetto, Gestione compiti.

  • Una classe boundary può essere funzionalmente correlata ad una particolare classe entità se la funzione della classe boundary è di presentare la classe entità.

Esempio

La classe boundary Modulo bancale nel Sistema di gestione magazzino presenta all'utente un'istanza della classe di entità Bancale. Ogni Bancale viene rappresentato da un numero di identificazione sullo schermo. Se le informazioni su un Bancale vengono modificate, ad esempio se gli viene assegnato anche un nome, potrebbe essere necessario modificare anche la classe boundary. Quindi anche Modulo bancale deve essere incluso nello stesso pacchetto di Bancale.

  • Due classi possono essere funzionalmente correlate se interagiscono con lo stesso attore o sono influenzate dalle modifiche apportate ad esso. Se due classi non implicano lo stesso attore, non devono stare nello stesso pacchetto. L'ultima regola può, naturalmente, essere ignorata per motivazioni più importanti.

Esempio

Nel Sistema di gestione magazzino esiste un pacchetto Gestione compiti, che include, fra le altre cose, la classe di controllo Esecutore compito. Questo è l'unico pacchetto coinvolto con l'attore Trasportatore, il trasportatore fisico che può trasportare un bancale nel magazzino. L'attore interagisce con la classe di controllo Esecutore compito tramite la classe boundary Interfaccia trasportatore. Questa classe boundary deve quindi essere inclusa nel pacchetto Gestione compiti.

Diagramma descritto nel testo di accompagnamento.

Interfaccia trasportatore e Esecutore compito appartengono allo stesso pacchetto poiché entrambi sono toccati dalle modifiche all'attore Trasportatore.

  • Due classi possono essere funzionalmente correlate se fra di loro intercorrono delle relazioni fra loro (associazioni, aggregazioni, ecc.). Naturalmente questo criterio non può essere seguito di routine ma può essere utilizzato quando non sono applicabili altri criteri.
  • Una classe può essere funzionalmente correlata alla classe che ne crea delle istanze.

Questi due criteri determinano quando due classi non devono essere collocate nello stesso pacchetto:

  • Due classi correlate a due attori diversi non devono essere messe nello stesso pacchetto.
  • Una classe facoltativa ed una obbligatoria non devono stare nello stesso pacchetto.

Valutazione della coesione del pacchetto

Innanzitutto tutti gli elementi di un pacchetto devono avere lo stesso tipo di opzionalità: non possono esserci degli elementi di modello facoltativi in un pacchetto obbligatorio.

Esempio

La classe entità obbligatoria Tipo di articolo ha, fra le altre cose, un attributo denominato Soglia di ristoccaggio. La funzione di ristoccaggio, tuttavia, è facoltativa nel sistema. Quindi Articolo deve essere suddiviso in due classi di entità, dove la classe facoltativa è correlata a quella obbligatoria.

Un pacchetto considerato obbligatorio non può dipendere da pacchetti considerati facoltativi.

Di regola, un singolo pacchetto non può essere utilizzato da due diversi attori. Il motivo di ciò è che una modifica al comportamento di un attore non deve influire anche sugli altri attori. Esistono delle eccezioni a questa regola, ad esempio per pacchetti che costituiscono dei servizi opzionali. I pacchetti di questo tipo non devono essere divisi, a prescindere dal numero di attori che li utilizza. Quindi, suddividere qualsiasi pacchetto o classe utilizzati da diversi attori, a meno che non si tratti di un pacchetto opzionale.

Tutte le classi dello stesso pacchetto devono essere funzionalmente correlate. Se sono stati seguiti i criteri della sezione "Ricerca di pacchetti da classi funzionalmente correlate", le classi contenute in un pacchetto saranno funzionalmente correlate fra loro. Tuttavia, una particolare classe potrebbe essa stessa contenere "troppa" funzionalità o troppe relazioni che non appartengono alla classe. Parte della classe deve allora essere rimossa e diventare una classe completamente nuova oppure deve essere spostata in qualche altra classe, che probabilmente appartiene ad un altro pacchetto.

Esempio

La funzionalità di una classe di controllo A in un pacchetto non deve dipendere troppo da una classe B di un altro pacchetto. Per isolare la funzionalità specifica di B, la classe di controllo A deve dividersi in due classi di controllo A' e A". La funzionalità specifica di B viene collocata nella nuova classe di controllo A", che viene messa nello stesso pacchetto di B. Anche la nuova classe A" ottiene una relazione, come la generalizzazione, all'oggetto originale A'.

Diagramma descritto nel testo di accompagnamento.

Per isolare la funzionalità specifica di B, la classe di controllo A, che manca di omogeneità, viene suddivisa in due classi di controllo, A' e A''.

Descrizione delle dipendenze del pacchetto

Se una classe di un pacchetto è associata ad una classe di un pacchetto diverso, i pacchetti dipenderanno l'uno dall'altro. Le dipendenze dei pacchetti vengono modellate utilizzando una relazione di dipendenza fra i pacchetti. Le relazioni di dipendenza sono utili per valutare le conseguenze delle modifiche: un pacchetto da cui dipendono molti altri pacchetti è più difficile da modificare di uno da cui non ne dipende nessuno.

Poiché durante la specifica dei pacchetti verranno rilevate diverse dipendenze come questa, le relazioni vengono collegate alla modifica durante il lavoro. La descrizione di una relazione di dipendenza può includere le informazioni relative a quali relazioni di classe hanno causato la dipendenza. Poiché questo introduce delle informazioni difficili da gestire, deve essere fatto solo se l'informazione è pertinente e di valore.

Esempio

In Sistema di gestione magazzino esiste una relazione di dipendenza fra il pacchetto Gestione dell'ordine ed il pacchetto Gestione dell'articolo. Questa associazione si verifica perché la classe di entità Ordine in Gestione dell'ordine ha un'associazione con la classe di entità Tipo di articolo nell'altro pacchetto.

Diagramma descritto nel testo di accompagnamento.

Il pacchetto Gestione dell'ordine dipende da Gestione articolo, poiché esiste un'associazione fra due classi dei pacchetti.

Valutazione della dipendenza dei pacchetti

La dipendenza dei pacchetti è una cosa buona e cattiva: buona perché rappresenta il riutilizzo e cattiva perché rappresenta le dipendenze che rendono il sistema più difficile da modificare e da evolvere. Si possono seguire alcuni principi generali:

  • I pacchetti non devono essere dipendenti a incrocio (co-dipendenti); per esempio due pacchetti non devono essere dipendenti uno dall'altro.

Diagramma descritto nel testo di accompagnamento.

In questi casi, i pacchetti devono essere riorganizzati per rimuovere le dipendenze incrociate.

  • I pacchetti dei livelli inferiori non devono essere dipendenti dai pacchetti dei livelli superiori. I pacchetti devono essere dipendenti solo da pacchetti dello stesso livello e nel successivo livello inferiore.

Diagramma descritto nel testo di accompagnamento.

In questi casi la funzionalità deve essere risuddivisa. Una soluzione deve asserire le dipendenze in termini di interfaccia ed organizzare le interfacce nel livello inferiore.

  • In generale, le dipendenze devono ignorare i livelli, a meno che il comportamento dipendente sia comune in tutti i livelli, e l'alternativa è di passare semplicemente i richiami di operazioni nei livelli.
  • I pacchetti non devono dipendere dai sottosistemi, solo da altri pacchetti o da interfacce.