Oggetto di configurazione vs funzione

3

Sto progettando una API configurabile e ho visto alcuni modi per accettare le opzioni dal chiamante.

Uno sta usando un oggetto opzioni in questo modo:

var options = new MyApiConfigurationOptions {
    Option1 = true,
    Option2 = false
};

var api1 = MyApiFactory.Create(options);

Un altro sta usando una funzione di configurazione:

var api2 = MyApiFactory.Create(o => {
    o.Option1 = true;
    o.Option2 = false;
});

C'è un approccio migliore / peggiore / diverso rispetto all'altro? C'è qualche differenza o sarebbe bello supportare entrambi in modo che il chiamante possa usare qualsiasi sintassi che preferiscono?

    
posta Eric B 05.01.2017 - 19:34
fonte

6 risposte

4

L'approccio alla funzione, pur apparendo meno intuitivo a prima vista, ha un paio di vantaggi significativi:

  1. La funzione viene fornita con l'oggetto config, ovvero l'API ha il controllo totale sulla creazione dell'oggetto. Ad esempio, potrebbe avere costruttori solo interni, garantendo che solo l'API stessa possa creare l'oggetto.
  2. L'API può supportare l'inizializzazione ritardata in piena regola. L'API non solo può eseguire l'inizializzazione pigra di se stessa, ma può quindi richiamare il codice principale in modo ritardato per i dati di configurazione. Un simile approccio può rivelarsi davvero utile quando, ad esempio, scandaglia un sistema tramite IoC, consentendo l'inserimento di riferimenti all'oggetto API in vari oggetti e successivamente la configurazione fornita all'API.

È fin troppo facile ignorare questo approccio come " essere semplicemente di fantasia per amore" "o" oscuro e inutile ". Ma prenditi il tempo per capire i benefici ed è facile capire perché può essere un meccanismo potente (e semplice da comprendere una volta che il concetto è stato compreso).

Ovviamente, se hai bisogno che l'approccio alla funzione dipenda completamente dalla tua API. Se nessuno dei suddetti punti offre vantaggi evidenti al tuo codice, più sviluppatori troveranno la tua API più facile da usare se adotterai l'approccio più semplice.

    
risposta data 05.01.2017 - 21:59
fonte
6

La versione che assume una funzione è oscura e inutile. L'unica ragione per farlo è se è necessario calcolare le opzioni in un secondo momento. La versione che prende l'oggetto è molto più leggibile e in realtà ha senso.

    
risposta data 05.01.2017 - 20:07
fonte
4

Penso che sia una questione di gusti. Sono un grande fan delle interfacce fluenti, quindi preferisco scrivere qualcosa del tipo:

var api1 = MyApiFactory.Create().Option1(true).Option2(true)

Ciò detto, ritengo che l'approccio alla funzione di configurazione abbia il vantaggio di mantenere le mani dell'utente fuori dai tuoi tipi interni (come MyApiConfigurationOptions ) e fornisce un migliore incapsulamento.

    
risposta data 05.01.2017 - 20:26
fonte
1

Sono molto diversi e se non stai attento potresti fare a pezzi i tuoi sviluppatori.

Nel primo snip del codice, le opzioni vengono lette e passate immediatamente, e possono essere applicate all'istanza dell'oggetto creato immediatamente o successivamente se c'è un'inizializzazione pigra. Ma penso che sarà abbastanza chiaro quali opzioni finiranno per essere impostate.

Nel secondo snip del codice, le opzioni potrebbero essere lette in seguito se l'oggetto creato utilizza l'inizializzazione pigra. Ciò significa che potrebbe verificarsi una chiusura in alcuni casi. Considera questo codice:

bool myOption = true;
var api2 = MyApiFactory.Create(o => {
    o.Option1 = myOption;
});
myOption = false;
Console.WriteLine(api2.Option1);

Cosa pensi che verrà prodotto? :) Penso che sia ambiguo, data solo l'interfaccia. Dovremmo effettivamente guardare all'interno della fabbrica per capire cosa accadrà.

Ora ovviamente c'è una terza opzione, che è così ovvia che potrebbe funzionare.

var api2 = MyApiFactory.Create();
o.Option1 = true;
o.Option2 = false;

Ovviamente non è così elegante. Ma è completamente non ambiguo.

    
risposta data 06.01.2017 - 00:43
fonte
-1

Preferisco prendere l'oggetto ma più da un punto di vista di test di unità e di iniezione di dipendenza. Quando scrivi un test unitario, è più facile creare un gruppo di oggetti derisi e poi creare un gruppo di lambda. In unit test potrei scrivere un assert generico in cui le opzioni. Option1 è uguale a api2.Settings.Option1 e se Option1 è true o false, posso chiamare questo metodo. Con lambda ho bisogno di passare un codice hard true o false da confrontare poiché il test unitario non avrà accesso ai valori che ho impostato nel lambda.

Anche per l'iniezione delle dipendenze posso registrare l'oggetto da inviare ma iniettare un lambda con le opzioni che desidero è più confuso per chiunque venga dopo di me. Anche in questo caso puoi eseguire sia il test DI che il test dell'unità con lambda, ma devi lavorarci un po 'di più.

Solo il tempo che vorrei fare è lambda se all'interno del factory statico era necessario impostare una proprietà o avere un costruttore interno che solo la classe factory poteva fornire.

Gran parte di questa è solo la preferenza degli sviluppatori.

    
risposta data 05.01.2017 - 22:19
fonte
-2

Sarà meglio fornire l'opzione oggetto a lungo termine. Per essere completamente sinceri, fare la cosa funzione è solo essere fantasioso per amore della fantasia .

Quando usi l'oggetto, incapsulerai il codice e semplificheresti il debugging. Un sacco di cose che sono difficili da debugare (ammettilo, hai avuto problemi con esso) si verificano nelle dichiarazioni Lambda. Non mi piacciono, e apportare un cambiamento a quel codice sottostante che opera su di loro è una seccatura.

Tuttavia, con l'oggetto di configurazione sei in grado di incapsulare il codice, espandere più facilmente e riutilizzare il codice in un secondo momento senza doverlo fare. Pertanto, a lungo termine è più semplice mantenere e implementare un oggetto.

    
risposta data 05.01.2017 - 20:39
fonte

Leggi altre domande sui tag