Conventions pour les méthodes d’access (getters et setters) en C ++

Plusieurs questions sur les méthodes d’access en C ++ ont été posées sur SO, mais aucune n’a pu satisfaire ma curiosité sur le problème.

J’essaie d’éviter les accesseurs chaque fois que possible, car, comme Stroustrup et d’autres programmeurs célèbres, je considère une classe avec beaucoup d’entre eux comme un signe de mauvaise OO. En C ++, dans la plupart des cas, je peux append plus de responsabilités à une classe ou utiliser le mot-clé friend pour les éviter. Cependant, dans certains cas, vous avez vraiment besoin d’accéder à des membres spécifiques de la classe.

Il y a plusieurs possibilités:

1. Ne pas utiliser les accesseurs du tout

Nous pouvons simplement rendre publiques les variables membres respectives. Ceci est un no-go en Java, mais semble être OK avec la communauté C ++. Cependant, je suis un peu inquiet à propos des cas où une copie explicite ou une référence en lecture seule (const) à un object devrait être renvoyée, est-ce exagéré?

2. Utiliser les méthodes get / set de style Java

Je ne sais pas si ça vient de Java, mais je veux dire:

int getAmount(); // Returns the amount void setAmount(int amount); // Sets the amount 

3. Utiliser les méthodes d’obtention / définition d’objective de style C

C’est un peu bizarre, mais apparemment de plus en plus commun:

 int amount(); // Returns the amount void amount(int amount); // Sets the amount 

Pour que cela fonctionne, vous devrez trouver un nom différent pour votre variable membre. Certaines personnes ajoutent un trait de soulignement, d’autres préfèrent “m_”. Je n’aime pas non plus.

Quel style utilisez-vous et pourquoi?

De mon sharepoint vue, assis avec 4 millions de lignes de code C ++ (et ce n’est qu’un projet) du sharepoint vue de la maintenance, je dirais:

  • Il est permis de ne pas utiliser les getters / setters si les membres sont immuables (ie const ) ou simples sans dépendances (comme une classe de points avec les membres X et Y).

  • Si le membre est private il est également permis de passer les getters / setters. Je compte également les membres des classes internes pimpl comme private si l’unité .cpp est petite.

  • Si member est public ou protected ( protected est aussi mauvais que public ) et non const , non simple ou a des dépendances, alors utilisez getters / setters.

En tant que préposé à l’entretien, ma principale raison de vouloir avoir des getters / setters est parce que j’ai un endroit pour mettre des points de rupture / consignation / autre chose.

Je préfère le style de l’alternative 2. Comme c’est plus consultable (un élément clé dans l’écriture du code maintenable).

  1. Je n’utilise jamais ce style. Parce que cela peut limiter l’avenir de votre conception de classe et que les geters ou les installateurs explicites sont tout aussi efficaces avec de bons compilateurs.

    Bien entendu, les getters ou setters explicites en ligne créent tout autant une dépendance sous-jacente à l’implémentation de la classe. Il suffit de réduire la dépendance sémantique. Vous devez toujours tout recomstackr si vous les modifiez.

  2. C’est mon style par défaut lorsque j’utilise les méthodes d’accesseur.

  3. Ce style me semble trop “intelligent”. Je l’utilise à de rares occasions, mais seulement dans les cas où je souhaite vraiment que l’accesseur se sente le plus possible comme une variable.

Je pense qu’il y a un cas pour des sacs de variables simples avec éventuellement un constructeur pour s’assurer qu’ils sont tous initialisés à quelque chose de sain. Lorsque je le fais, je le transforme tout simplement en struct et je le laisse tout public.

2) est la meilleure OMI, car elle rend vos intentions plus claires. set_amount(10) est plus significatif que la amount(10) , et en tant qu’effet secondaire agréable permet à un membre nommé amount .

Les variables publiques sont généralement une mauvaise idée, car il n’ya pas d’encapsulation. Supposons que vous deviez mettre à jour un cache ou rafraîchir une fenêtre quand une variable est mise à jour? Dommage si vos variables sont publiques. Si vous avez une méthode définie, vous pouvez l’append ici.

  1. C’est un bon style si nous voulons simplement représenter pure données pure .

  2. Je n’aime pas ça 🙂 parce que get_/set_ est vraiment inutile quand on peut les surcharger en C ++.

  3. STL utilise ce style, par exemple std::streamSsortingng::str et std::ios_base::flags , sauf si cela doit être évité! quand? Lorsque le nom de la méthode est en conflit avec le nom d’un autre type, alors get_/set_ style est utilisé, tel que std::ssortingng::get_allocator cause de std::allocator .

