Parametri opzionali o costruttori sovraccarichi


28

Sto implementando un DelegateCommand, e quando stavo per implementare il / i costruttore / i, ho escogitato le seguenti due scelte progettuali:

1: Avere più costruttori sovraccaricati

public DelegateCommand(Action<T> execute) : this(execute, null) { }

public DelegateCommand(Action<T> execute, Func<T, bool> canExecute)
{
    this.execute = execute;
    this.canExecute = canExecute;
}

2: avere un solo costruttore con un parametro opzionale

public DelegateCommand(Action<T> execute, Func<T, bool> canExecute = null)
{
    this.execute = execute;
    this.canExecute = canExecute;
}

Non so quale usare perché non so quali possibili vantaggi / svantaggi derivano da uno dei due modi proposti. Entrambi possono essere chiamati in questo modo:

var command = new DelegateCommand(this.myExecute);
var command2 = new DelegateCommand(this.myExecute, this.myCanExecute);

Qualcuno può indicarmi la giusta direzione e dare un feedback?


4
KISS e YAGNI. In caso di dubbi, implementare entrambi. Dopo un po ', fai una ricerca di riferimenti ad entrambi i costruttori. Non mi sorprenderebbe se uno di loro non fosse mai consumato esternamente. O consumato marginalmente. Questa è una cosa facile da trovare eseguendo analisi statiche del codice.
Laiv,

1
Anche i costruttori statici (come Bitmap.FromFile) sono un'opzione
BlueRaja - Danny Pflughoeft

3
Mantieni la regola di analisi del codice CA1026: i parametri predefiniti non devono essere presi in considerazione.
Dan Lyons,

2
@Laiv mi sto perdendo qualcosa? Sono usati allo stesso modo e quindi hanno la stessa firma. Non è possibile cercare riferimenti e dire a chi stava pensando il chiamante. Sembra più che tu ti stia riferendo al fatto che l'OP debba o meno avere quel secondo argomento, ma non è questo ciò che l'OP sta chiedendo (e potrebbero ben sapere con certezza che a volte hanno bisogno di entrambi, quindi li chiedono).
Kat

2
@Voo Il compilatore per Openedge | Progress 10.2B li ignora. Praticamente ha ucciso la nostra strategia di cambiamento di metodo senza interruzioni; invece, abbiamo dovuto usare il sovraccarico per lo stesso effetto.
Bret,

Risposte:


24

Preferisco più costruttori rispetto ai valori predefiniti e personalmente non mi piace il tuo esempio di due costruttori, dovrebbe essere implementato in modo diverso.

Il motivo dell'utilizzo di più costruttori è che quello principale può semplicemente verificare se tutti i parametri non sono nulli e se sono validi mentre altri costruttori possono fornire valori predefiniti per quello principale.

Nei tuoi esempi, tuttavia, non c'è alcuna differenza tra loro perché anche il costruttore secondario passa un nullvalore predefinito e anche il costruttore principale deve conoscere il valore predefinito. Penso che non dovrebbe.

Ciò significa che sarebbe più pulito e meglio separato se implementato in questo modo:

public DelegateCommand(Action<T> execute) : this(execute, _ => true) { }

public DelegateCommand(Action<T> execute, Func<T, bool> canExecute)
{
    this.execute = execute ?? throw new ArgumentNullException(..);
    this.canExecute = canExecute ?? throw new ArgumentNullException(..);
}

notate il _ => truepassaggio al costruttore principale che ora controlla anche tutti i parametri nulle non si preoccupa di alcuna impostazione predefinita.


Il punto più importante tuttavia è l'estensibilità. Più costruttori sono più sicuri quando esiste la possibilità di estendere il codice in futuro. Se aggiungi altri parametri richiesti e quelli opzionali devono arrivare alla fine, interromperai tutte le tue attuali implementazioni. Puoi creare il vecchio costruttore [Obsolete]e informare gli utenti che verrà rimosso, dando loro il tempo di migrare alla nuova implementazione senza rompere immediatamente il loro codice.


