Se una classe è responsabile della propria configurazione

5

Nel mio posto di lavoro vedo un problema che ritengo sia sbagliato, ma sono aperto all'idea che forse il modo in cui lo faccio sia sbagliato.

Un esempio minore sarebbe una classe che accetta la configurazione

//pull config from config file, then pass in to class
public class SomeClass()
{
   public SomeClass(string connectionString, string someOtherRequiredConfiguration)
   {
      //Do stuff
   }
}

In confronto, una classe responsabile della propria configurazione

public class SomeClass()
{
    public SomeClass(){
       //Do work to pull from config file in constructor or in parameter
    }        
}

Penso che il secondo modo rende DI molto più semplice e anche la classe è responsabile del recupero della configurazione che gli interessa.

Esiste un metodo preferito?

    
posta James 13.08.2018 - 11:40
fonte

2 risposte

9

Entrambi non sono davvero grandiosi.

La prima versione è leggermente migliore, perché almeno tentativi di essere in grado di iniettare qualcosa.

Il problema è che le stringhe non possono essere iniettate in base al nome del parametro (almeno nessuno dei framework standard fa questo e sebbene teoricamente possibile, nessuno sano di mente potrebbe provarlo).

Hai bisogno di un tipo per iniettare correttamente qualcosa.

public interface ISomeClassOptions
{
    public string ConnectionString { get; }

    public string SomeOtherRequiredConfiguration { get; }
}

public class SomeClass(ISomeClassOptions options)
{
   //Do stuff
}

Questa classe può ora essere utilizzata senza molta configurazione manuale con un contenitore per le dipendenze. Hai solo bisogno di trovare un modo per riempire le tue opzioni da qualche parte.

Tuttavia, l'uso di una stringa di connessione e di altra configurazione mi porta a supporre che solo tu puoi verificare: la stringa di connessione non dovrebbe essere un'opzione. Invece il SomeClass dovrebbe probabilmente ottenere un IDbConnection o DbContext o un'altra classe iniettata che gestisca i dettagli nitty di cosa sia il database. Perché potrebbe essere un SqlConnection per la produzione o forse solo una connessione al database in memoria per il test. Spetta al contenitore decidere e non dovrebbe essere all'interno della tua logica SomeClass .

Se stai usando .NET Core, dai un'occhiata al nuovo Modello di opzioni .

    
risposta data 13.08.2018 - 13:56
fonte
4
//pull config from config file, then pass in to class
public class SomeClass(string connectionString, string 
someOtherRequiredConfiguration)
{
   //Do stuff
}

Il resto della tua risposta parla di DI, ma questo particolare frammento non è realmente DI-oriented. Questo è un normale costruttore parametrizzato run-of-the-mill.

Mi chiedo circa someOtherRequiredConfiguration . Sulla base del nome, assumerò che tu abbia semplificato l'esempio e che il codice reale contenga parametri significativi.

public class SomeClass()
{
    //Do work to pull from config file in constructor or in parameter
}

the class is responsible for retrieving the configuration it cares about

Correggere oggettivamente, ma questo non risponde se è una buona idea o no. Elaborerò su questo punto.

I think the second way makes DI much easier

Al contrario. Quello che hai fatto qui è rimosso l'opzione per configurare la tua classe nel modo che preferisci. Questo lo rende più difficile da simulare, non più facile. Stai costringendo te stesso a creare una nuova classe (probabilmente derivata) ogni volta che desideri testare una risorsa esterna diversa (= diversa connectiontring).

You could have an IConfigRepository or something if you wanted to control that separately.

Quello che dici non è sbagliato, ma voglio solo riformulare quello che hai detto: la tua soluzione proposta ora richiede di creare un IConfigRepository prima che tu possa testare correttamente. Non è impossibile farlo, ma crea più lavoro per avere codice verificabile, che è l'opposto di ciò che cerca DI di raggiungere (aumentando la testabilità)

But pulling the config values and passing them in at the composition root feels wrong to me.

Il fatto che tu ti riferisca a SomeClass come una composizione root mi fa sospettare che stai pensando a DI / testabilità in un modo insolito.

Il valore di un parametro (connectiontring) ha poco a che fare con l'iniezione delle dipendenze. L'iniezione delle dipendenze si concentra sulla combinazione di classi personalizzate in un modo che puoi separarle facilmente in futuro (ad esempio, testare un singolo componente o scambiare un particolare componente con un altro).

Should a class be responsible for its own configuration?

Dipende molto dalla configurazione su cui ti stai concentrando. Alcune cose non fanno parte di un file di configurazione e possono essere semplicemente impostate come valore const .

Se puoi garantire che SomeClass utilizzerà sempre una determinata chiave di configurazione, senza esito negativo, puoi recuperare la chiave dall'interno della classe (costruttore o altro).

Se SomeClass deve essere in grado di passare da diverse stringhe di connessione (ad esempio un'applicazione che utilizza più database contemporaneamente), deve essere configurabile. Un parametro costruttore è l'approccio migliore qui.

    
risposta data 13.08.2018 - 16:42
fonte

Leggi altre domande sui tag