È un buon esempio: sostituire una funzione lunga con una serie di lambda?

14

Recentemente mi sono imbattuto nella seguente situazione.

class A{
public:
    void calculate(T inputs);
}

In primo luogo, A rappresenta un oggetto nel mondo fisico, che è un argomento valido per non dividere la classe. Ora, calculate() risulta essere una funzione piuttosto lunga e complicata. Percepisco tre possibili strutture per questo:

  • scrivilo come un muro di testo - vantaggi - tutte le informazioni sono in un posto
  • scrivi private funzioni di utilità nella classe e usale nel corpo di calculate - svantaggi - il resto della classe non sa / cura / capisce di questi metodi
  • scrivi calculate nel seguente modo:

    void A::calculate(T inputs){    
        auto lambda1 = () [] {};    
        auto lambda2 = () [] {};    
        auto lambda3 = () [] {};
    
        lambda1(inputs.first_logical_chunk);
        lambda2(inputs.second_logical_chunk);
        lambda3(inputs.third_logical_chunk);
    }
    

Può essere considerata una buona o cattiva pratica? Questo approccio rivela qualche problema? Tutto sommato, dovrei considerare questo un buon approccio quando mi trovo di nuovo di fronte alla stessa situazione?

EDIT:

class A{
    ...
public:
    // Reconfiguration of the algorithm.
    void set_colour(double colour);
    void set_density(double density);
    void set_predelay(unsigned long microseconds);
    void set_reverb_time(double reverb_time, double room_size);
    void set_drywet(double left, double right);
    void set_room_size(double value);;

private:
    // Sub-model objects.
    ...
}

Tutti questi metodi:

  • ottieni un valore
  • calcola altri valori, senza utilizzare lo stato
  • chiama alcuni degli "oggetti sotto-modello" per cambiare il loro stato.

Si scopre che, eccetto set_room_size() , quei metodi passano semplicemente il valore richiesto agli oggetti secondari. set_room_size() , d'altra parte, fa un paio di schermate di formule oscure e poi (2) fa una mezza schermata di richiami di sub-oggetti setter per applicare i vari risultati ottenuti. Pertanto, ho separato la funzione in due lambda e li ho chiamati alla fine della funzione. Se fossi riuscito a dividerlo in parti più logiche, avrei isolato più lambda.

Indipendentemente da ciò, l'obiettivo della domanda corrente è determinare se quel modo di pensare debba persistere, o nel migliore dei casi non aggiungendo valore (leggibilità, manutenibilità, debug-abilità ecc.).

    
posta Vorac 27.01.2015 - 12:07
fonte

2 risposte

13

No, generalmente non è un buon modello

Quello che stai facendo è suddividere una funzione in funzioni più piccole usando lambda. Tuttavia, esiste uno molto strumento migliore per suddividere le funzioni: funzioni.

Lambdas funziona, come hai visto, ma significa molto più di tanto più di una semplice suddivisione di una funzione in bit locali. Lambdas do:

  • Chiusure. Puoi usare le variabili nello scope esterno all'interno del lambda. Questo è molto potente e molto complicato.
  • Nuova assegnazione. Mentre il tuo esempio rende difficile l'assegnazione, devi sempre prestare attenzione all'idea che il codice possa scambiare le funzioni in qualsiasi momento.
  • Funzioni di prima classe. Puoi passare una funzione lambda a un'altra funzione, facendo qualcosa di noto come "programmazione funzionale".

Nel momento in cui introduci lambdas nel mix, il prossimo sviluppatore a guardare il codice deve immediatamente caricare mentalmente tutte quelle regole in preparazione per vedere come funziona il tuo codice. Non sanno che non userete tutte queste funzionalità. Questo è molto costoso, rispetto alle alternative.

È come usare una zappa per fare il giardinaggio. Sai che lo stai solo usando per scavare piccoli buchi per i fiori di quest'anno, ma i vicini si innervosiranno.

Considera che tutto ciò che stai facendo è raggruppare visivamente il tuo codice sorgente. Il compilatore in realtà non si preoccupa di aver curato le cose con lambda. In effetti, mi aspetto che l'ottimizzatore annulli immediatamente tutto ciò che hai appena fatto quando compila. Ti stai semplicemente occupando del prossimo lettore (Grazie per averlo fatto, anche se non siamo d'accordo sulla metodologia! Il codice viene letto molto più spesso di quanto non sia scritto!). Tutto quello che stai facendo è la funzionalità di raggruppamento.

  • Le funzioni posizionate localmente nello stream del codice sorgente farebbero altrettanto bene, senza invocare lambda. Ancora una volta, tutto ciò che conta è che il lettore possa leggerlo.
  • Commenti nella parte superiore della funzione che dice "dividiamo questa funzione in tre parti", seguita da grandi linee lunghe di // ------------------ tra ciascuna parte.
  • È inoltre possibile inserire ciascuna parte del calcolo nel proprio ambito. Questo ha il vantaggio di dimostrare immediatamente oltre ogni dubbio che non vi è alcuna condivisione variabile tra le parti.

EDIT: dal vedere la tua modifica con il codice di esempio, mi sto appoggiando alla notazione dei commenti che è la più pulita, con parentesi che rafforzano i limiti suggeriti dai commenti. Tuttavia, se una delle funzionalità era riutilizzabile in altre funzioni, ti consigliamo di utilizzare invece le funzioni

void A::set_room_size(double value)
{
    {
        // Part 1: {description of part 1}
        ...
    }
    // ------------------------
    {
        // Part 2: {description of part 2}
        ...
    }
    // ------------------------
    {
        // Part 3: {description of part 3}
        ...
    }
}
    
risposta data 27.01.2015 - 16:29
fonte
20

Penso che tu abbia fatto una brutta ipotesi:

Firstly, A represents an object in the physical world, which is a strong argument for not splitting the class up.

Non sono d'accordo con questo. Ad esempio, se avessi una classe che rappresenta una macchina, vorrei sicuramente dividerla, perché voglio sicuramente che una classe più piccola rappresenti le gomme.

Devi dividere questa funzione in funzioni private più piccole. Se sembra davvero separato dall'altra parte della classe, allora potrebbe essere un segno che la classe dovrebbe essere separata. Certo, è difficile dirlo senza un esempio esplicito.

In realtà non vedo il vantaggio dell'uso delle funzioni lambda, perché in realtà non rende il codice più pulito. Sono stati creati per aiutare la programmazione in stile funzionale, ma non è questo.

Ciò che hai scritto assomiglia un po 'agli oggetti funzione nidificati in stile Javascript. Che è di nuovo un segno che appartengono strettamente insieme. Sei sicuro che non dovresti creare una classe separata per loro?

Per riassumere, non penso che questo sia un buon modello.

Aggiorna

Se non vedi alcun modo per incapsulare questa funzionalità in una classe significativa, allora puoi creare funzioni helper con scope di file, che sono non membri della tua classe. Questo è C ++ dopotutto, il design OO non è un must.

    
risposta data 27.01.2015 - 12:42
fonte

Leggi altre domande sui tag