Evitare costruttori con molti argomenti


10

Quindi ho una fabbrica che crea oggetti di diverse classi. Le possibili classi sono tutte derivate da un antenato astratto. Il factory ha un file di configurazione (sintassi JSON) e decide quale classe creare, a seconda della configurazione dell'utente.

Per raggiungere questo obiettivo, la fabbrica utilizza boost :: property_tree per l'analisi JSON. Cammina attraverso il ptree e decide quale oggetto concreto creare.

Tuttavia, gli oggetti prodotto hanno molti campi (attributi). A seconda della classe concreta, l'oggetto ha circa 5-10 attributi, in futuro forse anche di più.

Quindi non sono sicuro di come dovrebbe apparire il costruttore degli oggetti. Mi vengono in mente due soluzioni:

1) Il costruttore del prodotto si aspetta che ogni attributo sia un parametro, quindi il costruttore finirà con 10+ parametri. Questo sarà brutto e porterà a lunghe righe di codice illeggibili. Tuttavia, il vantaggio è che la factory può analizzare JSON e invocare il costruttore con i parametri corretti. La classe di prodotto non deve sapere che è stata creata a causa della configurazione JSON. Non ha bisogno di sapere che è coinvolto JSON o la configurazione.

2) Il costruttore del prodotto prevede solo un argomento, l'oggetto property_tree. Quindi può analizzare le informazioni necessarie. Se le informazioni sulla configurazione sono mancanti o fuori limite, ogni classe di prodotto può reagire correttamente. La fabbrica non ha bisogno di sapere quali argomenti sono necessari ai vari prodotti. Inoltre, la fabbrica non deve sapere come reagire in caso di configurazione errata. E l'interfaccia del costruttore è unificata e piccola. Ma, come svantaggio, il prodotto deve estrarre le informazioni necessarie da JSON, quindi sa come è costruito.

Tendo a preferire la soluzione 2). Tuttavia, non sono sicuro che questo sia un buon modello di fabbrica. Sembra in qualche modo sbagliato far sapere al prodotto che è stato creato con la configurazione JSON. Dall'altro lato, i nuovi prodotti possono essere introdotti in modo molto semplice.

Qualche opinione su questo?



1
Ho seguito il tuo link. C'è un esempio nella risposta più votata dal maniaco del cricchetto. Ma quale problema risolve questo approccio "builder"? C'è la riga di codice DataClass data = builder.createResult () ;. Ma il metodo createResults () - deve ancora ottenere i 10 parametri nell'oggetto DataClass. Ma come? Sembra che tu abbia solo un ulteriore livello di astrazione, ma il costruttore della DataClass non si riduce.
lugge86,

Dai un'occhiata al costruttore e al prototipo.
Silviu Burcea,

Silviu Burcea, l'ho fatto. Tuttavia, quando si utilizza il builder, in che modo il builder ottiene i parametri nel prodotto? Da qualche parte deve ESSERE un'interfaccia fat Il costruttore è solo un altro livello, ma in qualche modo i parametri devono trovare la loro strada nella classe di prodotto.
lugge86,

1
Se la tua classe è troppo grande, spostarti attorno agli argomenti del costruttore non la renderà non troppo grande .
Telastyn,

Risposte:


10

Non farei l'opzione 2, perché in questo modo hai complicato per sempre la costruzione del tuo oggetto con l'analisi dell'albero delle proprietà boost. Se sei a tuo agio con una classe che ha bisogno di tanti parametri, dovresti essere a tuo agio con un costruttore che ha bisogno di tanti parametri, questa è la vita!

Se la tua preoccupazione principale è la leggibilità del codice, puoi usare il modello builder, è fondamentalmente lo stopgap c ++ / java per mancanza di argomenti con nome. Finisci con cose che assomigliano a questo:

MyObject o = MyObject::Builder()
               .setParam1(val1)
               .setParam2(val2)
               .setParam3(val3)
             .build();

Quindi ora MyObject avrà un costruttore privato, che viene chiamato in Builder :: build. La cosa bella è che sarà l'unico posto in cui dovrai chiamare un costruttore con 10 parametri. La factory dell'albero delle proprietà boost utilizzerà il builder e successivamente se si desidera costruire un MyObject direttamente o da una fonte diversa, si passerà al builder. E il builder fondamentalmente ti consente di nominare chiaramente ogni parametro mentre lo passi, quindi è più leggibile. Questo ovviamente aggiunge un po 'di boilerplate, quindi dovrai decidere se ne vale la pena rispetto al solo chiamare il costruttore disordinato, o raggruppare alcuni dei parametri esistenti in strutture, ecc. Basta lanciare un'altra opzione sul tavolo.

https://en.wikipedia.org/wiki/Builder_pattern#C.2B.2B_Example


5

NON utilizzare il secondo approccio.

Non è sicuramente la soluzione e porterebbe solo a creare un'istanza delle classi nella logica aziendale, anziché nella parte dell'app in cui si trovano le fabbriche.