En général, j’estime que ce n’est pas une bonne idée d’avoir trop d’entraîneurs et de régleurs utilisés par trop d’entités dans le système. C’est juste une indication d’une mauvaise conception ou d’une mauvaise encapsulation.

Cela dit, si une conception de ce type doit être modifiée et que le code source est disponible, je préférerais utiliser le modèle de conception de visiteur. La raison est:

une. Il donne à une classe l’occasion de décider qui autoriser l’access à son état privé

b. Il donne à une classe l’occasion de décider de l’access à accorder à chacune des entités intéressées par son état privé

c. Il documente clairement un tel access externe via une interface de classe claire

L’idée de base est la suivante:

a) refaire si possible autre chose,

b) Refactor tel que

  1. Tout access à l’état de classe se fait via une interface individualiste bien connue

  2. Il devrait être possible de configurer certaines choses à faire et à ne pas faire avec chaque interface, par exemple, tout access à une entité externe GOOD devrait être autorisé, tout access à une entité externe BAD devrait être interdit et une entité externe pas défini (par exemple)

  1. Je n’exclus pas l’utilisation des accesseurs. Mai pour certaines structures POD, mais je les considère comme une bonne chose (certains accesseurs peuvent aussi avoir une logique supplémentaire).

  2. La convention de dénomination ne compte pas vraiment, si vous êtes cohérent dans votre code. Si vous utilisez plusieurs bibliothèques tierces, elles peuvent utiliser des conventions de dénomination différentes. Donc c’est une question de goût.

J’ai vu l’idéalisation de classes au lieu de types intégraux pour faire référence à des données significatives.

Quelque chose comme cela ci-dessous ne fait généralement pas bon usage des propriétés C ++:

 struct particle { float mass; float acceleration; float velocity; } p; 

Pourquoi? Parce que le résultat de p.mass * p.acceleration est un flottant et ne force pas comme prévu.

La définition des classes pour désigner un objective (même si c’est une valeur, comme le montant mentionné plus haut) est plus logique et nous permet de faire quelque chose comme:

 struct amount { int value; amount() : value( 0 ) {} amount( int value0 ) : value( value0 ) {} operator int()& { return value; } operator int()const& { return value; } amount& operator = ( int const newvalue ) { value = newvalue; return *this; } }; 

Vous pouvez accéder à la valeur en valeur implicitement par l’opérateur int. En outre:

 struct wage { amount balance; operator amount()& { return balance; } operator amount()const& { return balance; } wage& operator = ( amount const& newbalance ) { balance = newbalance; return *this; } }; 

Utilisation de Getter / Setter:

 void wage_test() { wage worker; (amount&)worker = 100; // if you like this, can remove = operator worker = amount(105); // an alternative if the first one is too weird int value = (amount)worker; // getting amount is more clear } 

C’est une approche différente, cela ne veut pas dire que c’est bon ou mauvais, mais différent.

Permettez-moi de vous parler d’une possibilité supplémentaire, qui semble la plus consciente.

Besoin de lire et modifier

Déclarez simplement cette variable publique:

 class Worker { public: int wage = 5000; } worker.wage = 8000; cout << worker.wage << endl; 

Juste besoin de lire

 class Worker { int _wage = 5000; public: inline int wage() { return _wage; } } worker.wage = 8000; // error !! cout << worker.wage() << endl; 

L'inconvénient de cette approche est que vous devez modifier tout le code d'appel (append des parenthèses, c'est-à-dire) lorsque vous souhaitez modifier le modèle d'access.

Une possibilité supplémentaire pourrait être:

 int& amount(); 

Je ne suis pas sûr de le recommander, mais il a l’avantage que la notation inhabituelle peut empêcher les utilisateurs de modifier les données.

 str.length() = 5; // Ok ssortingng is a very bad example :) 

Parfois, c’est peut-être juste le bon choix à faire:

 image(point) = 255; 

Encore une possibilité, utilisez la notation fonctionnelle pour modifier l’object.

 edit::change_amount(obj, val) 

De cette façon, la fonction d’édition / modification peut être extraite dans un espace de noms distinct avec sa propre documentation. Celui-ci semble venir naturellement avec une programmation générique.