web-dev-qa-db-fra.com

Membres des données publiques vs Getters, Setters

Je travaille actuellement en Qt et donc en C++. J'ai des cours qui ont des membres de données privées et des fonctions de membre public. J'ai des getters et setters publics pour les membres de données disponibles dans la classe.

Maintenant, ma question est la suivante: si nous avons des getters et setters pour les membres de données dans nos classes, quel est l'intérêt de rendre ces membres de données privés? Je suis d'accord pour avoir les membres de données privées dans les classes de base semblent logiques. Mais en plus de cela, avoir des membres privés et leurs getters et setters ne semble pas être logique pour moi.

Ou à la place pouvons-nous rendre toutes les variables comme publiques afin de ne pas avoir besoin de getters et de setters? Est-ce une bonne pratique de les avoir? Je sais que le fait d'avoir des membres privés assure l'abstraction des données mais avoir des getters et des setters permet en fait d'accéder assez facilement à ces variables. Tous les conseils à ce sujet sont les bienvenus.

69
liaK

Ni. Vous devriez avoir des méthodes qui font les choses. Si l'une de ces choses correspond à une variable interne spécifique, c'est bien, mais rien ne devrait le télégraphier aux utilisateurs de votre classe.

Les données privées sont privées, vous pouvez donc remplacer l'implémentation quand vous le souhaitez (et vous pouvez effectuer des reconstructions complètes, mais c'est un problème différent). Une fois que vous avez laissé le Génie hors de la bouteille, il vous sera impossible de le repousser.

EDIT: Suite à un commentaire que j'ai fait à une autre réponse.

Mon point ici est que vous posez la mauvaise question. Il n'y a pas de meilleure pratique en ce qui concerne l'utilisation de getters/setters ou d'avoir des membres du public. Il n'y a que ce qui est le mieux pour votre objet spécifique et comment il modélise une chose spécifique du monde réel (ou une chose imaginaire peut-être dans le cas du jeu).

Les getters/setters sont personnellement le moindre de deux maux. Parce qu'une fois que vous commencez à créer des getters/setters, les gens cessent de concevoir des objets avec un œil critique sur les données qui devraient être visibles et celles qui ne devraient pas. Avec les membres du public, c'est encore pire parce que la tendance devient de tout rendre public.

Examinez plutôt ce que fait l'objet et ce que cela signifie pour quelque chose d'être cet objet. Créez ensuite des méthodes qui fournissent une interface naturelle dans cet objet. Que cette interface naturelle implique d'exposer certaines propriétés internes à l'aide de getters et setters, qu'il en soit ainsi. Mais l'important est que vous y ayez pensé à l'avance et créé les getters/setters pour une raison justifiée par la conception.

71
jmucchiello

Non, ce n'est même pas à distance la même chose.

Il existe différents niveaux de protection/mise en œuvre masquables qui peuvent être atteints par différentes approches d'une interface de classe:


1. Membre de données publiques:

  • fournit un accès en lecture et en écriture (sinon const) au membre de données
  • expose le fait que l'objet de données existe physiquement et est physiquement membre de cette classe (permet de créer des pointeurs de type pointeur sur membre vers ce membre de données)
  • fournit un accès lvalue au membre de données (permet de créer des pointeurs ordinaires vers le membre)


2. Une méthode qui renvoie une référence à un élément de données (éventuellement à un membre de données privées):

  • fournit un accès en lecture et en écriture (sinon const) aux données
  • expose le fait que l'objet de données existe physiquement mais n'expose pas qu'il est physiquement membre de cette classe (ne permet pas de créer des pointeurs de type pointeur sur membre vers les données)
  • fournit un accès lvalue aux données (permet de créer des pointeurs ordinaires)


3. Méthodes getter et/ou setter (accéder éventuellement à un membre de données privées):

  • fournit un accès en lecture et/ou en écriture à la propriété
  • n'expose pas le fait que l'objet de données existe physiquement, et encore moins physiquement présent dans cette classe (ne permet pas de créer des pointeurs de type pointeur vers membre vers ces données, ou tout type de pointeurs pour cela importe)
  • ne fournit pas lvalue l'accès aux données (ne permet pas de créer des pointeurs ordinaires)

L'approche getter/setter n'expose même pas le fait que la propriété est implémentée par un objet physique. C'est à dire. il ne peut y avoir aucun membre de données physiques derrière la paire getter/setter.

Compte tenu de ce qui précède, il est étrange de voir quelqu'un affirmer qu'une paire getter et setter est la même chose qu'un membre de données publiques. En fait, ils n'ont rien en commun.

Bien sûr, il existe des variantes de chaque approche. Une méthode getter, par exemple, pourrait renvoyer une référence const aux données, ce qui la placerait quelque part entre (2) et (3).

