Domanda

(Lasciando da parte la domanda se dovresti averli.)

Ho sempre preferito usare solo il sovraccarico della funzione per darti lo stesso nome sia per getter che per setter.

int rate() { return _rate; }      
void rate(int value) { _rate = value; }

// instead of     
int getRate() { return _rate; }      
void setRate(int value) { _rate = value; }

// mainly because it allows me to write the much cleaner     
total( period() * rate() );    
// instead of      
setTotal( getPeriod() * getRate() );

Naturalmente ho ragione, ma mi chiedevo se gli scrittori della biblioteca avessero qualche buona ragione?

È stato utile?

Soluzione

Preferirei le versioni get / set perché è più chiaro cosa sta succedendo. Se ho visto rate () e rate (10), come faccio a sapere che rate (10) non sta semplicemente usando 10 nel calcolo per restituire la tariffa? Non lo so, quindi ora devo iniziare a cercare per capire cosa sta succedendo. Un singolo nome di funzione dovrebbe fare una cosa, non due cose opposte.

Inoltre, come altri hanno sottolineato, alcuni preferiscono omettere il 'get' e lasciare il 'set', cioè,

int Rate( );
void SetRate( int value );

Anche quella convenzione è abbastanza chiara, non avrei problemi a leggerlo.

Altri suggerimenti

Ho sempre preferito omettere 'get' sui miei getter, come fai tu, con rate () invece di getRate () . Ma sovraccaricare il setter non mi sembra un'ottima idea, dal momento che il nome rate non indica che l'oggetto è stato mutato. Prendere in considerazione:

total(period() * rate()); // awesome, very clear

rate(20); // Looks like it computes a rate, using '20'...but for what?  And why ignore the return value?

Che ne dici di int rate (); e void setRate (int value); ? Questo ha il pregio di non avere due funzioni con lo stesso nome che fanno cose diverse, e consente comunque period () * rate () .

Qualche anno fa, avrei accettato completamente. Più recentemente, un dubbio ha iniziato a farsi strada, perché ciò rende ambiguo prendere l'indirizzo di un getter o setter. Con strumenti come tr1 :: bind, questo è davvero fastidioso.

Ad esempio:

struct A
{
   void Foo(int);
   int Foo()const;
};

std::vector<A> v = ....;
std::vector<int> foos;
// Extract Foo
std::transform(
   v.begin(), v.end(), 
   std::back_inserter(foos), 
   //Ambiguous
   // std::tr1::bind(&A::Foo)
   //must write this instead. Yuck!
   std::tr1::bind(static_cast<int(Foo::*)()>(&A::Foo));
);

Lasciando da parte la domanda di dovresti averli affatto ;-)

Vado avanti e menziono che questa dovrebbe essere una domanda wiki della community.

Quando ho iniziato a studiare il C ++, ho cercato delle guide di stile, e per alcuni punti Google è stato utile:

  • Metodi in maiuscolo (è solo più carino).
  • getter chiaramente e in minuscolo ( rate ).
  • setter esplicitamente e minuscoli ( setRate ).

Essere concisi è importante, ma non a costo di essere incompleti o fuorvianti. Per questo motivo, preferisco GetFoo () e SetFoo () a Foo () e Foo (int foo).

Esistono diversi livelli per " Ottenere " e " Impostazione "

  • Uso Get and Set per " fast " operazioni.
  • Se l'esecuzione di qualcosa richiederà più tempo, spesso sarà un Calc, poiché questi nomi implicano che è necessario fare del lavoro per recuperare il risultato.
  • Per operazioni più lunghe, inizi a entrare in prefissi come Carica / Salva, Query / Store, Leggi / Scrivi, Cerca / Trova ecc.

Quindi Get / Set può essere attribuito un significato utile ed essere parte di una strategia di denominazione più ampia e coerente.

Mentre il commento di Ed è vero, preferisco le proprietà reali rispetto al setter / getter antipattern. Quando 1/3 dei metodi nel grafico del tuo dominio sono metodi fittizi generati da eclipse, c'è qualcosa che non va.

Senza proprietà di prima classe, tuttavia, credo che l'antipasto abbia più senso.

Inoltre, semplifica il completamento del codice.

obj.set (spazio del control shift) per setter
obj.get (control shift space) per getter

Personalmente, penso che getter e setter trovati in coppie siano un odore di codice riportato da "visual". lingue e loro "quotazioni". In un "buono" classe, i membri dei dati sono di sola lettura o sola lettura ma non di lettura / scrittura.

Penso che la causa più comune di getter e setter non stia portando il modello a oggetti abbastanza in profondità. Nel tuo esempio, perché viene superato il periodo e la tariffa? Non sono membri della classe? Quindi dovresti solo impostare il periodo e la tariffa e dovresti ottenere solo un totale.

Probabilmente ci sono delle eccezioni, ma odio guardare una classe e trovare " getX / setX, getY / setY, ecc. ecc. " Sembra che non ci sia abbastanza pensiero messo su come la classe DOVREBBE essere usata e piuttosto l'autore ha reso la classe FACILE per ottenere i dati in modo da non dover considerare come usare la classe.

Naturalmente ho ragione.

Un altro problema che nessun altro ha menzionato è il caso del sovraccarico delle funzioni. Prendi questo esempio (inventato e incompleto):

class Employee {
    virtual int salary() { return salary_; }
    virtual void salary(int newSalary) { salary_ = newSalary; }
};

class Contractor : public Employee {
    virtual void salary(int newSalary) {
        validateSalaryCap(newSalary);
        Employee::salary(newSalary);
    }
    using Employee::salary; // Most developers will forget this
};

Senza quella che utilizza , gli utenti di Contractor non possono richiedere lo stipendio a causa del sovraccarico. Di recente ho aggiunto -Woverloaded-virtual al set di avvertimenti di un progetto su cui lavoro, ed ecco, questo è apparso ovunque.

Faccio rispettare la convenzione in cui un metodo dovrebbe essere sempre un verbo e una classe dovrebbe sempre essere un sostantivo (tranne che per i funzioni, per ovvie ragioni). In tal caso, per coerenza è necessario utilizzare un prefisso get / set. Detto questo, sono anche completamente d'accordo con Ed Swangren. Questo per me è come usare quei prefissi come un gioco da ragazzi.

Preferisco evitare le etichette get e set, le informazioni non sono necessarie per il compilatore per fare il suo lavoro per la maggior parte di queste semplici proprietà.

puoi avere problemi:

class Stuff {
  void widget( int something ); // 'special' setter
  const Widget& widget( int somethingelse ) const; // getter
}
Stuff a; 
a.widget(1); // compiler won't know which widget you mean, not enough info

Se il tuo getter è semplicemente rate () , il tuo compilatore si lamenterebbe che si tratta di una ridefinizione dell'altro simbolo rate , a condizione che tu abbia dato al tuo campo un nome significativo come quello. In tal caso devi fare qualcosa di stupido come nominare il tuo membro _rate o un altro approccio simile. Personalmente odio vedere / digitare quei caratteri di sottolineatura, quindi tendo ad andare con l'approccio getRate () .

Questo è ovviamente soggettivo e questa è solo la mia preferenza personale.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top