O:

  • prova a raggruppare alcuni parametri che sembrano rappresentare cose simili in oggetti
  • dividere la classe corrente in più classi più piccole (avere una classe di servizio con 10 parametri sembra che la classe faccia troppe cose)
  • lascialo così com'è, se la tua classe non è in realtà un servizio, ma un oggetto valore

A meno che l'oggetto che si sta creando non sia effettivamente una classe che è responsabile della conservazione dei dati, si dovrebbe provare a rifattorizzare il codice e dividere la classe grande in classi più piccole.


Bene, la logica di bsiness sta usando una fabbrica che restituisce i prodotti, quindi la logica di business non vede le cose JSON / ptree. Ma vedo il tuo punto, avere codice Parser nel construcotr sembra sbagliato.
lugge86,

La classe rappresenta un widget in una GUI per un sistema incorporato, quindi per me 5+ attributi mi sembrano OK: x_coord, y_coord, backgroundcolor, framesize, framecolor, text ...
lugge86

1
@ lugge86 Anche se stai utilizzando una factory per analizzare JSON, evitando così di chiamare newo costruire oggetti all'interno della tua logica aziendale, non è un ottimo design. Controllare il non guardare le cose parlano da Miško Hevery , che spiega in modo più approfondito il motivo per cui l'approccio fabbrica si ha accennato è un male sia da test e il punto di vista di lettura. Inoltre, la tua classe sembra essere un oggetto dati e per quelli va generalmente bene avere più parametri rispetto alla normale classe di servizio. Non sarei disturbato troppo.
Andy,

Mi sento bene con il mio approccio di fabbrica, ma seguirò il tuo link e ci penserò. Tuttavia, la fabbrica non è in discussione in questo argomento. La domanda è ancora come installare i prodotti ...
lugge86,

"Avere una classe di servizio con 10 parametri sembra che la classe faccia troppe cose" Non nell'apprendimento automatico. Qualsiasi algoritmo ML avrebbe tonnellate di parametri sintonizzabili. Mi chiedo quale sia il modo giusto di affrontarlo quando si codifica ML.
Siyuan Ren,

0

L'opzione 2 è quasi giusta.

Un'opzione migliorata 2

Creare una classe "frontale" che ha il compito di prendere quell'oggetto con struttura JSON e selezionare i bit e chiamare i costruttori di fabbrica. Prende ciò che la fabbrica produce e lo dà al cliente.

  • La fabbrica non ha assolutamente idea che esista una cosa del genere JSON.
  • Il client non deve sapere quali bit specifici sono richiesti dalla fabbrica.

Fondamentalmente il "front-end" sta dicendo ai 2 Bob: "Ho a che fare con i clienti redatte in modo che gli ingegneri non debbano farlo! Ho le competenze delle persone!" Povero Tom. Se solo avesse detto "Ho disaccoppiato il cliente dalla costruzione. Questo risultato è una fabbrica altamente coesa"; avrebbe potuto mantenere il suo lavoro.

Molti argomenti?

Non per il client - comunicazione front-end.

Front-end - fabbrica? Se non 10 parametri, il meglio che puoi fare è rimandare la decompressione, se non la cosa JSON originale, allora un po 'di DTO. È meglio che passare il JSON alla fabbrica? Stessa differenza dico io.

Considererei fortemente il passaggio di singoli parametri. Attenersi all'obiettivo di una fabbrica pulita e coerente. Evita le preoccupazioni della risposta di @DavidPacker.

Mitigare "troppi argomenti"

  • Costruttori di fabbrica o di classe

    • prendendo solo argomenti per la costruzione specifica di classe / oggetto.
    • parametri di default
    • parametri opzionali
    • argomenti denominati
  • Raggruppamento di argomenti front-end

    • Esamina, valuta, convalida, imposta, ecc. I valori degli argomenti guidati dalle firme del costruttore sopra.

"La fabbrica non ha assolutamente idea che esista una cosa del genere JSON" - beh, allora a cosa serve la fabbrica ?? Nasconde i dettagli della creazione del prodotto sia dal prodotto che dal consumatore. Perché dovrebbe ancora aiutare un'altra classe? Sto bene con la fabbrica che parla di JSON. Si può implementare un'altra fabbrica per analizzare XML e implementare "Fabbrica astratta" in futuro ...
lugge86

Mr. Factory: "Quale oggetto vuoi? ... Che cos'è? Dimmi solo quale oggetto di classe costruire." Il file di configurazione JSON è un'origine dati, come dice lo zio Bob "è un dettaglio di implementazione". Potrebbe da un'altra fonte e / o in un'altra forma. In genere vogliamo separare dai dettagli specifici dell'origine dati. Se la fonte o il modulo dovessero cambiare, la fabbrica no. Dato un sorgente + parser e una fabbrica come moduli disaccoppiati rende entrambi riutilizzabili.
radarbob,
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.