34
AnT

Si vous avez des getters et setters pour chacun de vos éléments de données, il est inutile de rendre les données privées. C'est pourquoi avoir des getters et setters pour chacun de vos éléments de données est une mauvaise idée. Considérez la classe std :: string - elle a (probablement) UN getter, la fonction size () et aucun paramètre.

Ou considérons un objet BankAccount - devrions-nous avoir SetBalance() setter pour changer l'équilibre actuel? Non, la plupart des banques ne vous remercieront pas d'avoir mis en œuvre une telle chose. Au lieu de cela, nous voulons quelque chose comme ApplyTransaction( Transaction & tx ).

23
anon

Rendez les données publiques. Dans le cas (plutôt improbable) où vous avez un jour besoin de logique dans le "getter" ou le "setter", vous pouvez changer le type de données en une classe proxy qui surcharge operator= et/ou operator T (où T = quel que soit le type que vous utilisez actuellement) pour implémenter la logique nécessaire.

Edit: l'idée que le contrôle de l'accès aux données constitue une encapsulation est fondamentalement fausse. L'encapsulation consiste à cacher les détails de l'implémentation (en général!) pas contrôler l'accès aux données.

L'encapsulation est complémentaire de l'abstraction: l'abstraction traite du comportement visible de l'extérieur de l'objet, tandis que l'encapsulation traite de masquer les détails de la façon dont ce comportement est mis en œuvre.

L'utilisation d'un getter ou d'un setter réduit en fait le niveau d'abstraction et expose l'implémentation - cela nécessite que le code client sache que cette classe particulière implémente ce qui est logiquement "données" comme une paire de fonctions (le getter et le setter). L'utilisation d'un proxy comme je l'ai suggéré ci-dessus fournit une véritable encapsulation - à l'exception d'un cas de coin obscur, il est complètement cache le fait que ce qui est logiquement un élément de données est réellement implémenté via une paire de fonctions.

Bien sûr, cela doit être mis en contexte: pour certaines classes, les "données" ne sont pas du tout une bonne abstraction. De manière générale, si vous pouvez fournir des opérations de niveau supérieur au lieu de données, c'est préférable. Néanmoins, il existe des classes pour lesquelles l'abstraction la plus utilisable est la lecture et l'écriture de données - et lorsque c'est le cas, les données (abstraites) doivent être rendues visibles comme toutes les autres données. Le fait que l'obtention ou la définition de la valeur puisse impliquer plus qu'une simple copie de bits est un détail d'implémentation qui doit être caché à l'utilisateur.

10
Jerry Coffin

Les Getters et Setters vous permettent d'appliquer une logique à l'entrée/sortie des membres privés, contrôlant ainsi l'accès aux données (Encapsulation à ceux qui connaissent leurs OO termes)).

Les variables publiques laissent les données de votre classe ouvertes au public pour une manipulation incontrôlée et non validée, ce qui est presque toujours indésirable.

Vous devez également penser à ces choses à long terme. Vous n'avez peut-être pas de validation maintenant (c'est pourquoi les variables publiques semblent être une bonne idée), mais il est possible qu'elles soient ajoutées ultérieurement. Les ajouter à l'avance laisse le cadre donc il y a moins de refactorisation dans le raod sans oublier que la validation ne cassera pas le code dépendant de cette façon).

Gardez à l'esprit, cependant, que cela ne signifie pas que chaque variable privée a besoin de son propre getter/setter. Neil soulève un bon point dans son exemple bancaire que parfois les Getters/Setters n'ont tout simplement pas de sens.

10
Justin Niessner

Si vous êtes certain que votre logique est simple et que vous n'avez jamais besoin de faire autre chose lors de la lecture/écriture d'une variable, il est préférable de garder les données publiques. Dans le cas C++, je préfère utiliser struct au lieu de classe pour souligner le fait que les données sont publiques.

Cependant, vous devez très souvent faire d'autres choses lors de l'accès aux membres de données, ou vous voulez vous donner la liberté d'ajouter cette logique plus tard. Dans ce cas, les getters et setters sont une bonne idée. Votre modification sera transparente pour les clients de votre code.

Un exemple simple de fonctionnalités supplémentaires - vous souhaiterez peut-être enregistrer une chaîne de débogage chaque fois que vous accédez à une variable.

5
Igor Krivokon

Mis à part les problèmes d'encapsulation (qui sont une raison suffisante), il est très facile de définir un point d'arrêt chaque fois que la variable est définie/accessible lorsque vous avez des getters/setters.

