Consigli sull'integrazione del contenitore DI / IoC in un'applicazione esistente


10

Sono ora di fronte all'integrazione di un contenitore di inversione di controllo (IoC) in un'applicazione esistente e sto cercando alcuni consigli su come realizzare più facilmente l'obiettivo finale di ridurre l'accoppiamento, aumentando così la testabilità. Anche se generalmente non classificherei la maggior parte delle classi come oggetti divini , ognuna ha troppe responsabilità e dipendenze nascoste attraverso statica, caratteri singolari e mancanza di interfacce.

Ecco un po 'di background alcune delle sfide che devono essere affrontate:

  • L'iniezione di dipendenza è usata raramente
  • I metodi statici abbondano, sia come metodi di fabbrica che di supporto
  • I singoli sono abbastanza diffusi
  • Le interfacce, quando utilizzate, non sono troppo granulari
  • Gli oggetti spesso trascinano dipendenze non necessarie attraverso le classi di base

Il nostro intento è che la prossima volta che avremo bisogno di apportare modifiche in una determinata area, che cercheremo di stuzzicare le dipendenze che, in realtà, esistono ma sono nascoste dietro globi come singoli e statici.

Suppongo che ciò renda il contenitore dell'IoC secondario all'introduzione dell'iniezione di dipendenza, ma mi aspetto che ci sia una serie di pratiche e raccomandazioni che potrebbero essere seguite o considerate che ci aiuteranno a eliminare queste dipendenze.


3
Devo chiedere il motivo per farlo ora ... cosa sta guidando questo cambiamento? Manutenibilità? Scalabilità poiché crescerà esponenzialmente? Gli sviluppatori sono annoiati?
Aaron McIver il

1
Di recente mi sono unito all'azienda e sto cercando di introdurre alcune "migliori pratiche". Il mio obiettivo principale è aumentare la testabilità e ridurre l'accoppiamento. Possiamo facilmente usare DI / IoC per il nuovo codice e non intendiamo provare a cambiare tutto il codice esistente in una volta, ma vorrei consigli su come possiamo cambiare meglio il codice esistente in meglio la prossima volta che stiamo facendo cambiamenti in quella zona. Finora l'unica cosa che ho trovato online è la seguente: code.google.com/p/autofac/wiki/ExistingApplications
Kaleb Pederson

3
A meno che non siano in atto numerosi test automatizzati di integrazione / unità; la modifica dell'infrastruttura esistente, a meno che non esista un problema ai fini delle migliori pratiche, sta richiedendo problemi. Anche se la tua suite di test di integrazione / unità era solida, avevo ancora esitazioni.
Aaron McIver il

1
@Aaron Spero che non sembra che lo stiamo facendo per il bene delle migliori pratiche. Stiamo apportando modifiche perché è difficile e lento lavorare con il codice esistente e farlo in modo frammentario mentre lavoriamo in quella particolare area. Volentieri, abbiamo una serie di test di integrazione ragionevoli e alcuni test unitari per supportare le modifiche.
Kaleb Pederson,

Risposte:


8

Per realizzare qualcosa del genere, dovrai lavorare a passi, ognuno di questi non è banale ma possono essere realizzati. Prima di iniziare, devi capire che non puoi affrettare questo processo.

  1. Definire i principali sottosistemi dell'applicazione e uno interfaceper ciascuno di essi. Questa interfaccia dovrebbe definire solo i metodi che altre parti del sistema useranno per parlarci. NOTA: potrebbe essere necessario eseguire più di un passaggio.
  2. Fornire un'implementazione wrapper di tale interfaccia che deleghi al codice esistente. Lo scopo di questo esercizio è di evitare di riscrivere in massa , ma di riformattare il codice in modo da utilizzare le nuove interfacce, ovvero ridurre l'accoppiamento nel sistema.
  3. Imposta il contenitore IoC per costruire il sistema usando le interfacce e le implementazioni che hai creato. A questo punto, devi occuparti di creare un'istanza del contenitore IoC in modo che possa visualizzare l'applicazione. Vale a dire se ci si trova in un ambiente servlet, assicurarsi di poter ottenere / creare il contenitore nel init()metodo servlet .
  4. Fai di nuovo la stessa cosa all'interno di ciascun sottosistema, questa volta quando esegui il refactoring, stai trasformando l'implementazione dello stub nella cosa reale che a sua volta utilizza le interfacce per parlare con i suoi componenti.
  5. Ripetere se necessario fino ad avere un buon equilibrio tra dimensione del componente e funzionalità.

Quando hai finito, gli unici metodi statici che dovresti avere nel tuo sistema sono quelli che sono veramente funzioni, ad esempio guarda la Collectionsclasse o la Mathclasse. Nessun metodo statico dovrebbe tentare di accedere direttamente ad altri componenti.

Questo è qualcosa che richiederà molto tempo, pianificare di conseguenza. Assicurati che man mano che esegui il refactoring diventi più SOLIDO nel tuo approccio progettuale. All'inizio sarà doloroso. Stai cambiando drasticamente il design dell'applicazione in modo iterativo.


Buoni suggerimenti. Faccio riferimento anche agli altri suggerimenti qui quando apporto
Kaleb Pederson

7

Continua a lavorare in modo efficace con il codice legacy e segui i suoi consigli. Inizia costruendo isole di codice coperto e spostati gradualmente verso un'applicazione più ben strutturata. Cercare di apportare il cambiamento in massa è una ricetta per il disastro.


Adoro quel libro !!
Martijn Verburg,

Buona raccomandazione Anche se ne ho letto la metà anni fa, immagino che ne trarrebbe molto di più ora che sono profondamente in mezzo alla situazione.
Kaleb Pederson,

È divertente, la risposta selezionata riassume sostanzialmente il libro;) Naturalmente Feathers entra molto più in dettaglio.
Michael Brown,

5

Il motivo principale per l'introduzione dell'IoC è il disaccoppiamento dei moduli. Il problema con Java, in particolare, è lo straordinario legame forte che l' newoperatore offre, combinato con il fatto che il codice chiamante sa esattamente quale modulo utilizzerà.

Le fabbriche sono state introdotte per spostare questa conoscenza in una posizione centrale, ma alla fine o si ancora cablano i moduli nell'uso di new/ singleton che mantiene il legame rigido, oppure si legge in un file di configurazione e si usa reflection / Class.forName che è fragile quando si refactoring .

Se l'obiettivo non è modularizzato, l'IoC non ti darà nulla.

L'introduzione di Unit test molto probabilmente cambierà questo, poiché dovrai deridere i moduli che non sono in fase di test e il modo più semplice per gestirlo, così come i moduli di produzione effettivi con lo stesso codice è utilizzare un framework IoC per iniettare l'appropriato moduli.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.