Dependency Injection: belle parole o fatti?
Mi trovo a disagio a parlare di Inversion of Control (IoC), Dependency Injection (DI) e delle tecnologie che le implementano (Spring, PicoContainer, ecc). Non mi preoccupa il fatto che siano temi di moda e che quindi parlarne criticamente sia impopolare. Mi preoccupa il fatto che siano un po’ come un pozzo dei desideri: ognuno ci vede quello vuole. Quindi quello che vuol dire per me può non aver senso per un altro e viceversa, ma entrambi in un certo senso avremmo ragione. Cercherò di spiegarmi. Sarò criticissimo, ma costruttivamente :-)
Focalizzazione
Innanzitutto: IoC o DI? Inversion of Control è un termine troppo generico. Ancora più generica è la spiegazione che spesso si sente: “non chiamarmi, io chiamo te” riferito al rapporto fra container e i componenti da esso gestiti. E’ una spiegazione che si applica anche ad altri pattern object oriented (”call back”, “chain of responsibility”, “command”, “visitor”) e quindi non può essere distintiva.
In effetti la comunità sembra riconoscere l’ambiguità del termine IoC e viene attualmente preferito il termine DI e la spiegazione “non istanziare le risorse di cui hai bisogno, sarò io a fornirtele”. Quindi di DI parleremo.
Altra questione: DI o Spring? Spring è molto più di DI, dove il “di più” è un ricco insieme di librerie. Però:
- Le librerie Spring (come qualunque software) usano molti concetti di design anche diversi dal DI. Molto importante è p.e. il concetto di “adapter”.
- Se da Spring si togliesse il concetto di DI rimarrebbero solo… un po’ di librerie e non saremmo qui a parlarne:-) E’ la DI che dà unità al tutto.
Ok. Direi che come focalizzazione ci siamo. Possiamo cominciare a parlare di DI.
Una differenza che non fa la differenza
Spring ha in qualche modo abituato i programmatori che per per fare DI bisogna scrivere un file di configurazione XML. DI e sua configurazione via XML sono diventati legati l’uno all’altro. Ci tengo a precisare che non è così.
Il motto di DI è “non istanziare le risorse di cui hai bisogno, sarò io a fornirtele”, quindi DI riguarda (a) l’istanziazione dei componenti e (b) il collegamento - wiring - dei componenti fra di loro. DI dice che istanziazione e wiring non sono responsabilità dei componenti ma di un entità dedicata che può chiamarsi builder (mettendo in evidenza l’istanziazione) o container (mettendo in evidenza alcune funzioni dinamiche di cui non parlerò).
Come questo builder/container è configurato può variare ma non è importantissimo. E’ quella che io chiamo una differenza che non fa la differenza. Spring dice che si fa in XML, PicoContainer dice che si fa programmaticamente, Guice dice che si fa con le annotazioni (Nota: ultimamente tutti fanno di tutto. Qui ho schematizzato un po’).
Il mio amico Giampiero ha mostrato che può bastare un po’ di reflection. Ma anche se scrivete un bel main() che in modo pulito si occupa di istanziare (senza reflection) e collegare i vari componenti, state comunque facendo della DI.
Una differenza che fa la differenza
Ma allora qual è l’essenza della DI? Proviamo a fare un esempio, anche provocatorio. Partiamo da un semplice servlet (conforme alla servlet API standard) e proviamo a riscriverlo in ’spirito’ DI (finirò per modificare la servlet API. Non è una cosa che suggerisco veramente di fare. E’ solo un esempio ma appunto è provocatorio). Ecco il servlet di partenza:

Il metodo principale è doGet() che estrae un parametro dalla richiesta e stampa un semplice messaggio nella risposta. Il metodo init() fornisce al servlet un ServletConfig che dietro le quinte (nella superclass HttpServlet) viene memorizzato. doGet() non fa uso del ServletConfig ma, in linea di principio, potrebbe.
Vediamo come si potrebbe riscrivere il servlet:

Cose da notare:
- ServletConfig, req e resp sono diventati property della classe.
- Le property sono accessibili tramite getter (omessi per semplicità) e setter.
- Il vecchio metodo
doGet()ha perso i parametri ed è stato rinominatoprocessRequest(). - La classe implementa una nuova interfacca
ServletInterface
Abbiamo profondamente cambiato il concetto di servlet e il modo in cui il servlet container dovrebbe interagire con esso. Il container doverebbe:
- istanziare il servlet
- configurarlo tramite i setter
- chiamare
processRequest()senza parametri
E’ interessante notare la nuova interfaccia del servlet:

Analisi dell’esempio
Nell’esempio sono successe tre cose.
Primo, il servlet dichiara tutte le sue dipendenze (servletConfig, req, resp) come property. Le dipendenze sono qualunque cosa il servlet possa o voglia mai utilizzare durante la sua esistenza.
Secondo, processRequest() ha perso i parametri ed è diventato, se guardiamo l’interfaccia ServletInterface, più astratto. Non c’è più niente che dice che il metodo lavora su richieste HTTP. Potrebbero essere richieste di qualunque altro tipo.
Terzo, in conseguenza dei due punti precedenti, “che cosa fa il servlet” e “quali risorse gli servono per farlo” sono stati separati. Se al servlet servisse un’altra risorsa (p.e. una connessione DB già aperta) questa potrebbe essere inserita come proprietà senza cambiare la signature di processRequest().
La prova del nove
Si potrebbe obbiettare che il servlet alla nuova maniera ha un problema. ServletConfig, req e resp sono attributi della classe e noi sappiamo che, per un servlet, questa è un’operazione pericolosa. Se infatti ci fosse una sola istanza del servlet e più richieste contemporanee da gestire, le richieste andrebbero in conflitto.
La risposta è che il container potrebbe creare un’istanza del servlet per ogni nuova richiesta oppure potrebbe creare un pool di oggetti che si alternano nella gestione delle richieste. Da notare che questi accorgimenti riguardano tutti l’istanziazione e il wiring, proprio le attività cui è preposto il builder/container in un approccio basato su DI.
In questo senso la DI ha una sua coerenza interna.
Conclusioni
Ok. Ho qualche problema a trarre conclusioni questa volta. Sto consigliando di usare uno dei vari IoC/DI container? No, non era argomento del blog. Sto consigliando di usare la DI come design pattern? Sì e no a seconda del vostro giudizio.
Quello che ho voluto fare è separare la DI dalle mille cose che normalmente si trascina dietro (i container, l’AOP, le librerie Spring, l’esaltazione dei POJO come panacea di tutti i mali, ecc), andare al cuore del problema e imparare qualcosa che migliori i miei programmi. Spero esserci riuscito :-)

