Come mostra l’esempio 8, gli sviluppatori devono affrontare le dipendenze che sorgono come risultato della loro decomposizione di un problema e della sua soluzione in un certo numero di moduli. Diciamo che un modulo di un sistema dipende da un altro se è possibile che una modifica a un modulo richieda una modifica a un altro. Per esempio, se un’azienda cambia i suoi metodi di produzione, questo può causare un conseguente cambiamento nel modo in cui calcola i pagamenti richiesti per i beni che produce.
Uno sviluppatore deve occuparsi non solo della natura di ogni dipendenza ma anche del numero di dipendenze. Nell’ingegneria del software, “accoppiamento” è usato per riferirsi al grado di interdipendenza tra le diverse parti di un sistema. È facile vedere che certi sistemi possono avere catene di moduli interdipendenti dove, per esempio, il modulo A dipende dal modulo B, che dipende dal modulo C, e così via. In alcuni casi queste catene possono unirsi e creare una dipendenza circolare, che è una forma particolare di accoppiamento forte (o alto).
Gli sviluppatori cercano di costruire sistemi liberamente accoppiati perché sono più facili da capire e mantenere. Quindi un buon sistema software ha un basso accoppiamento, il che significa che i cambiamenti ad una parte hanno meno probabilità di propagarsi attraverso il resto del sistema. Un ulteriore vantaggio del basso accoppiamento è che i componenti sono facili da sostituire e, potenzialmente, da riutilizzare.
Qualunque sia il livello di accoppiamento in un sistema software, è importante sapere quali moduli sono accoppiati. Se non ci fossero registrazioni dell’accoppiamento tra i moduli, uno sviluppatore dovrebbe passare del tempo a lavorare attraverso i moduli per determinare se ognuno è interessato o meno da un cambiamento. Il risultato sarebbe un sacco di sforzo speso per controllare, anche se non fosse necessario alcun cambiamento. L’esempio 9 illustra il pericolo di avere più di un modulo che fa uso di dati comuni o condivisi.
Esempio 9
La gestione della data è sempre stata un problema per gli sviluppatori di software. Per le applicazioni di una certa età, il formato di memorizzazione più applicabile per rappresentare un anno era un numero tra 0 e 99. Aveva senso perché il 1966 veniva memorizzato come 66, il 1989 come 89, e così via, quindi era necessario meno spazio per memorizzare solo due cifre. Inoltre, se le date erano memorizzate come numeri, i compiti che comportavano l’ordinamento per data erano semplici – 22 gennaio 1989 memorizzato come 890122, è dopo il 22 dicembre 1966 memorizzato come 661222.
Purtroppo, un certo numero di queste applicazioni erano ancora in uso con l’avvicinarsi dell’anno 2000, così ogni modulo in ogni applicazione che usava la forma abbreviata dell’anno doveva essere investigato.
Un aspetto importante del problema nell’esempio 9 era che diversi sviluppatori avevano modi diversi di leggere e manipolare i valori memorizzati nelle variabili che usavano il formato della data a sei cifre. Questo ha aumentato lo sforzo richiesto per risolvere il cosiddetto millennium bug. Se gli sviluppatori avessero avuto un modo coerente di manipolare le date che non si basava sul formato di memorizzazione, il millennium bug non sarebbe stato un problema di preoccupazione.
La coesione è un modo di descrivere quanto strettamente le attività all’interno di un singolo modulo sono collegate tra loro. La coesione è un concetto generale – per esempio, un dipartimento in un’organizzazione potrebbe avere un insieme coeso di responsabilità (conti, per esempio), oppure no (servizi vari). Nei sistemi software, un modulo altamente coeso esegue un compito o raggiunge un singolo obiettivo – ‘fai una cosa sola e falla bene’ è un utile motto da applicare. Un modulo dovrebbe implementare un singolo compito logico o una singola entità logica.
Basso accoppiamento e alta coesione sono obiettivi concorrenti. Se ogni modulo fa solo una cosa ad un basso livello di astrazione, potremmo aver bisogno di un complesso edificio di moduli altamente accoppiati per eseguire un’attività a livelli più alti di astrazione. Uno sviluppatore dovrebbe cercare di raggiungere il miglior equilibrio tra i livelli di accoppiamento e coesione per un sistema software. Per esempio, gli hotel generano reddito affittando le loro stanze agli ospiti. Il concetto di camera è probabile che sia rappresentato da qualche parte nel sistema software per le prenotazioni di un hotel. Può essere conveniente usare un modulo o una classe che rappresenti il concetto di camera per raccogliere e memorizzare i dati sul reddito generato dall’affitto delle camere. Tuttavia, una soluzione migliore è quella di avere un modulo separato di fatturazione o pagamento, perché è più coesivo, specialmente quando un hotel genera entrate in altri modi, per esempio, dal servire pasti a persone che non sono ospiti residenti.
Attività 5 Dividi e conquista
- a.Perché potresti considerare di dividere un grande progetto in pezzi più piccoli?
- b.Come influisce la complessità di un sistema software sul compito di manutenzione?
- c.Cos’è un modulo?
- d.Perché è utile avere un basso accoppiamento in un sistema software?
- e.Dare esempi dei tipi di informazione che sarebbero preziosi quando si considera un cambiamento a un dato modulo.
- f.Cosa sono le dipendenze di contesto di un modulo? Come si relazionano all’interfaccia di un modulo?
- g.Quali sono i vantaggi di usare moduli con interfacce definite?
- h.Perché è utile avere un’alta coesione nei moduli di un sistema software?
- i.Quali caratteristiche dovrebbe mostrare un modulo che aiuterà a garantire che sia facile ed economico da sviluppare e mantenere, e che gli errori siano tenuti al minimo?
- j.Perché è importante raggiungere un equilibrio tra accoppiamento e coesione?
Risposta
- a.C’è un limite a quanto una persona può capire in qualsiasi momento. Quindi c’è un limite alla dimensione di un sistema software che una sola persona può affrontare. Dividendo un grande progetto in pezzi più piccoli, è possibile identificare un numero di compiti più gestibili per le persone coinvolte.
- b.E’ essenziale essere in grado di fare un cambiamento ad un sistema software senza dover conoscere tutto di quel sistema. Ogni cambiamento diventa difficile quando il flusso di controllo e le dipendenze all’interno dei programmi sono complessi. Maggiore è il numero e la natura delle dipendenze, più difficile è mantenere un sistema software.
- c.Un modulo è qualsiasi parte identificabile di un sistema software che viene considerata separatamente. Per esempio, i moduli possono essere subroutine (in un linguaggio procedurale equivalente ai metodi), classi (in un linguaggio orientato agli oggetti), funzioni di libreria o altri costrutti che possono essere trattati indipendentemente.
- d.Con un basso accoppiamento, ci sono poche dipendenze tra i moduli. Perciò i cambiamenti fatti a una parte (uno o più moduli) di un sistema software hanno meno probabilità di propagarsi in tutto il sistema. (Una chiara registrazione delle dipendenze tra i moduli aiuta a prevedere l’impatto di un cambiamento proposto a un sistema software.)
- e.Ci sono due tipi di informazioni che contribuiscono all’analisi di un cambiamento proposto:
- Quali moduli sono clienti del modulo in questione? Questa informazione indica quanto un cambiamento può propagarsi attraverso il sistema software.
- Quali assunzioni sono state fatte nei moduli client del modulo in questione? Una comprensione dei servizi previsti di un modulo aiuterà a valutare i rischi associati a un particolare cambiamento.
- f.Le dipendenze di contesto per un modulo sono i servizi di altri moduli di cui il modulo ha bisogno per funzionare correttamente. Potete esprimere le dipendenze di contesto per un modulo in termini di altre interfacce. In effetti, potete esprimere le responsabilità di un modulo in termini della sua interfaccia e delle sue dipendenze dal contesto. Se il contesto fornisce i servizi di cui il modulo ha bisogno e i client soddisfano qualsiasi condizione specificata nell’interfaccia, il modulo può garantire la fornitura dei servizi descritti nella sua interfaccia.
- g.I benefici sono i seguenti:
- Gli sviluppatori dovranno conoscere solo l’interfaccia del modulo (la sua sintassi e ciò che richiede e realizza – la sua semantica), non come fornisce quei servizi. Di conseguenza gli sviluppatori possono essere più produttivi.
- Gli sviluppatori possono capire gli aspetti del sistema software più a fondo, così saranno introdotti meno bug.
- Dovrebbe essere più facile trovare i bug, dato che si evitano moduli irrilevanti.
- La possibilità di riutilizzo del modulo è aumentata una volta che si sa cosa quel modulo fornisce e richiede.
- h.Con alta coesione, un modulo esegue un insieme ragionevole di operazioni o attività. Idealmente un’alta coesione implica una sola astrazione principale per modulo. L’interfaccia astrae da ciò che uno sviluppatore deve sapere per usare un modulo. Questo rende più facile per gli sviluppatori capire lo scopo del modulo e come usarlo. Inoltre un’alta coesione tende a rendere un modulo più riutilizzabile in altre applicazioni, perché fornisce un insieme di operazioni che stanno naturalmente insieme.
- i.Un modulo dovrebbe avere basso accoppiamento e alta coesione, rappresentare una buona astrazione, e avere un’interfaccia ben definita che è un’astrazione incapsulata di un concetto ben compreso.
- j.Nella costruzione di un sistema, si può avere una scelta tra un insieme più piccolo di moduli poco accoppiati e meno coesivi, o un insieme più grande di moduli strettamente accoppiati e più coesivi. Nel primo caso ogni modulo può essere difficile da capire, mentre nel secondo caso le relazioni tra loro possono essere troppo complesse. È necessario trovare un equilibrio appropriato.