les accesseurs et mutateurs style
(laissant de côté la question de si vous les avez du tout.)
J'ai toujours préféré utiliser la surcharge de fonction pour vous donner le même nom pour getter et setters.
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() );
Naturellement, je suis correct, mais je me demandais si les auteurs de la bibliothèque avaient une bonne raison ?
13 réponses
Je préférerais les versions get/set car il est plus clair sur ce qui se passe. Si j'ai vu rate () et rate(10), Comment puis-je savoir que rate(10) n'utilise pas simplement 10 dans le calcul pour retourner le taux? Je ne le fais pas, alors maintenant je dois commencer à chercher Pour comprendre ce qui se passe. Un seul nom de fonction devrait faire une chose, pas deux choses opposées.
Aussi, comme d'autres l'ont souligné, certains préfèrent l'omettre le " get "et laisser le "set", c'est à dire,
int Rate( );
void SetRate( int value );
Cette convention est assez clair aussi, je n'aurais aucun problème à lire cela.
Que diriez-vous de int rate();
et void setRate(int value);
? Cela a la vertu de ne pas avoir deux fonctions du même nom faisant des choses différentes, et permet toujours period() * rate()
.
J'ai toujours préféré omettre le " get " sur mon getters, comme vous le faites, avec rate()
au lieu de getRate()
. Mais la surcharge pour le setter ne me semble pas une très bonne idée, puisque le nom rate
ne signifie pas que l'objet est en mutation. Considérez:
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?
Il y a quelques années, j'aurais complètement accepté. Plus récemment, un doute a commencé à faire son chemin, car cela rend l'adresse d'un getter ou d'un setter ambiguë. Avec des outils comme tr1::bind, c'est vraiment ennuyeux.
Par exemple:
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));
);
Laissant de côté la question de si vous les avez du tout; -)
Je vais aller de l'avant et mentionner que cela devrait être une question wiki communautaire.
Quand j'ai commencé à apprendre le C++, j'ai cherché des guides de style, et Google était bon pour certains points:
- Méthodes en majuscules (c'est juste plus joli).
- getters clairement et lowecase (
rate
). - setters explicitement et en minuscules (
setRate
).
Être concis est important, mais pas au prix d'être incomplet ou trompeur. Pour cette raison, je préfère GetFoo () et SetFoo () à Foo () et Foo (int foo).
Il y a plusieurs niveaux à "Getting"et" Setting "
- j'utilise Get et Set pour les opérations "rapides".
- si quelque chose prend plus de temps à exécuter, alors ce sera souvent un Calc, car ces noms impliquent qu'un travail doit être fait pour récupérer le résultat.
- Pour plus d'opérations, vous commencez à rentrer dans les préfixes comme Charger/Enregistrer, Requête/Magasin, Lecture/Écriture, de Recherche, etc.
Donc Get / Set peut être attribué une signification utile, et faire partie d'un plus grand, stratégie de nommage cohérente.
Bien que le commentaire D'Ed soit vrai, je préfère les propriétés réelles à l'antipattern setter / getter. Lorsque 1 / 3rd des méthodes dans votre graphe de domaine sont des méthodes factices qui ont été générées par eclipse, il y a quelque chose qui ne va pas.
Sans propriétés de première classe, cependant, je crois que l'antipattern a le plus de sens.
En outre, cela facilite l'achèvement du code.
obj.set (control shift space)
pour les settersobj.get (control shift space)
pour les getters
Personnellement, je pense que les getters et les setters trouvés par paires sont une odeur de code transmise par les langages " visuels "et leurs"propriétés". Dans une classe "Bonne", les membres de données sont writeonly ou readonly mais pas en lecture / écriture.
Je pense que la cause la plus fréquente des getters et des setters n'est pas assez profonde pour porter le modèle d'objet. Dans votre exemple, pourquoi total est-il passé la période et le taux? Ne sont-ils pas membres de la classe? Donc, vous ne devriez définir la période et le taux et vous ça ne devrait être qu'un total.
Il y a probablement des exceptions mais je déteste juste regarder une classe et trouver " GETX / setX, getY / setY, etc. etc."Il semble juste qu'il n'y avait pas assez de réflexion sur la façon dont la classe devrait être utilisée et plutôt l'auteur a rendu la classe facile à obtenir les données afin qu'il n'ait pas à considérer comment la classe devrait être utilisée.
Naturellement, je suis correct.
Un autre problème que personne d'autre n'a mentionné est le cas de la surcharge de fonction. Prenez cet exemple (artificiel et incomplet):
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
};
Sans cette clause using
, les utilisateurs de Contractor
ne peuvent pas interroger le salaire en raison de la surcharge. J'ai récemment ajouté -Woverloaded-virtual
à l'ensemble d'avertissement d'un projet sur lequel je travaille, et voilà, cela s'est montré partout.
J'applique la convention dans laquelle une méthode doit toujours être un verbe et une classe doit toujours être un nom (sauf pour les foncteurs, pour des raisons évidentes). Dans ce cas, un préfixe get/set doit être utilisé pour la cohérence. Cela dit, je suis également entièrement d'accord avec Ed Swangren. Cette somme m'que l'utilisation de ces préfixes une évidence.
Je préfère éviter les étiquettes get et set, l'information n'est pas nécessaire pour que le compilateur fasse son travail pour la plupart de ces propriétés simples.
Vous pouvez avoir des problèmes:
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
Si votre getter est simplement rate()
, votre compilateur se plaindrait que c'est une redéfinition de votre autre symbole rate
, à condition que vous donniez à votre champ un bon nom significatif comme ça. Dans ce cas, vous devez faire quelque chose de stupide comme nommer votre membre _rate
ou une autre approche similaire. Personnellement, je déteste voir / taper ces traits de soulignement, donc j'ai tendance à aller avec l'approche getRate()
.
C'est évidemment subjectif et c'est juste ma préférence personnelle.