2011-02-04 11 views
13

Recentemente ho usato una libreria che consente il seguente tipo di sintassi:Tornando * questo funzioni membro

MyClass myObject; 
myObject 
    .setMember1("string value") 
    .setMember2(4.0f) 
    .setMember3(-1); 

Ovviamente ciò è ottenuto avente le incastonatori restituiscono un MyClass & tipo; qualcosa come return * this. Mi piace il modo in cui appare questo codice, ma non lo vedo molto. Quando ciò accade, di solito sono sospettoso sul perché.

Quindi, questa è una cattiva pratica? Quali sono alcune delle implicazioni di farlo in questo modo?

+7

Si chiama "Fluent Interface". –

+0

Ciao Joe, +1 per il tuo nome e una buona domanda. +1 per Robert Harvey per il nome, ecco un link http://en.wikipedia.org/wiki/Fluent_interface – Joe

+0

È come chiedere perché non tutti hanno un'istruzione superiore ... –

risposta

5

Questo è talvolta denominato Named Parameter Idiom o metodo di concatenamento. Questa non è una cattiva pratica, può aiutare la leggibilità. Considerate questo esempio sollevato dal C++ FAQ

File f = OpenFile("foo.txt") 
      .readonly() 
      .createIfNotExist() 
      .appendWhenWriting() 
      .blockSize(1024) 
      .unbuffered() 
      .exclusiveAccess(); 

L'alternativa sarebbe quella di utilizzare argomenti posizionali al metodo OpenFile, richiedendo al programmatore di ricordare la posizione di ciascun argomento.

+3

Oppure usa il Boost Parameter, che ha un aspetto uniforme. –

+0

-1 Il codice dell'OP non è l'idioma del parametro denominato. Quello che mostri (dalle FAQ) è un esempio di idioma dei parametri con nome. La differenza è ciò che può essere impostato: gli attributi dell'oggetto finale che stai costruendo (molto male, davvero non lo vuoi), o gli attributi di argument pack (buono). –

+5

Forse dovrei espandere il commento negativo. L'idioma dei parametri con nome è buono: produce ** costruzione monofase **, dove dopo che un costruttore è stato eseguito correttamente, l'oggetto è completamente inizializzato con l'invarianza della classe di lavoro. Il codice dell'OP utilizza invece ** costruzione a due fasi ** o ** costruzione multifase **, che è errato perché dopo il completamento del costruttore C++, non si dispone ancora di un oggetto pronto all'uso. Bjarne ha scritto a riguardo in appendice a TCPPPL, disponibile come PDF dal suo sito. Inoltre, il codice OP espone gli attributi, volenti o nolenti. Quindi, NPI = buono, codice OP = cattivo. –

1

Non c'è nessun problema con questo stile. L'unico svantaggio è che non è possibile utilizzare il valore restituito per scopi più tipici, come restituire il risultato della funzione.

+0

vedi la risposta di @Jerry Coffin e la mia risposta.La costruzione a due fasi è una mostruosità, esponendo anche gli attributi volenti o nolenti, combinati (come qui) sono, beh, qualunque cosa, mostruosità . Si dovrebbe invece usare per es. il nome dei parametri Idiom. –

+0

@ Alf, grazie per aver sottolineato la differenza: ho già visto l'idioma dei Parametri nominati ma non l'ho mai usato, quindi le sue sottigliezze mi sono state perse. Non condannerò la costruzione in due fasi o gli attributi esposti così severamente come te, ma sono d'accordo sul fatto che spesso non sono la soluzione migliore. –

1

Si chiama fluent api. Non è una cattiva pratica, solo uno stile di programmazione diverso.

Il più grande svantaggio (IMO) è che dal momento che si restituisce un riferimento a te stesso, non è possibile restituire altro e può essere difficile eseguire il debug delle dichiarazioni fluenti poiché sono viste dal compilatore come una "linea gigante" "di codice.

1

Non sono sicuro che sia considerata una cattiva pratica o meno, ma una delle implicazioni è che non è più possibile restituire codici di errore, quindi si è obbligati a utilizzare eccezioni o brutti oggetti di errore passati per riferimento. Le eccezioni a volte sono la soluzione giusta, ma spesso non lo sono, dal momento che possono essere costose da lanciare e sono disabilitate su alcune piattaforme.

