Quante variabili dovrebbe avere un costruttore?

Mi rendo conto che questa è una domanda abbastanza aperta e potrebbe ottenere una varietà di risposte, ma qui va.

Usando C # (o Java, o qualsiasi linguaggio OO), esiste una regola generale che indica quante variabili devono essere passate nel costruttore? Il numero di variabili che sto passando al costruttore delle classi estese sembra sfuggire di mano.

Nel tentativo di incapsulare i dati di una class, dichiaro privati ​​i membri, li inizializzo nel mio costruttore e utilizzo i metodi di accesso pubblici.

Ecco un esempio:

public class A { private int var1; private int var2; private int var3; //3 variables passed in public A(int v1, int v2, int v3) { var1 = v1; var2 = v2; var3 = v3; } //Properties (accessors) here } public class B : A { private int var4; private int var5; //5 variables passed in public B(int v1, int v2, int v3, int v4, int v5) : base(v1,v2,v3) { var4 = v4; var5 = v5; } //Properties (accessors) here } public class C : B { private int var6; private int var7; //7 variables passed in !!! public C(int v1, int v2, int v3, int v4, int v5, int v6, int v7) : base(v1,v2,v3,v4,v5) { var6 = v6; var7 = v7; } //Properties (accessors) here } 

I miei costruttori di solito passano in oggetti diversi, non solo inti. Ho iniziato a mettere in discussione il mio progetto quando ho iniziato a passare 7 variabili al costruttore della class figlio, ma ho anche avuto problemi a trovare un modo diverso per farlo.

È considerata una ctriggers pratica di programmazione? C’è un limite generale al numero di variabili che dovresti passare in un costruttore?

Generalmente ho trovato se ci sono più di 3, questo è un segno per fare un rapido controllo di sanità mentale sul design. Se ce ne sono più di 5, è un avvertimento importante che probabilmente c’è qualcosa di sbagliato nel progetto.

Tuttavia, nota la parola “probabilmente” – alla fine, l’unica regola reale è usare quante ne sono necessarie per funzionare, né più né meno . Ci sono sempre eccezioni e casi in cui più parametri hanno più senso.

Se i parametri sono correlati in qualche modo, dovresti incapsularli in una class contenitore.

Se i parametri non sono correlati, ad esempio non ha senso raggrupparli in una class contenitore: la tua class probabilmente sta facendo troppe cose. Generalmente non c’è motivo per cui una singola class debba essere a conoscenza di 7 informazioni totalmente diverse. Rompi la tua class in classi separate. Potrebbe avere senso debind i parametri 3 e 4 ad una sottoclass e 5, 6 e 7 ad un’altra class per esempio – e la class genitore semplicemente coordina le operazioni tra di loro.

Per me, la risposta corretta è:

È necessario immettere tutte le variabili richieste per impostare l’object in uno stato non valido.

Qualsiasi altra cosa che sia “opzione”, preferisco lasciare come proprietà, specialmente ora che C # fornisce gli inizializzatori di oggetti.

È difficile mettere un numero duro e veloce in quello che è “troppo”. La vera domanda è questa: cosa sta facendo la tua class? La class sta facendo troppo? Se è così, è il momento di rompere la class in classi più piccole e più concise.

I parametri del costruttore dovrebbero includere il numero di volte necessario per definire le dipendenze / input per la class. Se la class è ridotta per avere un lavoro nella vita, allora i parametri del costruttore saranno probabilmente corretti.

Come altri hanno già detto, non c’è una regola difficile su questo, dipende davvero. Tuttavia, vi sono prove concrete su quante cose il cervello di una persona possa afferrare contemporaneamente: questa è la regola 7 + o – 2.

Questo tipo di domanda ha una risposta molto buona in Code Complete di Steve McConnell. Ti consiglio di leggere questo libro se non lo hai già fatto.

Una cosa che amo del framework 3.5 …

