Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

D - Dipendency Inversion Principle

Astrarre le dipendenze tra moduli di alto e basso livello
Astrarre le dipendenze tra moduli di alto e basso livello
Link copiato negli appunti

Nella programmazione di tutti i giorni siamo abituati ad operare secondo un pattern ormai collaudato: i componenti 'di servizio', come il gestore di connessione al database, il template engine o il generatore di documenti XML, ordinati in librerie, vengono inclusi dai componenti che integrano la logica di business e da loro utilizzati.

Il problema legato a questa operatività è insito nel profondo legame di dipendenza che si crea fra la
classe che incorpora la logica di business e tutte le librerie di servizio, rendendo di fatto difficile il
riutilizzo della stessa in altri progetti.

Il Dipendency Inversion Principle (DIP), formulato da Robert C. Martin, indica in modo chiaro una tecnica di implementazione pensata per evitare i problemi di poco fa. il tutto si basa

L'Inversione della dipendenza si basa su due assunzioni, un po' criptiche:

  1. “ I moduli di alto livello non dovrebbero dipendere da moduli di basso livello. Entrambi dovrebbero dipendere da astrazioni. ”
  2. “ Le astrazioni non dovrebbero dipendere dai dettagli. I dettagli dovrebbero dipendere dalle astrazioni ”

Cerchiamo di gettar luce su quanto enunciato con qualche esempio.

Connessione al database

Per questo articolo utilizziamo un esempio classico e diretto, il driver di connessione al database. Supponiamo di avere una struttura come la seguente:

Figura 12. Dipendenza diretta tra classi
Dipendenza diretta tra classi

Una classe Utente che richiede una classe MySqlDriver per poter compiere operazioni come:

richiedi MySqlDriver
classe Utente
  void salva()
    MySqlDriver.scriviSuDatabase('tab_utente',...)

Dov'è il problema? La classe Utente sarà sempre dipendente dalla classe MySqlDriver: se avessimo l'opportunità di sviluppare un progetto simile, nel quale però i dati vengono memorizzati su di un'altro database (Oracle, ad esempio), non potremmo beneficiare del codice che abbiamo già scritto ma dovremmo re-implementare da zero la classe. Inoltre anche in questo progetto la classe Utente è troppo rigida nei confronti di MySqlDriver.

Applichiamo il punto A enunciato dal principio: I moduli di alto livello non devono dipendere da moduli di basso livello, entrambi devono dipendere da astrazioni.

Dobbiamo spostare la relazione tra Utente e MySqlDriver su due classi più generiche, ad esempio:

Figura 13. Dipendenza spostata tra classi più astratte
Dipendenza diretta tra classi

In questo modo abbiamo ottenuto un duplice beneficio, in primo luogo abbiamo slegato completamente Utente dalla libreria di servizio, in seconda istanza è ora molto più semplice beneficiare delle funzioni di salvataggio su database dalle classi di alto livello attraverso la classe Modello:

richiedi Modello
classe Utente che deriva da Modello
  void salva
    salvaSuDatabase('tab_utente',...)

richiedi DatabaseDriver
classe Modello
  costruttore Modello(DatabaseDriver d)
    driver = d
  void salvaSuDatabase(string tabella, ...)
    driver.salvaSuDatabase(tabella, ...)

richiedi DatabaseDriver
classe MySqlDriver che deriva da DatabaseDriver
  void salvaSuDatabase(tabella, comando)
    // implementazione della funzione di salvataggio

classe DatabaseDriver
  void salvaSuDatabase(tabella, comando)
    // metodo astratto

L'intera struttura è incredibilmente più flessibile, possiamo infatti creare altre classi che ereditino da Modello e beneficino quindi automaticamente dell'interazione con il database di riferimento.

Lo stesso discorso vale anche nel senso opposto, nuovi Driver possono essere tranquillamente inseriti nel progetto senza causare nessuna modifica alle classi utilizzatrici.

A questo si aggiunge la possibilità di riutilizzare sia i componenti che contengono la logica di business sia le librerie di servizio per futuri progetti a patto di mantenere la relazione tra Modello e DatabaseDriver; vincolo accettabile in quanto queste due classi sono così generiche da non causare, almeno in linea teorica, particolari problematiche.

Prima di proseguire notiamo anche che in questo modo abbiamo anche ottenuto l'aderenza al
secondo punto
dell'enunciato del principio: l'astrazione non deve dipendere dai dettagli, i dettagli
devono dipendere dall'astrazione.

Ma cosa possiamo fare se MySqlDriver è una libreria di terze parti sulla quale non possiamo insistere con questo tipo di refactory?

Semplice: utilizziamo un Adapter che sappia tradurre i metodi esposti dalla classe generica (DatabaseDriver) nelle funzioni esposte dalla libreria:

Figura 14. Indipendenza dal driver di terze parti
Indipendenza dal driver di terze parti

Ti consigliamo anche