Iniezione di dipendenza vs metodi statici


21

Oggi ho avuto un'interessante discussione con un altro sviluppatore su come affrontare una classe con un metodo che accetta una stringa e genera una stringa.

Immagina qualcosa di simile al seguente, completamente inventato a scopo di esempio

public string GetStringPart(string input)
{ 
   //Some input validation which is removed for clarity

   if(input.Length > 5)
        return input.Substring(0,1);

   if(input.Substring(0,1) == "B")
        return input.Substring(0,3);

   return string.empty;
}

Una funzione che ha una logica basata sul suo input di stringa viene aggiunta a un progetto usando DI e ha un contenitore DI in atto. Aggiungeresti questa nuova classe con un'interfaccia e la inietteresti dove necessario, o la trasformeresti in una classe statica? Quali sono i pro ed i contro di ognuno? Perché vorresti (o no) rendere questo qualcosa usato con l'iniezione del costruttore piuttosto che accedervi quando richiesto ovunque.


1
@Ewan Per i metodi di supporto che non servono per l'astrazione. Esempio: Apache FileUtils.
Walfrat,

3
@Ewan: i metodi statici senza effetti collaterali sono il miglior tipo di metodi, perché sono semplici da capire e semplici da testare.
JacquesB,

1
ma rendono impossibile testare le cose che dipendono da loro
Ewan

3
@Ewan Eh, non proprio. Math.Max ​​() è male perché è statico? Se si verifica il metodo statico e funziona, è possibile utilizzarlo in modo sicuro su altri metodi senza problemi. Se quello statico fallisce, il suo test lo catturerà.
T. Sar - Ripristina Monica il

1
se "statico" non garantisse effetti collaterali potrei essere in grado di vedere l'argomento. ma non lo fa.
Ewan,

Risposte:


25

Non vi è alcun motivo per cui questo debba essere iniettato. Questa è solo una funzione, non ha dipendenze, quindi chiamala. Può anche essere statico se lo si desidera in quanto sembra essere puro. Si può scrivere test unitari contro questo senza difficoltà. Se viene utilizzato in altre classi, è ancora possibile scrivere unit test.

Non è necessario sottrarre funzioni senza dipendenze, è eccessivo.

Se questo diventa più complesso, allora è garantito il passaggio di un'interfaccia in un costruttore o in un metodo. Ma non seguirei quella strada se non avessi una GetStringPartlogica complessa basata sulla posizione, ecc.


2
Questa è la risposta corretta! Peccato che la risposta cult-cargo sia stata accettata.
JacquesB,

3
che la funzione non ha dipendenze non è il punto. il problema è quando altre cose dipendono dalla funzione e si accoppiano strettamente ad essa
Ewan,

2
Che cosa succede se la funzione cambia in 6 mesi e non è così pura, ma è già utilizzata in molti luoghi. Non è estensibile come statico e non è nemmeno qualcosa che può essere isolato dal consumo di test unitari delle classi. Se c'è anche una leggera possibilità di cambiamento, farei l'iniezione. Detto questo, sono aperto all'ascolto di argomenti opposti, questo è il punto di questo post. Quali sono gli svantaggi di questo essere iniettato e positivi di una statica?
James,

1
In generale, le persone che parlano dell'iniezione di dipendenza su questo sito Web non hanno bisogno di iniezione di dipendenza. Da qui la tendenza verso una complessità non necessaria.
Frank Hileman,

2
@James Se la funzione diventa meno pura, stai facendo qualcosa di sbagliato e la funzione probabilmente non è stata ben definita dall'inizio. Il calcolo dell'area di un quadrato non dovrebbe improvvisamente richiedere una chiamata al database o distribuire un aggiornamento visivo per l'interfaccia utente. Riesci a immaginare uno scenario in cui un metodo "Sottostringa" diventa improvvisamente impuro?
T. Sar - Ripristina Monica il

12

Ecco perché

class DOSClient {
    OrderParser orderParser;
    string orderCode;