 new Foo { Street = "909 Rose Ave", City = "San Diego", State = "CA", FName = "Joe", LName = "Wazowski", ID = "987665454" }; 

Non dovrai più preoccuparti di troppi costruttori o troppi costruttori con troppi parametri.

La mia regola empirica personale è 5

Se hai bisogno di più, avvolgili in una struttura o in un object.

Questo cambia quando non è necessario inizializzare l’object. Se inizializzo un object utilizzando un contenitore IOC, il numero di parametri constructure è praticamente illimitato.

Soprattutto perché le nuove funzionalità che ti permettono di impostare le variabili in un blocco con l’istanziazione, tendo ad usare solo i parametri sulla creazione per cose che DEVONO essere impostate quando viene creata la class, nel qual caso renderò il costruttore di base privato o protetto .

Ad esempio, se hai un rettangolo di class, potrebbe avere senso creare il costruttore Rectangle (larghezza doppia, altezza doppia) e rendere privato il costruttore di Rectangle ().

Inoltre, potresti mettere un costruttore privato senza parametri, se ritieni che la tua class debba solo impostare i suoi valori di proprietà attraverso il costruttore.

Un sistema ha troppi parametri non appena diventa difficile ricordare come usarli. Se sono tutti ints, 3 sono troppi. Se sono tipi diversi puoi averne di più.

Questo è un odore nel libro Refactoring di Martin Fowler. Questa pagina web contiene collegamenti a refactoring standard che aiutano.

Nel tuo caso, potresti prendere in considerazione un object costruttore (dove puoi aggiungere gradualmente i parametri e il costruttore calcola la class) o un object parametro.

IMHO, usando TDD è una prospettiva utile per valutare questo problema. Una volta che il tuo censore è difficile da applicare all’unità test ctor o SetUp (), gli argomenti dell’inizializzazione / factory sono troppi e relativamente troppo strettamente accoppiati.

Devi passare tutto il necessario per build la class. Quello che faccio spesso quando mi trovo a passare molte variabili è creare una class “Configuration” per l’object che contiene le informazioni necessarie per costruirlo, passandolo al costruttore. Semplifica notevolmente la creazione di oggetti e rende il design più pulito.

Proverò a rispondere a questa domanda da una prospettiva diversa. Vedo due modi principali di utilizzare i costruttori, quindi due modi di pensare su quanti parametri sono troppi.

1) Costruttori esplicitamente chiamati

Questo è l’approccio classico in cui “nuovi” i tuoi oggetti e devi specificare tutti i valori dei parametri obbligatori. Questo è stato completamente coperto da altre risposte qui. La mia regola pratica personale è: tutti i parametri dovrebbero essere contenuti su una riga, quindi avrò un massimo di 3-4 parametri.

Inoltre, se sai che la tua class è in grado di gestire più casi che richiedono più parametri in futuro, andrei direttamente con una struct / class. Per esempio:

diciamo che una class dovrebbe gestire alcuni filtri degli oggetti sulla base di alcuni criteri. All’inizio ci saranno solo 2-3 criteri. Sapendo che probabilmente in futuro avrà molti più criteri, invierò direttamente un object FilterValue fin dall’inizio.

2) Costruttori implicitamente chiamati

Un caso tipico sta usando l’ iniezione di dipendenza . In quasi tutti i casi, vengono iniettati tutti i parametri del costruttore, quindi è compito dell’azienda DI framework creare gli oggetti per te.

Qui, il “senso comune” non si applica, in quanto si possono iniettare tutti i servizi necessari. Per esempio:

Se la persistenza dei dati viene eseguita utilizzando un modello di Unità di lavoro che aggrega le modifiche da un numero qualsiasi di repository, l’unità della class di lavoro verrà iniettata con tutti i repository utilizzati. Maggiori dettagli possono essere trovati all’interno di questa risposta da CodeReview .

A proposito, il numero massimo teorico di parametri per un metodo dovrebbe essere abbastanza alto da non raggiungerlo mai: C # e Java .