Si une variable a un getter et un setter, devrait-elle être publique?

23

J'ai une classe avec une variable qui est privée et la classe a un getter et un setter pour cette variable. Pourquoi ne pas rendre cette variable publique?

Le seul cas, je pense que vous devez utiliser des getters et setters, c'est si vous avez besoin de faire une opération en plus de l'ensemble ou du get. Exemple:

void my_class::set_variable(int x){
   /* Some operation like updating a log */
   this->variable = x;
}
Oni
la source
10
Getters et setters sont là pour le blindage. Un jour, vous voudrez peut-être faire this->variable = x + 5, ou appeler une UpdateStatisticsfonction dans setter, et dans ces cas, classinstancea->variable = 5cela causera des problèmes.
Coder
1
Un autre exemple est la suivante : set_inch, set_centimeter, get_inch, get_centimeter, avec quelques actions fantasmagoriques.
rwong
Oui, les getters et les setters vous aident à contrôler vos variables d'instance de la manière dont vous souhaitez qu'elles soient contrôlées.
developerdoug
7
@Coder: vous pouvez toujours ajouter votre getter / setter lorsque ce besoin se fait sentir? Ou est-ce quelque chose que vous ne pouvez pas facilement faire en C ++ (mon expérience est principalement Delphi)?
Marjan Venema
Cette question: programmers.stackexchange.com/questions/21802/… peut être intéressante.
Winston Ewert

Réponses:

21

Avez-vous déjà entendu parler d'une propriété?

Une propriété est un champ qui a des accesseurs "intégrés" (getters et setters). Java, par exemple, n'a pas de propriétés, mais il est recommandé d'écrire les getters et setters dans un champ privé. C # a des propriétés.