Penso davvero che sia comunque una cosa stilistica, e grazie alla stretta relazione di C++ con C sia in sintassi che in cultura, molti programmatori C++ come i codici di errore preferiscono quindi restituirli invece di restituire un riferimento. Ma ho visto il ritorno di * questo spesso pure e non penso che sia una cattiva pratica.

1

In teoria, si potrebbe finire con un riferimento penzoloni se fai qualcosa di terribile come:

MyClass *myObject = new MyClass; 
MyClass & dangling = myObject->setMember1("string"); 
delete myObject; 
dangling.setMember2(yrParam); 

modo da essere consapevoli di questo.

2

È una pratica comune. Il sovraccarico di operator= implica a farlo per le chiamate a catena:

class Foo { 

public: 
    Foo& operator=(const Foo& f) { 
     if (this != &f) { // check for self-assignment 
     // do some stuff... 
     } 
     return *this; 
    } 

}; 

Questo codice consente di fare cose come:

Foo a, b, c; 
a = b = c; 

Si prega di notare che il controllo per l'auto-assegnazione è obbligatoria perché si deve spesso deallocare oggetti nell'oggetto corrente, quindi consentire a = a interromperà il codice.

Seguendo il commento di @Fred Nurk, vorrei aggiungere che si dovrebbe dare un'occhiata all'idioma Copy-and-Swap per evitare la duplicazione del codice e rilasciare un codice senza eccezioni.
Date un'occhiata al link qui di seguito per maggiori informazioni:

What is the copy-and-swap idiom?
http://gotw.ca/gotw/059.htm

+4

L'idioma di copia-swap gestisce (il raro) caso di autoassegnazione in modo più pulito. –

+0

+1, ci sono anche l'operatore 'canonico' 'e' operator >> 'per lo streaming. Il concatenamento non è nuovo, sicuramente e ben approvato dalla Libreria standard. –

+0

+1 per il tuo commento @Fred Nurk. Ma possiamo applicare l'idioma di copia-swap ogni volta? Ad esempio, quando una classe ha bisogno di riallocare la memoria più grande in alcuni casi? – jopasserat

1

Non male pratica, in realtà si vede spesso con flussi di uscita al fine di concatenare più stringhe e valori.

Lo svantaggio che vedo è che impedisce di restituire qualsiasi altra cosa, anche se è un metodo impostato, non dovrebbe avere importanza.

6

Alcune persone chiamano questa programmazione fluente (o un'interfaccia fluente). Altri lo chiamano un casino.

I tendere un po 'verso il secondo campo tendenzialmente. In particolare, la mia esperienza è stata che in molti casi, le persone che scrivono il codice in questo modo dipendono da sulla "fluente interfaccia" per un bel po 'di inizializzazione di un oggetto. In altre parole, nonostante il travestimento, è ancora in fase di inizializzazione. Allo stesso modo, anche se è probabilmente evitabile in molti casi, spesso sembra comportare un bel po 'di ciò che dovrebbe essere interamente privato alla classe che viene resa pubblicamente modificabile tramite manipolatori.

Personalmente I preferisco che gli oggetti siano immutabili dopo la creazione. Chiaramente non è sempre possibile, e in alcuni casi non puoi nemmeno avvicinarti molto. Ciononostante, quanto più gli interni di un oggetto si aprono alla manipolazione esterna, tanto meno si diventa certi che l'oggetto mantenga uno stato coerente (e, in genere, più lavoro si deve fare per mantenere uno stato coerente).

+0

Correggimi se sbaglio, ma sembra che tu sia contro i setter in generale (fluente o meno) :-) – Joe

+1

@Joe: sì, generalmente io sono. –

+0

È facile usare l'interfaccia fluente e gli oggetti immutabili. Il costruttore per l'immutabile accetta un parametro oggetto. Quell'oggetto è configurato usando un'interfaccia fluente. –

2

Il tuo esempio è non i parametri denominati idioma.

Con l'idioma dei parametri denominati, i settabili concatenabili impostano gli attributi di un pacchetto argomento (parametro).

Il tuo codice ha invece un gruppo di setter per la modifica dell'oggetto finale che stai costruendo, chiamato costruzione a due fasi.

In generale, la costruzione a due fasi è solo Bad ™ e la costruzione a due fasi implementata esponendo gli attributi al codice client, come nell'esempio, è Molto male ™.

Ad esempio, in genere non si desidera poter modificare gli attributi di un oggetto file, una volta che l'oggetto è stato creato (e possibilmente aperto).

Acclamazioni & hth.,

Problemi correlati