D'altra parte, rendere troppi parametri facoltativi sarebbe anche fonte di confusione perché se alcuni di essi sono richiesti in uno scenario e facoltativi in ​​un altro, è necessario studiare la documentazione invece di scegliere semplicemente il costruttore giusto semplicemente osservandone i parametri.


12
D'altra parte, rendere troppi parametri facoltativi sarebbe fonte di confusione ... Ad essere onesti, avere troppi parametri (indipendentemente dal fatto che siano facoltativi o meno) è fonte di confusione.
Andy,

1
@DavidPacker Sono d'accordo con te, ma quanti parametri sono troppi è un'altra storia, penso ;-)
t3chb0t

2
C'è un'altra differenza tra avere un costruttore che omette un parametro rispetto a un costruttore che ha un valore predefinito per il parametro. Nel primo caso, il chiamante può evitare esplicitamente di passare il parametro, mentre in quest'ultimo caso il chiamante non può - perché passare nessun parametro equivale a passare il valore predefinito. Se il costruttore deve fare questa distinzione, allora due costruttori sono l'unica strada da percorrere.
Gary McGill,

1
Ad esempio, supponiamo di avere una classe che formatta le stringhe, che posso facoltativamente costruire con un CultureInfooggetto. Io preferirei un'API che ha permesso il CultureInfoparametro da essere alimentato attraverso un parametro aggiuntivo, ma ha insistito che se è stata fornita allora dovrebbe non essere null. In questo modo i nullvalori accidentali non vengono interpretati erroneamente come "nessuno".
Gary McGill,

Non sono sicuro che l'estensibilità sia davvero una ragione contro i parametri opzionali; se hai richiesto tutti i parametri e hai mai cambiato il numero di essi dovrai creare un nuovo costruttore e Obsoletequello vecchio se vuoi evitare di rompere le cose, che tu abbia anche parametri opzionali o no. Con i parametri opzionali devi solo Obsoletese ne rimuovi uno.
Herohtar,

17

Considerando che l'unica cosa che stai facendo nel costruttore è la semplice assegnazione, la soluzione del singolo costruttore potrebbe essere una scelta migliore nel tuo caso. L'altro costruttore non fornisce funzionalità extra ed è chiaro dalla progettazione del costruttore con due parametri che non è necessario fornire il secondo argomento.

Più costruttori hanno senso quando si costruisce un oggetto di diversi tipi. In tal caso, i costruttori sostituiscono una fabbrica esterna perché elaborano i parametri di input e li formattano in una corretta rappresentazione della proprietà interna della classe che viene costruita. Ciò non accade tuttavia nel tuo caso, quindi perché un singolo costruttore dovrebbe essere più che sufficiente.


3

Penso che ci siano due casi diversi per i quali ogni approccio può brillare.

In primo luogo, quando abbiamo semplici costruttori (che di solito è il caso, nella mia esperienza), considererei gli argomenti opzionali brillanti.

  1. Riducono al minimo il codice che deve essere scritto (e quindi anche che deve essere letto).
  2. Puoi assicurarti che la documentazione sia in un posto e non venga ripetuta (il che è male perché apre un'area extra che potrebbe essere obsoleta).
  3. Se hai molti argomenti opzionali, puoi evitare di avere un insieme molto confuso di combinazioni di costruttori. Diamine, anche con solo 2 argomenti opzionali (non correlati tra loro), se volevi costruttori separati e sovraccarichi, dovresti avere 4 costruttori (versione senza nessuno, versione con ciascuno e versione con entrambi). Questo ovviamente non si adatta bene.

Buuuut, ci sono casi in cui argomenti opzionali nei costruttori rendono le cose chiaramente più confuse. L'esempio ovvio è quando questi argomenti opzionali sono esclusivi (cioè non possono essere usati insieme). Avere costruttori sovraccarichi che consentano solo le combinazioni di argomenti effettivamente valide garantirebbe la compilazione dei tempi di questo vincolo. Detto questo, dovresti anche evitare di incontrare questo caso (ad esempio, con più classi che ereditano da una base, in cui ogni classe ha il comportamento esclusivo).


+1 per menzionare l'esplosione della permutazione con più parametri opzionali.
Jpsy,
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.