L'Iniezione delle Dipendenze di Poor Man è un buon modo per introdurre la testabilità su un'applicazione legacy?

11

Nell'ultimo anno, ho creato un nuovo sistema utilizzando Dependency Injection e un container IOC. Questo mi ha insegnato molto su DI!

Tuttavia, anche dopo aver appreso i concetti e gli schemi appropriati, ritengo sia una sfida disaccoppiare il codice e introdurre un contenitore IOC in un'applicazione legacy. L'applicazione è abbastanza grande al punto che una vera implementazione sarebbe schiacciante. Anche se il valore è stato compreso e il tempo è stato concesso. A chi è concesso tempo per qualcosa di simile ??

L'obiettivo, naturalmente, è portare i test unitari alla logica aziendale!
Logica di business che si intreccia con le chiamate al database che impediscono il test.

Ho letto gli articoli e capisco i pericoli della Dipendenza da Iniezione di Poor Man come descritto in questo articolo di Los Techies . Capisco che veramente non disaccoppia nulla.
Capisco che possa coinvolgere molto il refactoring del sistema poiché le implementazioni richiedono nuove dipendenze. Non prenderei in considerazione l'utilizzo su un nuovo progetto con qualsiasi dimensione.

Domanda: Va bene usare DI di Poor Man per introdurre testabilità su un'applicazione legacy e avviare il lancio?

Inoltre, sta utilizzando il DI di Poor Man come approccio di base alla vera Iniezione di dipendenza, un modo valido per educare alla necessità e ai benefici del principio?

È possibile refactoring un metodo che ha una dipendenza di chiamata database e abstract che chiama dietro un'interfaccia? Il semplice fatto di avere quell'astrazione renderebbe quel metodo testabile dal momento che un'implementazione simulata potrebbe essere passata attraverso un overload di costruttore.

Lungo la strada, una volta che lo sforzo guadagna sostenitori, il progetto potrebbe essere aggiornato per implementare un container IOC e i costruttori sarebbero là fuori a prendere le astrazioni.

    
posta Airn5475 16.01.2018 - 20:54
fonte

3 risposte

23

La critica di Poor Man's Injection su NerdDinner ha meno a che fare con l'utilizzo di un contenitore DI piuttosto che con impostando correttamente le classi.

Nell'articolo, affermano che

public class SearchController : Controller {

    IDinnerRepository dinnerRepository;

    public SearchController() : this(new DinnerRepository()) { }

    public SearchController(IDinnerRepository repository) {
        dinnerRepository = repository;
    }
}

non è corretto perché, mentre il primo costruttore fornisce un comodo meccanismo di fallback per la costruzione della classe, crea anche una dipendenza strettamente vincolata a DinnerRepository .

Ovviamente il rimedio corretto non è, come suggerisce Los Techies, aggiungere un contenitore DI, ma piuttosto rimuovere il costruttore dannoso.

public class SearchController : Controller 
{
    IDinnerRepository dinnerRepository;

    public SearchController(IDinnerRepository repository) {
        dinnerRepository = repository;
    }
}

La classe rimanente ora ha le sue dipendenze correttamente invertite. Ora sei libero di iniettare quelle dipendenze come preferisci.

    
risposta data 16.01.2018 - 23:36
fonte
15

Qui fai una falsa ipotesi su cosa sia il "DI uomo povero".

La creazione di una classe che ha un costruttore di "collegamento" che ancora crea l'accoppiamento non è DI dell'individuo.

Non utilizzando un contenitore e creando manualmente tutte le iniezioni e le mappature, è DI povero.

Il termine "DI poveri" sembra una cosa brutta da fare. Per questo motivo, il termine "puro DI" è incoraggiato in questi giorni come sembra più positivo e in realtà descrive più accuratamente il processo.

Non solo è assolutamente bene usare il DI puro / povero per introdurre DI in un'app esistente, ma è anche un modo valido di usare DI per molte nuove applicazioni. E come dici tu, tutti dovrebbero usare puro DI su almeno un progetto per capire veramente come funziona il DI, prima di gestire la responsabilità sulla "magia" di un contenitore IoC.

    
risposta data 17.01.2018 - 10:28
fonte
1

I cambiamenti paragidici nei team / basi di codice legacy sono estremamente rischiosi:

Any time you suggest "improvements" to legacy code and there are "legacy" programmers on the team, you are just telling everyone that "they did it wrong" and you become The Enemy for the rest of your/their time with the company.

L'utilizzo di un DI Framework come martello per distruggere tutte le unghie renderà il codice legacy ancora peggiore che in tutti i casi. È anche estremamente rischioso personalmente.

Anche nei casi più limitati, così come può essere utilizzato nei casi di test, solo quei codici di test "non standard" e "estranei" che nel migliore dei casi verranno contrassegnati come @Ignore quando si rompono o peggiori si lamentano costantemente dei programmatori legacy con il maggior peso di gestione e vengono riscritti "correttamente" con tutto questo "tempo sprecato sui test unitari" incolpato unicamente su di te.

Introducing a DI framework, or even the concept of "Pure DI" to a line of business app, much less a huge legacy code base without the by off of management, the team and especially sponsorship of the lead developer will just be the death knell for you socially and politically on the team/company. Doing things like this is extremely risky and can be political suicide in the worst way.

Dipendenza dall'iniezione è una soluzione alla ricerca di un problema.

Any language with constructors by definition uses dependency injection by convention if you know what you are doing and understand how to properly use a constructor, this is just good design.

L'iniezione di dipendenza è utile solo in piccole dosi per un intervallo molto ristretto di cose:

  • Cose che cambiano molto o che hanno molte implementazioni alternative vincolate staticamente.

    • I driver JDBC sono un esempio perfetto.
    • Client HTTP che potrebbero variare da piattaforma a piattaforma.
    • Sistemi di registrazione che variano in base alla piattaforma.
  • Sistemi di plugin con plug-in configurabili che possono essere definiti nel codice di configurazione nel framework e rilevati automaticamente all'avvio e caricati / ricaricati dinamicamente mentre il programma è in esecuzione.

risposta data 17.01.2018 - 01:03
fonte