Inversion of Control e dintorni: Dependency Injection – parte 1 di 3

Qualche tempo fa stavo mettendo mano al codice scritto da un altro collega programmatore e, dopo aver verificato che il tutto era scritto sicuramente in modo molto originale ed efficace, ho anche notato che il risultato non rispettava, nemmeno da lontano, alcun principio SOLID.

In particolare il principio Dependency Inversion Principle (per gli amici DIP) che permette, tra l’altro, di scrivere unit test in modo veloce, era stato “bellamente” ignorato.

Dopo aver discusso con l’autore mi sono reso conto che il problema era che lui non aveva la minima conoscenza di questo argomento, pur essendo un ottimo programmatore. Per questo motivo ho pensato di scrivere queste poche righe, nella speranza che possano essere utili anche ad altri.

Inizio con il dire che un codice, per essere considerato “fantastico”, deve rispettare alcuni principi, che ne facilitino la manutentibilità.

Ovviamente uno può scrivere il codice come più gli aggrada, ma un lavoro ben fatto è sempre un lavoro ben fatto: e un lavoro ben fatto si riconosce sempre quando poi occorre metterci mano per apportare qualche aggiunta o modifica al flusso funzionale.

Dunque: nell’ambito della programmazione queste regole ad oggi sono universamente riconosciute nei principi SOLID, che danno le “tavole della legge” per la programmazione moderna.

S: Single Responsibility Principle (SRP)
O: Open-Closed Principle (OCP)
L: Liskov Substitution Principle (LSP)
I: Interface Segregation Principle (ISP)
D: Dependency Inversion Principle (DIP)

Questa tavola delle “leggi della programmazione” è state compilata da Robert C. Martin negli anni 90, e sono ancora tutt’ora è considerata come la più efficace e valida: sicuramente è ancora la più conosciuta e utilizzata.


Quindi un software ben progettato deve sottostare a queste leggi, cioè deve rispettare questi Software design principle.

Occorre dire che queste sono leggi dicono cosa occorre fare e cosa non fare: non spiegano in dettaglio come implementare un algoritmo.

E’ un pò come la legge che dice “non rubare”, ma poi non vi dà indicazione su come campare senza rubare ad altri……

Per fare questo entra in gioco il Software design pattern: in pratica è una lista di soluzioni a problemi noti.

Queste soluzioni sono applicabili a tutti gli i linguaggi di programmazione moderni, e la descrizione della risoluzione al problema è abbastanza generale da poter essere implementato con qualsiasi codice.

Osservazione personale: tutto quanto detto sopra è sicuramente meraviglioso, ed è perfettamente funzionale in un modo perfetto.

Purtroppo molti (tra cui me) sulla programmazione campa (anche se ne è molto appassionato), e quindi occorre poi arrivare al nocciolo della questione.

Con questo voglio dire che tutte queste regole sicuramente hanno il risultato di produrre un codice perfetto ma… in alcuni casi il codice anche se non è perfetto va bene lo stesso, basta che funzioni in modo corretto e sia sufficientemente manuntenibile.

In altri termini occorre saper valutare se e quanto far sottostare al nostro codice tutte le software design principle, che innegabilmente rendono il codice più manuntenibile e pronto a eventuali espansioni, ma anche introducono dei costi e incrementano i tempi di realizzazione.

Quindi ai principi SOLID sappiate sempre contrapporre anche il principio DEVO-FINIRE-IL-CODICE-NEI-TEMPI-STABILITI-PER-CAMPARE.

Altra Osservazione personale: un codice scritto tutti i principi SOLID pur essendo sicuramente molto più manutenibile, può introdurre in alcune condizioni alcuni problemi legati per esempio alle perfomance.

Fine Osservazioni personali.

In questa serie mi concentrerò su un metodo per rispettare il Dependency Inversion Principle (DIP), per gli amici D: il più sistema più utilizzato per rispettare questa regola è implementare il design partten Inversion Of Control IOC.

Partiamo dal problema pricipale di cui parla la “sacra legge”: usualmente alcuni parti di codice dipendono da altri codici o librerie. Il modo con cui viene creata questa dipendenza può introdurre difficoltà e problematiche.

Esponiamo il seguente esempio: diciamo che abbiamo un software che dovrebbe tiene sotto controllo la cartella c:\tmp, e segnala qualosa nella stessa avvenga l’inserimento/modifica/cancellazione di qualche file ivi contenuto.

Eccone la semplicissima implemementazione.

Non fa molto, ma il problema viene svolto egregiamente. Ora diciamo che potremmo volere la notifica del cambiamento della cartella c:\tmp non nel file di log ma magari su il log di sistema (Event Viewer).

Ecco che quindi una prima implementazione di questa modifica potrebbe essere come nel seguito.

Sicuramente funziona tutto, ma siamo sicuri che tutto vada per il meglio ?? Tutto sommato è onorevole, sicuramente può essere ulteriormente migliorata (per esempio la classe che accede al file di log o al registro eventi di sistema non è SingleTone) ma direi che rispetta il punto basilare: funziona perfettamente).

Adesso espongo quelli che secondo me una tale implementazione introduce.

– L’algoritmo dell’esempio è molto semplice, ma che succederebbe se i punti nel codice in cui scrivere sul log aumentassero ? Oppure se oltre a scrivere il log su un file o sull’event viewer si scegliessero altre possibilità (esempio invio via mail di una nota) ? Si arriverebbe a un ginepraio di IF veramente ingestibili (cioè oltre a if _scriviSuFileLog ci sarebbe if _invioMailAvviso etc etc).

– La classe LogWriterOnEventViewer e LogWriter deve essere presente quando si compila il progetto (capuiremo cosa vuole dire tra poco).

– Similmente se si volessero aumentare le possibilità di log occorrerebbe mettere mano al codice della classe DirectoryController

– La classe sopra sarebbe impossibile da testare usando unit test, e quindi usando stub o mock.

Per risolvere questo problema ci viene in aiuto il pattern Inversion Of Control (IoC).

Questo pattern prevede due implementazioni.

– Dependency Injection

– Service Locator

Nei prossimio post esporrò gli esempi di implementazione relativa.