Alors, pourquoi avons-nous besoin de getters et de setters? Fondamentalement, nous en avons besoin pour protéger / protéger le champ. Par exemple, vous n'accédez pas au champ en référence mémoire, vous accédez à une méthode qui changera ensuite le champ (référence). Cette méthode est capable d'effectuer certaines opérations qu'un utilisateur n'est pas disposé à connaître ( comportement d'encapsulation ), comme dans votre exemple. Imaginez, par exemple, qu'une douzaine de classes utilisent votre champ public et que vous devez changer la façon dont il est utilisé ... Vous devez regarder chacune de ces classes afin de changer la façon dont elles utilisent le champ ... Non donc "OOlysh".

Mais, par exemple, si vous avez un champ booléen appelé mort. Vous devriez réfléchir à deux fois avant de déclarer un setDead et isDead. Vous devez écrire des accesseurs lisibles par l'homme , par exemple, kill () au lieu de setDead.

Cependant, il existe de nombreux frameworks qui supposent que vous suivez la convention de nommage JavaBean (en parlant de Java ici), par conséquent, dans ces cas, vous devez déclarer tous les getters et setters après la convection de nommage.

wleao
la source
2
Maintenant "lisible par l'homme" est enfin un argument que je peux "grogner". Tous les autres arguments que j'entends habituellement sont une sorte d'épreuvage futur qui sont tout aussi facilement abordés lorsque le besoin s'en fait sentir
Marjan Venema
13
Votre mépris pour "l'épreuvage futur" est courant, mais peu judicieux. Oui, vous pouvez apporter de telles modifications lorsque le besoin s'en fait sentir, si vous êtes le seul client de ce code . Si vous ne publiez jamais votre solution pour des clients externes, vous pouvez simplement contracter une dette technique et personne n'a jamais besoin de le savoir. Mais les projets internes ont l'habitude de devenir publics quand vous vous y attendez le moins, et alors malheur à vous.
Kilian Foth
2
Vous soulevez en fait un point fantastique. tuer n'est pas un set / get, c'est une action. Il masque complètement l'implémentation - c'est ainsi que vous codez OO. Les propriétés, en revanche, sont aussi stupides que les setters / getters - stupides même parce que la syntaxe facile encourage les gens à les ajouter aux variables quand ils ne sont même pas nécessaires (OO-Furbar attend de se produire). Qui penserait à utiliser kill () alors qu'il pourrait simplement ajouter une balise "property" à son drapeau mort?
Bill K
1
La question est étiquetée comme une question C ++. C ++ ne prend pas en charge les propriétés, alors pourquoi publieriez-vous cela?
Thomas Eding
1
@ThomasEding: C ++ est quelque peu unique en ce qu'il a des opérateurs d'affectation remplaçables. Bien que C ++ n'utilise pas le terme "propriété" de la même manière que des langages comme C # ou VB.NET, le concept est toujours là. Il est possible en C ++ d'utiliser la surcharge d'opérateur afin d' foo.bar = biz.baz+5appeler une fonction bizà évaluer baz, puis d'en ajouter cinq et d'appeler une fonction foopour définir barla valeur résultante. Ces surcharges ne sont pas appelées «propriétés», mais elles peuvent servir le même objectif.
supercat
25

Ce n'est pas l'opinion la plus populaire mais je ne vois pas beaucoup de différence.

Les setters et les getters sont une assez mauvaise idée. J'y ai pensé et honnêtement, je ne peux pas trouver de différence entre un setter / getter une propriété et une variable publique dans la pratique.

Dans THEORY, un setter et un getter ou une propriété ajoutent un endroit pour effectuer des actions supplémentaires lorsqu'une variable est définie / obtenue et en théorie, ils isolent votre code des modifications.

En réalité, je vois rarement des setters et des getters utilisés pour ajouter une action, et lorsque vous voulez ajouter une action, vous voulez l'ajouter à TOUS les setters ou getters d'une classe (comme la journalisation), ce qui devrait vous faire penser qu'il devrait y avoir être une meilleure solution.

En ce qui concerne l'isolement des décisions de conception, si vous changez un int en un long, vous devez toujours changer vos setters et vérifier au moins chaque ligne qui y accède à la main - pas beaucoup d'isolement là-bas.

Les classes mutables doivent être évitées par défaut de toute façon, donc l'ajout d'un setter devrait être un dernier recours. Ceci est atténué avec le modèle de générateur où une valeur peut être définie jusqu'à ce que l'objet soit dans l'état souhaité, puis la classe peut devenir immuable et vos setters lèveront des exceptions.

En ce qui concerne les getters - je ne peux toujours pas trouver une grande différence entre un getter et une variable finale publique. Le problème ici est que c'est mauvais OO dans les deux cas. Vous ne devriez pas demander une valeur à un objet et opérer dessus - vous devriez demander à un objet d'effectuer une opération pour vous.

Soit dit en passant, je ne préconise en aucun cas les variables publiques - je dis que les setters et les getters (et même les propriétés) sont bien trop proches des variables publiques.

Le gros problème est simplement que les gens qui ne sont pas des programmeurs OO sont trop tentés d'utiliser des setters et des getters pour transformer des objets en propriétés (structures) qui sont transmises et exploitées, à peu près le contraire du fonctionnement du code orienté objet.

Bill K
la source
Une bonne chose en dehors des autres réponses ici, à propos de getter setter est: je peux ajouter des validations / conversions à l'intérieur avant que la variable privée réelle ne suppose sa valeur.
WinW
1
@Kiran true, mais si vous le définissez dans le constructeur, vous pouvez avoir des validations ET vous avez une classe immuable. Pour les setters, je ne dis pas qu'une variable accessible en écriture est bonne, je dis que même les setters sont mauvais. Pour les getters, je pourrais considérer une variable finale publique comme une alternative viable.
Bill K
Dans certains langages comme C #, les propriétés ne sont pas compatibles binaires avec les variables publiques, donc si vous intégrez une variable publique à votre API, mais que vous souhaitez ensuite ajouter une validation, vous allez interrompre le programme de votre client lorsque vous le convertissez en une propriété. Mieux vaut en faire un bien public dès le départ.
Robert Harvey
@RobertHarvey Cela signifie toujours que vous exposez des propriétés. Mon argument était que l'exposition des propriétés devrait être évitée, et si nécessaire, vous devriez essayer de les restreindre aux getters et de rendre votre classe immuable. Si votre classe est immuable, pourquoi auriez-vous besoin de code dans un getter - et donc il s'ensuit que vous pourriez tout aussi bien ne pas avoir de getter. Si vous êtes incapable d'écrire une classe sans propriétés mutables exposées, alors oui, un setter est toujours mieux qu'une variable publique inscriptible (et se faire tirer dans le pied est toujours mieux que d'être poignardé dans l'intestin).
Bill K
1
Un état mutable est acceptable, mais encore une fois, vous devriez demander à votre objet de faire quelque chose pour vous, pas de faire quelque chose pour votre objet - par conséquent, lorsque vous modifiez votre état, un setter n'est pas un excellent moyen de le faire, essayez d'écrire une méthode commerciale avec logique à la place. Par exemple, "déplacer (Up5) plutôt que setZLocation (getZLocation () + 5)".
Bill K
8

L'utilisateur de getters et setters entre dans le principe de l' encapsulation . Cela vous permettra de changer la façon dont les choses fonctionnent à l'intérieur de la classe et de garder tout fonctionne.

Par exemple, si 3 autres objets appellent foo.barpour obtenir la valeur de la barre et que vous décidez de changer le nom de la barre, vous avez un problème. Si les objets appelés, foo.barvous devrez changer toutes les classes qui ont ceci. Si un setter / getter est utilisé, vous n'avez rien à changer. Une autre possibilité consiste à changer le type de la variable, auquel cas il suffit d'ajouter du code de transformation au getter / setter et tout va bien.


la source
1
+1 - la variable membre est l'implémentation, le getter et le setter sont l'interface. Seule l'interface doit être publique. De plus, les noms de getter et de setter doivent avoir un sens en termes d'abstraction, qu'il y ait une simple variable membre sous-jacente ou une autre implémentation. Il existe des exceptions - des cas où un sous-système construit à partir de classes étroitement couplées est le moyen le plus simple - mais qui repousse de toute façon la limite de masquage des données vers la classe qui représente l'ensemble du sous-système.
Steve314
Ne pourrais-je pas simplement introduire le getter et / ou le setter quand j'ai besoin de changer le travail à l'intérieur de la classe? C'est quelque chose qui se fait facilement en Delphi, mais peut-être pas dans d'autres langues?
Marjan Venema
@ marjan Bien sûr, vous pourrez introduire le getter / setter à tout moment plus tard. Le problème est alors que vous devez revenir en arrière et modifier TOUT le code qui utilisait le champ public au lieu de getter / setter. Je ne sais pas si je suis celui qui est confus ou si vous l'êtes. 0.o
Pete
3
Personnellement, je pense que c'est un peu un faux argument. Si vous changez la signification d'une variable mais que la variable a un getter et un setter, peu importe comment elle s'appelle. Ce n'est pas une partie visible de l'interface. Vous êtes beaucoup plus susceptible de vouloir changer le nom des getters et setters eux-mêmes pour indiquer aux clients le changement ou la clarification de la signification de la variable encapsulée. Dans ce dernier cas, vous n'êtes pas mieux placé qu'avec une variable publique. Ceci est tout à fait à part l'argument selon lequel une variable avec des getters et des setters est plus exposée qu'encapsulée.
CB Bailey
1
Une opération de refactorisation est pratiquement gratuite dans les deux cas, donc je n'achète pas vraiment celui-ci. De plus, il est très mauvais d'utiliser un setter ou un getter à moins que vous n'en ayez absolument besoin - vous obtiendrez finalement des gens qui ne comprennent pas le concept en ajoutant automatiquement des setters et des getters pour les membres au lieu du moment où ils sont nécessaires, ce qui ne fait que couler des graines du mal dans votre base de code.
Bill K
5

L'utilisation de getters et setters vous permet également de contrôler le contenu stocké dans une variable particulière. Si le contenu doit être d'un certain type ou d'une certaine valeur, une partie de votre code de définition peut être de garantir que la nouvelle valeur répond à ces exigences. Si la variable est publique, vous ne pouvez pas vous assurer que ces exigences sont remplies.

Cette approche rend également votre code plus adaptable et gérable. Il est beaucoup plus facile d'apporter des modifications à l'architecture d'une classe si vous avez des fonctions en place qui gardent cette architecture cachée de toutes les autres classes ou fonctions qui utilisent cette classe. Le changement déjà mentionné d'un nom de variable n'est que l'un des nombreux changements qui sont beaucoup plus faciles à effectuer si vous avez des fonctions comme les getters et les setters. L'idée générale est de garder le plus de confidentialité possible, en particulier vos variables de classe.

Kenneth
la source
Merci beaucoup! Je pensais au même cas que vous mentionnez. Si je veux qu'une variable soit contenue dans [0,1] je devrais vérifier la valeur entrante dans son setter :)
Oni
Lorsque vous êtes le seul développeur à travailler sur un projet, il est facile de penser que des choses comme celle-ci sont inutiles, mais lorsque vous vous retrouverez à travailler avec une équipe, vous constaterez que l'habitude de techniques comme celle-ci vous sera très utile et vous aidera éviter beaucoup de bugs sur la route.
Kenneth
Si vous utilisez C #, utilisez des propriétés, vous pouvez utiliser une propriété pour définir une instance privée si nécessaire en fonction d'une logique dans la partie setter de la propriété.
developerdoug
2

Vous dites "Le seul cas où je pense que vous devez utiliser des getters et setters est si vous avez besoin de faire une opération en plus du set ou du get".

Vous devriez utiliser des getters et setters si à un moment donné dans le futur vous pourriez avoir besoin de faire une opération en plus de l'ensemble et d'obtenir et que vous ne voulez pas changer des milliers de lignes de code source lorsque cela se produit.

Vous devez utiliser des getters et setters si vous ne voulez pas que quelqu'un prenne l'adresse de la variable et la transmette, avec des conséquences désastreuses si cette variable peut ensuite être modifiée sans que le code source le mentionne, ou même après que l'objet a cessé d'exister. .

gnasher729
la source
Votre dernier paragraphe fait un excellent point qui va dans les deux sens: exiger l'utilisation de getters et setters empêchera tout autre code de prendre l'adresse de quelque chose. Dans les cas où il y aurait des avantages significatifs et peu d'inconvénients à ce que du code extérieur prenne l'adresse des choses, une telle restriction pourrait être une mauvaise chose. s'il y aurait peu d'avantages et d'inconvénients graves à autoriser un tel comportement, le restreindre pourrait être une bonne chose.
supercat
1

Tout d'abord, soyons clairs sur le paradigme.

  • Structures de données -> une disposition de la mémoire qui peut être parcourue et manipulée par des fonctions bien informées.
  • Objets -> un module autonome qui cache son implémentation et fournit une interface qui peut être communiquée via.

Où un getter / setter est-il utile?

Les getters / setters sont-ils utiles dans les structures de données? Non.

Une structure de données est une spécification de disposition de mémoire qui est commune et manipulée par une famille de fonctions.

Généralement, toute ancienne fonction nouvelle peut venir et manipuler une structure de données, si elle le fait de manière à ce que les autres fonctions puissent toujours la comprendre, alors la fonction rejoint la famille. Sinon, c'est une fonction escroc et une source de bugs.

Ne vous méprenez pas, il pourrait y avoir plusieurs familles de fonctions qui se disputent cette structure de données avec des snitches, des turn-coats et des agents doubles partout. C'est bien quand ils ont chacun leur propre structure de données pour jouer, mais quand ils la partagent ... imaginez simplement plusieurs familles criminelles en désaccord sur la politique, cela peut devenir un gâchis très rapidement.

Étant donné le désordre que les familles de fonctions étendues peuvent atteindre, existe-t-il un moyen de coder la structure des données afin que les fonctions non autorisées ne gâchent pas tout? Oui, ils sont appelés objets.

Les getters / setters sont-ils utiles dans les objets? Non.

L'intérêt de regrouper une structure de données dans un objet était de garantir qu'aucune fonction non autorisée ne pouvait exister. Si la fonction voulait rejoindre la famille, elle devait d'abord être soigneusement examinée, puis faire partie de l'objet.

Le but / but d'un getter et d'un setter est de permettre à des fonctions en dehors de l'objet de modifier directement la disposition de la mémoire de l'objet. Cela ressemble à une porte ouverte pour permettre aux voyous ...

The Edge Case

Il y a deux situations où un getter / setter public avait du sens.

  • Une partie de la structure de données à l'intérieur de l'objet est gérée par l'objet, mais n'est pas contrôlée par l'objet.
  • Une interface décrivant une abstraction de haut niveau d'une structure de données où certains éléments ne devraient pas être en contrôle de l'objet d'implémentation.

Les conteneurs et les interfaces de conteneurs sont des exemples parfaits de ces deux situations. Le conteneur gère les structures de données (liste liée, carte, arborescence) en interne, mais donne le contrôle de l'élément spécifique à tout le monde. L'interface résume cela et ignore complètement l'implémentation et décrit uniquement les attentes.

Malheureusement, de nombreuses implémentations se trompent et définissent l'interface de ces sortes d'objets pour donner un accès direct à l'objet réel. Quelque chose comme:

interface Container<T>
{
    typedef ...T... TRef; //<somehow make TRef to be a reference or pointer to the memory location of T
    TRef item(int index); 
}

C'est cassé. Les implémentations de Container doivent explicitement remettre le contrôle de leurs composants internes à quiconque les utilise. Je n'ai pas encore vu un langage à valeur mutable où cela soit bien (les langages avec une sémantique à valeur immuable sont par définition bien du point de vue de la corruption des données, mais pas nécessairement du point de vue de l'espionnage des données).

Vous pouvez améliorer / corriger les getters / setter en utilisant uniquement la copie sémantique, ou en utilisant un proxy:

interface Proxy<T>
{
     operator T(); //<returns a copy
     ... operator ->(); //<permits a function call to be forwarded to an element
     Proxy<T> operator=(T); //< permits the specific element to be replaced/assigned by another T.
}

interface Container<T>
{
     Proxy<T> item(int index);
     T item(int index); //<When T is a copy of the original value.
     void item(int index, T new_value); //<where new_value is used to replace the old value
}

On peut soutenir qu'une fonction escroc pourrait encore jouer au chaos ici (avec assez d'efforts, la plupart des choses sont possibles), mais la copie sémantique et / ou le proxy réduisent les risques d'erreurs.

  • débordement
  • débordement
  • les interactions avec le sous-élément sont vérifiées par type / vérifiables par type (dans les langues perdues, c'est une aubaine)
  • L'élément réel peut ou non être résident en mémoire.

Getters / Setters privés

Il s'agit du dernier bastion de getters et setters travaillant directement sur le type. En fait, je n'appellerais même pas ces getters et setters mais des accesseurs et des manipulateurs.

Dans ce contexte, la manipulation d'une partie spécifique de la structure de données nécessite parfois / presque toujours / généralement une tenue de livres spécifique. Supposons que lorsque vous mettez à jour la racine d'une arborescence, le cache de surveillance doit être purgé, ou lorsque vous accédez à l'élément de données externe, un verrou doit être obtenu / libéré. Dans ces cas, il est logique d'appliquer le principal DRY et de répartir ces actions ensemble.

Dans le contexte privé, il est toujours possible pour les autres fonctions de la famille de contourner ces «getters et setters» et de manipuler la structure des données. C'est pourquoi je les considère davantage comme des accesseurs et des manipulateurs. Vous pouvez accéder directement aux données ou compter sur un autre membre de la famille pour obtenir cette partie correctement.

Getters / Setters protégés

Dans un contexte protégé, ce n'est pas très différent d'un contexte public. Les fonctions étrangères éventuellement escrocs veulent accéder à la structure de données. Donc non, s'ils existent, ils fonctionnent comme des getters / setters publics.

Kain0_0
la source
0

De l'expérience C ++, le setter / getter est utile pour 2 scénarios:

  1. si vous devez effectuer un contrôle d'intégrité avant d'attribuer une valeur au membre comme des chaînes ou un tableau.
  2. cela peut être utile lorsqu'une surcharge de fonction est nécessaire comme une valeur int pour booléen lorsque -1 (ou des valeurs négatives) est faux. et vice versa pour getter.

En dehors de cela, la sécurité est un point valable mais son importance est limitée à très peu d'applications de développement comme les modules liés à la connexion ou à l'accès à la base de données. Pourquoi s'embêter à coder en plus quand peu de gens utilisent votre module?

Ajay nandoriya
la source