Decomposizione gerarchica
Non dovresti mai iniziare subito a scrivere lezioni per la tua applicazione. Prima deve essere progettato. Il design dovrebbe terminare con un'architettura ponderata. E per ottenere questa architettura, è necessario scomporre costantemente il sistema.
La scomposizione deve essere eseguita gerarchicamente: in primo luogo, il sistema è suddiviso in grandi moduli / sottosistemi funzionali che ne descrivono il funzionamento nel modo più generale. Quindi i moduli risultanti vengono analizzati in modo più dettagliato e suddivisi in sottomoduli o oggetti.
Prima di selezionare gli oggetti, dividi il sistema in blocchi semantici di base, almeno mentalmente. Nelle piccole applicazioni, questo di solito è molto facile da fare: un paio di livelli di gerarchia sono abbastanza, poiché il sistema viene prima diviso in sottosistemi / pacchetti e i pacchetti sono divisi in classi.
![Decomposizione gerarchica](https://cdn.codegym.cc/images/article/d5e9aa2b-5783-430f-b5b5-1832bc33c540/800.jpeg)
Questa idea non è così banale come sembra. Ad esempio, qual è l'essenza di un "modello architettonico" così comune come Model-View-Controller (MVC)?
Si tratta di separare la presentazione dalla logica aziendale . Innanzitutto, qualsiasi applicazione utente è divisa in due moduli: uno è responsabile dell'implementazione della logica aziendale stessa (modello) e il secondo è responsabile dell'interazione con l'utente (interfaccia utente o vista).
Poi si scopre che i moduli devono in qualche modo interagire, per questo aggiungono un Controller, il cui compito è gestire l'interazione dei moduli. Anche nella versione mobile (classica) di MVC, viene aggiunto il pattern Observer in modo che la View possa ricevere eventi dal modello e modificare i dati visualizzati in tempo reale.
Tipici moduli di primo livello, ottenuti a seguito della prima suddivisione del sistema nei componenti più grandi, sono appunto:
- Logica di business;
- Interfaccia utente;
- Banca dati;
- Sistema di messaggistica;
- Contenitore di oggetti.
La prima divisione di solito divide l'intera applicazione in 2-7 (massimo 10 parti). Se lo suddividiamo in più parti, ci sarà il desiderio di raggrupparle e otterremo nuovamente 2-7 moduli di livello superiore.
Decomposizione funzionale
La suddivisione in moduli/sottosistemi viene eseguita al meglio in base ai compiti che il sistema risolve . L'attività principale è suddivisa nelle sue sottoattività costitutive, che possono essere risolte/eseguite autonomamente, indipendentemente l'una dall'altra.
Ogni modulo dovrebbe essere responsabile della risoluzione di alcune attività secondarie e svolgere la funzione corrispondente . Oltre allo scopo funzionale, il modulo è caratterizzato anche da un insieme di dati necessari allo stesso per svolgere la sua funzione, ovvero:
Modulo = Funzione + Dati necessari per eseguirlo.
Se la scomposizione in moduli viene eseguita correttamente, l'interazione con altri moduli (responsabili di altre funzioni) sarà minima. Potrebbe esserlo, ma la sua assenza non dovrebbe essere critica per il tuo modulo.
Un modulo non è un pezzo di codice arbitrario, ma un'unità di programma completa (sottoprogramma) funzionalmente significativa e separata che fornisce una soluzione a un determinato compito e, idealmente, può funzionare indipendentemente o in un altro ambiente ed essere riutilizzata. Il modulo dovrebbe essere una sorta di "integrità capace di relativa indipendenza nel comportamento e nello sviluppo". (Cristoforo Alessandro)
Pertanto, la decomposizione competente si basa, prima di tutto, sull'analisi delle funzioni del sistema e dei dati necessari per svolgere tali funzioni. Le funzioni in questo caso non sono funzioni di classe e moduli, perché non sono oggetti. Se hai solo un paio di classi in un modulo, allora hai esagerato.
Connettività forte e debole
È molto importante non esagerare con la modularizzazione. Se dai a un principiante un'applicazione Spring monolitica e gli chiedi di suddividerla in moduli, eliminerà ogni Spring Bean in un modulo separato e considererà che il suo lavoro è finito. Ma non lo è.
Il criterio principale per la qualità della scomposizione è il modo in cui i moduli sono focalizzati sulla risoluzione dei loro compiti e sono indipendenti.
Questo di solito è formulato come segue: "I moduli ottenuti come risultato della decomposizione dovrebbero essere massimamente coniugati internamente (elevata coesione interna) e minimamente interconnessi tra loro (basso accoppiamento esterno)".
Alta coesione, alta coesione o "coesione" all'interno del modulo, indica che il modulo è focalizzato sulla risoluzione di un problema ristretto e non è impegnato nello svolgimento di funzioni eterogenee o responsabilità non correlate.
La coesione caratterizza il grado in cui i compiti svolti dal modulo sono correlati tra loro.
Una conseguenza di High Cohesion è il Principio di Responsabilità Unica - il primo dei cinque principi SOLID , secondo il quale qualsiasi oggetto/modulo dovrebbe avere una sola responsabilità e non dovrebbe esserci più di un motivo per cambiarlo.
Low Coupling , loose coupling, significa che i moduli in cui è suddiviso il sistema devono essere, se possibile, indipendenti o debolmente accoppiati tra loro. Dovrebbero essere in grado di interagire, ma allo stesso tempo sapere il meno possibile l'uno dell'altro.
Ogni modulo non ha bisogno di sapere come funziona l'altro modulo, in quale lingua è scritto e come funziona. Spesso, per organizzare l'interazione di tali moduli, viene utilizzato un determinato contenitore, in cui vengono caricati questi moduli.
Con una corretta progettazione, se si modifica un modulo, non sarà necessario modificarne altri o tali modifiche saranno minime. Più allentato è l'accoppiamento, più facile è scrivere/comprendere/estendere/riparare il programma.
Si ritiene che i moduli ben progettati dovrebbero avere le seguenti proprietà:
- Integrità e completezza funzionale : ogni modulo implementa una funzione, ma la implementa bene e completamente, il modulo esegue in modo indipendente una serie completa di operazioni per implementare la sua funzione.
- Un input e un output : all'input, il modulo del programma riceve un determinato set di dati iniziali, esegue un'elaborazione significativa e restituisce un set di dati di risultato, ovvero viene implementato il principio IPO standard - input -\u003e processo -\u003e produzione.
- Indipendenza logica : il risultato del lavoro del modulo del programma dipende solo dai dati iniziali, ma non dipende dal lavoro di altri moduli.
- Collegamenti informativi deboli con altri moduli : lo scambio di informazioni tra moduli dovrebbe essere ridotto al minimo, se possibile.
È molto difficile per un principiante capire come ridurre ulteriormente la connettività dei moduli. In parte questa conoscenza arriva con l'esperienza, in parte dopo aver letto libri intelligenti. Ma è meglio analizzare le architetture delle applicazioni esistenti.
Composizione invece di eredità
La scomposizione competente è una sorta di arte e un compito difficile per la maggior parte dei programmatori. La semplicità è ingannevole qui e gli errori sono costosi.
![](https://cdn.codegym.cc/images/article/d7e785ed-e16d-4354-883c-0ad83327512a/512.jpeg)
Accade che i moduli dedicati siano fortemente accoppiati tra loro e non possano essere sviluppati indipendentemente. Oppure non è chiaro di quale funzione sia responsabile ciascuno di essi. Se riscontri un problema simile, molto probabilmente il partizionamento in moduli è stato eseguito in modo errato.
Dovrebbe essere sempre chiaro il ruolo svolto da ciascun modulo . Il criterio più affidabile per verificare che la scomposizione sia eseguita correttamente è se i moduli sono subroutine indipendenti e preziose che possono essere utilizzate isolatamente dal resto dell'applicazione (e quindi possono essere riutilizzate).
Quando si decompone un sistema, è opportuno verificarne la qualità ponendosi le domande: "Quale compito svolge ciascun modulo?", "Quanto sono facili da testare i moduli?", "È possibile utilizzare i moduli da soli o in un altro ambiente?" influenzare gli altri?"
Devi cercare di mantenere i moduli il più autonomi possibile . Come accennato in precedenza, questo è un parametro chiave per una corretta decomposizione . Pertanto, deve essere eseguito in modo tale che i moduli siano inizialmente debolmente dipendenti l'uno dall'altro. Se ci sei riuscito, allora sei un grande.
In caso contrario, anche qui non tutto è perduto. Esistono numerose tecniche e schemi speciali che consentono di ridurre al minimo e indebolire ulteriormente i collegamenti tra i sottosistemi. Ad esempio, nel caso di MVC, a questo scopo è stato utilizzato il pattern Observer, ma sono possibili altre soluzioni.
Si può affermare che le tecniche per il disaccoppiamento costituiscono il principale "strumento dell'architetto". È solo necessario capire che stiamo parlando di tutti i sottosistemi ed è necessario indebolire la connessione a tutti i livelli della gerarchia , cioè non solo tra classi, ma anche tra moduli a ciascun livello gerarchico.