Les raisons d'utiliser les champs publics plutôt que les getters et les setters incluent:

  1. Il n'y a pas de valeurs illégales.
  2. Le client doit le modifier.
  3. Pour pouvoir écrire des choses comme object.X.Y = Z.
  4. Faire une forte promesse que la valeur n'est qu'une valeur et qu'il n'y a pas d'effets secondaires associés (et ne le sera pas à l'avenir non plus).

Selon le type de logiciel sur lequel vous travaillez, il peut s'agir de cas vraiment exceptionnels (et si vous pensez en avoir rencontré un, vous avez probablement tort) ou ils peuvent se produire tout le temps. Ça dépend vraiment.

(De Dix questions sur la programmation basée sur la valeur .)

4
Ian Goldby

Sur une base strictement pratique, je vous suggère de commencer par rendre tous vos membres de données privés, ET rendre leurs getters et setters privés. Lorsque vous découvrez ce dont le reste du monde (c'est-à-dire votre "(l) communauté d'utilisateurs") a réellement besoin, vous pouvez exposer les getters et/ou setters appropriés, ou écrire des accesseurs publics correctement contrôlés.

De plus (pour le bénéfice de Neil), pendant le temps de débogage, il est parfois utile d'avoir un endroit pratique pour suspendre les impressions de débogage et d'autres actions, lorsqu'un membre de données particulier est lu ou écrit. Avec les getters et setters, c'est facile. Avec les membres de données publiques, c'est une énorme douleur dans la partie postérieure.

3
John R. Strohm

Je crois que l'utilisation de getters et setters simplement pour obtenir et définir la valeur est inutile. Il n'y a aucune différence entre un membre public et un membre privé avec de telles méthodes. Utilisez les getters et setters uniquement lorsque vous devez contrôler les valeurs d'une manière ou d'une autre ou que vous pensez que cela pourrait être utile à l'avenir (l'ajout de logique ne vous fera pas éditer le reste du code).

Pour référence, lisez les directives C++ (C.131)

2
Hitokage

J'ai toujours pensé que les getters et setters sont délibérément verbeux dans la plupart des langages de programmation spécifiquement pour vous faire réfléchir à deux fois avant de les utiliser - pourquoi votre appelant doit-il connaître le fonctionnement interne de votre classe devrait être la question à l'avant de votre esprit .

2
blissapp

Je suggère que vous n'ayez pas de membres de données publiques (sauf pour les structures POD). Je ne recommande pas non plus que vous ayez des getters et setters pour tous vos membres de données. Définissez plutôt une interface publique propre pour votre classe. Cela peut inclure des méthodes qui obtiennent et/ou définissent des valeurs de propriété, et ces propriétés peuvent être implémentées en tant que variables membres. Mais ne faites pas de getters et de setters pour tous vos membres.

L'idée est que vous sépariez votre interface de votre implémentation, ce qui vous permet de modifier l'implémentation sans que les utilisateurs de la classe n'aient à changer leur code. Si vous exposez tout à travers des getters et des setters, vous n'avez rien amélioré par rapport à l'utilisation de données publiques.

1
Fred Larson

L'utilisation de getters et setters vous permettra de modifier la façon dont vous donnez les valeurs à l'utilisateur.

Considérer ce qui suit:

double premium;
double tax;

Vous écrivez ensuite du code partout en utilisant cette valeur premium pour obtenir la prime:

double myPremium = class.premium;

Vos spécifications viennent d'être modifiées et la prime du point de vue de l'utilisateur doit être premium + tax

Vous devrez modifier partout où cette valeur premium est utilisée dans votre code et y ajouter tax.

Si à la place vous l'avez implémenté comme tel:

double premium;
double tax;

double GetPremium(){return premium;};

Tout votre code utiliserait GetPremium() et votre changement de tax serait une ligne:

double premium;
double tax;

double GetPremium(){return premium + tax;};
1
Ben Burnett

La valeur de retour affecte également l'utilisation de getters et setters. C'est une différence pour obtenir la valeur d'une variable ou pour accéder à la variable membre de données privées. La sous-valeur conserve l'intégrité, par référence ou par pointeur moins.

0
DaClown

Les Getters et Setters existent principalement pour que nous puissions contrôler comment les membres sont récupérés et comment ils sont définis. Les getters et setters n'existent pas uniquement comme moyen d'accéder à un membre spécifique, mais pour s'assurer qu'avant d'essayer de définir un membre, qu'il remplit peut-être certaines conditions, ou si nous le récupérons, nous pourrions contrôler que nous retournons une copie de ce membre dans le cas d'un type non primitif. Dans l'ensemble, vous devez essayer d'utiliser g/s'ers lorsque vous souhaitez définir la manière dont un membre de données doit interagir, sans cela, le membre serait utilisé de manière ad hoc.

0
user1043000