    DOSClient(OrderParser orderParser, string ordercode) { 
        this.orderParser = orderParser; 
        this.ordercode = ordercode;
    }
    void DisplayOrderCode() {
        Console.Write( "Prefix: " + orderParser.GetStringPart(ordercode) ); 
        ...
    }
}

class GUIClient {
    OrderParser orderParser;
    string orderCode;
    GUI gui;

    GUIClient(OrderParser orderParser, string ordercode, GUI gui) { 
        this.orderParser = orderParser; 
        this.ordercode = ordercode;
        this.gui = gui;
    }

    void DisplayOrderCode() {
        gui.Prefix( orderParser.GetStringPart(ordercode) ); 
        ...
    }
}

 

class OrderParserUS : IOrderParser {

    public string GetStringPart(string input)
    { 
        //Some input validation which is removed for clarity

        if(input.Length > 5)
            return input.Substring(0,1);

        if(input.Substring(0,1) == "B")
            return input.Substring(0,3);

        return string.empty;
    }
}

class OrderParserEU : IOrderParser {

    public string GetStringPart(string input)
    { 
        //Some input validation which is removed for clarity

        if(input.Length > 6)
            return input.Substring(0,1);

        if(input.Substring(0,1) == "#")
            return input.Substring(0,3);

        return string.empty;
    }
}

Se avessi adottato un metodo statico, non ci sarebbe modo di cambiare il comportamento GetStringPartsenza distruggere il vecchio comportamento o inquinarlo con la logica condizionale. È vero che la statica è una malvagità globale sotto mentite spoglie, ma il fatto che disabilitino il polimorfismo è la mia principale lamentela. I metodi statici non sono di prima classe nei linguaggi OOP. Dando al metodo un oggetto in cui vivere, anche uno senza stato, rendiamo il metodo portatile. Il suo comportamento può essere passato come il valore di una variabile.

Qui ho immaginato un sistema che deve comportarsi in modo leggermente diverso quando distribuito in Europa rispetto a quando implementato negli Stati Uniti. Invece di forzare un sistema a contenere codice necessario solo all'altro, possiamo modificare il comportamento controllando l'ordine di analisi dell'oggetto iniettato nei client. Questo ci consente di contenere la diffusione dei dettagli della regione. Inoltre, semplifica l'aggiunta di OrderParserCanada senza dover toccare i parser esistenti.

Se questo non significa nulla per te, allora non c'è davvero un buon argomento per questo.

A proposito, GetStringPartè un nome terribile.


* Cringe allo stile del codice tra parentesi * Immagino che tu sia un programmatore Java che prova a scrivere codice C #? Prende uno per conoscerne uno, suppongo. :)
Neil

La domanda non è specifica per una lingua, più sul design. Il mio problema con i metodi statici è che impediscono l'isolamento del codice per i test. Lo sviluppatore con cui stavo parlando pensa di fare troppo affidamento su DI ea volte i metodi di estensione / metodi statici sono rilevanti. Penso che possibilmente in rari casi ma non come scopo generale. La discussione in corso che ho sentito è stata buona per discutere ulteriormente. Concordo anche con il tuo caso di polimorfismo.
James,

Il test è un motivo ma ce ne sono altri. Non mi piace dare la colpa ai test perché inizia a criticare i test. Il semplice fatto è che ai programmatori procedurali non piace se non si programma in modo procedurale.
candied_orange

Non potresti semplicemente dirlo static getStringPartEU()? Il tuo esempio ha senso solo se ci sono altri metodi in quella classe che richiedono anche il trattamento specializzato dell'UE e devono essere trattati come una singola unità.
Robert Harvey,

2
Stai decisamente discutendo a favore di complessità inutili. A tutto può essere aggiunto sempre più codice. Le cose che devono cambiare, dovrebbero essere facili da modificare, ma non dovresti cercare di prevedere cosa dovrebbe cambiare in futuro. I campi statici possono essere identici alle variabili globali, ma i metodi statici possono essere puri, il miglior tipo possibile di metodo.
Frank Hileman,
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.