Pourquoi utiliser des getters et des setters / accesseurs?

1544

Quel est l'avantage d'utiliser des getters et setters - qui ne sont que get et set - au lieu d'utiliser simplement des champs publics pour ces variables?

Si les getters et les setters font jamais plus que le simple get / set, je peux comprendre celui-ci très rapidement, mais je ne sais pas à 100% comment:

public String foo;

est pire que:

private String foo;
public void setFoo(String foo) { this.foo = foo; }
public String getFoo() { return foo; }

Alors que le premier prend beaucoup moins de code passe-partout.

Dean J
la source
6
@Dean J: Dupliquer avec de nombreuses autres questions: stackoverflow.com/search?q=getters+setters
Asaph
8
Bien sûr, les deux sont tout aussi mauvais lorsque l'objet n'a pas besoin de changer de propriété. Je préfère tout rendre privé, puis ajouter des getters si cela est utile et des setters si nécessaire.
Tordek
26
Google "les accesseurs sont mauvais"
OMG Ponies
34
«Les accesseurs sont mauvais» si vous écrivez du code fonctionnel ou des objets immuables. S'il vous arrive d'écrire des objets mutables avec état, alors ils sont assez essentiels.
Christian Hayter
18
Dites, ne demandez pas. pragprog.com/articles/tell-dont-ask
Dave Jarvis

Réponses:

982

Il existe en fait de nombreuses bonnes raisons pour envisager d'utiliser des accesseurs plutôt que d'exposer directement les champs d'une classe - au-delà de l'argument de l'encapsulation et de faciliter les changements futurs.

Voici quelques-unes des raisons que je connais:

  • Encapsulation du comportement associé à l'obtention ou à la définition de la propriété - cela permet d'ajouter plus facilement des fonctionnalités supplémentaires (comme la validation) ultérieurement.
  • Masquage de la représentation interne de la propriété tout en exposant une propriété à l'aide d'une autre représentation.
  • Isoler votre interface publique du changement - permettant à l'interface publique de rester constante pendant que la mise en œuvre change sans affecter les consommateurs existants.
  • Contrôle de la durée de vie et de la sémantique de gestion (élimination) de la propriété - particulièrement important dans les environnements de mémoire non gérés (comme C ++ ou Objective-C).
  • Fournir un point d'interception de débogage pour quand une propriété change au moment de l'exécution - le débogage quand et où une propriété a changé pour une valeur particulière peut être assez difficile sans cela dans certaines langues.
  • Meilleure interopérabilité avec les bibliothèques conçues pour fonctionner contre les getter / setters de propriétés - Mocking, Serialization et WPF me viennent à l'esprit.
  • Permettre aux héritiers de modifier la sémantique du comportement et de l'exposition de la propriété en remplaçant les méthodes getter / setter.
  • Permettre au getter / setter d'être transmis sous forme d'expressions lambda plutôt que de valeurs.
  • Les accesseurs et les setters peuvent autoriser différents niveaux d'accès - par exemple, le get peut être public, mais l'ensemble peut être protégé.
LBushkin
la source
60
+1. Juste pour ajouter: permettre le chargement paresseux. Permettre la copie lors de l'écriture.
NewbiZ
6
@bjarkef: Je pense que les publicméthodes d'accesseur provoquent la duplication de code et exposent les informations. Les accesseurs publics ne sont pas nécessaires pour le marshaling (sérialisation). Dans certains langages (Java), les publicaccesseurs get ne peuvent pas avoir le type de retour modifié sans casser les classes dépendantes. De plus, je pense qu'ils vont à l'encontre des principes OO. Voir aussi: stackoverflow.com/a/1462424/59087
Dave Jarvis
15
@sbi: Une chose qui est achetée en utilisant un setter de propriété, même dans un cadre bien conçu, est la possibilité d'avoir un objet en aviser un autre quand une propriété change.
supercat
22
@supercat: Cependant, je ne fais généralement pas la promotion de données publiques. La notification d'autres objets peut tout aussi bien être effectuée à partir de méthodes réelles fournissant une abstraction significative sur un simple conteneur de données avec (plus ou moins) des champs de données publics. Au lieu de plane.turnTo(dir); plane.setSpeed(spd); plane.setTargetAltitude(alt); plane.getBreaks().release();je veux dire plane.takeOff(alt). Les champs de données internes qui doivent être modifiés pour mettre l'avion en takingOffmode ne me préoccupent pas. Et quels autres objets ( breaks) la méthode notifie je ne veux pas savoir non plus.
sbi
8
@BenLee: Je ne sais vraiment pas comment le dire autrement. "Accesseurs" n'est qu'un synonyme de "getters / setters", et dans mes deux premiers commentaires j'ai expliqué pourquoi ceux-ci sont un abus du terme "OOP". Si vous devez y accéder et manipuler directement l'état, ce n'est pas un objet au sens OOP de ce terme.
sbi
480

Parce que dans 2 semaines (mois, années) à partir de maintenant, lorsque vous réaliserez que votre setter doit faire plus que simplement définir la valeur, vous vous rendrez également compte que la propriété a été utilisée directement dans 238 autres classes :-)

ChssPly76
la source
84
Je suis assis et regarde une application de ligne 500k où elle n'a jamais été nécessaire. Cela dit, si cela était nécessaire une fois, cela commencerait à provoquer un cauchemar de maintenance. Assez bon pour une coche pour moi.
Dean J
20
Je ne peux que vous envier et votre application :-) Cela dit, cela dépend aussi de votre pile logicielle. Delphi, par exemple (et C # - je pense?) Vous permet de définir des propriétés en tant que citoyens de première classe où ils peuvent lire / écrire un champ directement initialement mais - si vous en avez besoin - le faire via des méthodes getter / setter également. Beaucoup pratique. Java, hélas, ne le fait pas - sans parler de la norme javabeans qui vous oblige à utiliser également des getters / setters.
ChssPly76
14
Bien que ce soit en effet une bonne raison d'utiliser des accesseurs, de nombreux environnements de programmation et éditeurs proposent désormais une prise en charge de la refactorisation (soit dans l'EDI, soit en tant que compléments gratuits), ce qui réduit quelque peu l'impact du problème.
LBushkin
62
sonne comme une optimisation
prématurée
41
La question que vous devez vous poser lorsque vous vous demandez si vous devez implémenter des getters et setters est la suivante: pourquoi les utilisateurs d'une classe auraient-ils besoin d'accéder aux entrailles de la classe? Peu importe qu'ils le fassent directement ou qu'ils soient protégés par une pseudo couche mince - si les utilisateurs ont besoin d'accéder aux détails de l'implémentation, c'est un signe que la classe n'offre pas suffisamment d'abstraction. Voir aussi ce commentaire .
sbi
358

Un champ public n'est pas pire qu'une paire getter / setter qui ne fait rien d'autre que de renvoyer le champ et de l'affecter. Tout d'abord, il est clair que (dans la plupart des langues) il n'y a pas de différence fonctionnelle. Toute différence doit provenir d'autres facteurs, comme la maintenabilité ou la lisibilité.

Un avantage souvent mentionné des paires getter / setter ne l'est pas. Il y a cette affirmation selon laquelle vous pouvez modifier l'implémentation et vos clients n'ont pas besoin d'être recompilés. Soi-disant, les setters vous permettent d'ajouter des fonctionnalités comme la validation plus tard et vos clients n'ont même pas besoin de le savoir. Cependant, l'ajout de validation à un setter est une modification de ses conditions préalables, une violation du contrat précédent , qui était, tout simplement, "vous pouvez mettre n'importe quoi ici, et vous pouvez obtenir la même chose plus tard du getter".

Donc, maintenant que vous avez rompu le contrat, changer chaque fichier de la base de code est quelque chose que vous devriez faire, pas éviter. Si vous l'évitez, vous faites l'hypothèse que tout le code supposait que le contrat pour ces méthodes était différent.

Si cela ne devait pas être le contrat, alors l'interface permettait aux clients de mettre l'objet dans des états invalides. C'est exactement le contraire de l'encapsulation. Si ce champ ne pouvait pas vraiment être défini sur quoi que ce soit dès le début, pourquoi n'y avait-il pas la validation depuis le début?

Ce même argument s'applique à d'autres avantages supposés de ces paires getter / setter pass-through: si vous décidez ultérieurement de modifier la valeur définie, vous rompez le contrat. Si vous remplacez la fonctionnalité par défaut dans une classe dérivée, d'une manière au-delà de quelques modifications inoffensives (comme la journalisation ou tout autre comportement non observable), vous rompez le contrat de la classe de base. C'est une violation du principe de substituabilité de Liskov, qui est considéré comme l'un des principes de l'OO.

Si une classe a ces getters et setters stupides pour chaque domaine, alors c'est une classe qui n'a aucun invariant, aucun contrat . Est-ce vraiment une conception orientée objet? Si tout ce que la classe a est ces getters et setters, c'est juste un détenteur de données stupide, et les détenteurs de données stupides devraient ressembler à des détenteurs de données stupides:

class Foo {
public:
    int DaysLeft;
    int ContestantNumber;
};

L'ajout de paires getter / setter pass-through à une telle classe n'ajoute aucune valeur. Les autres classes devraient fournir des opérations significatives, pas seulement les opérations que les champs fournissent déjà. C'est ainsi que vous pouvez définir et maintenir des invariants utiles.

Client : "Que puis-je faire avec un objet de cette classe?"
Designer : "Vous pouvez lire et écrire plusieurs variables."
Client : "Oh ... cool, je suppose?"

Il y a des raisons d'utiliser des getters et des setters, mais si ces raisons n'existent pas, faire des paires getter / setter au nom de faux dieux d'encapsulation n'est pas une bonne chose. Les raisons valables de faire des getters ou setters incluent les choses souvent mentionnées comme les changements potentiels que vous pouvez apporter plus tard, comme la validation ou différentes représentations internes. Ou peut-être que la valeur doit être lisible par les clients mais pas inscriptible (par exemple, lire la taille d'un dictionnaire), donc un simple getter est un bon choix. Mais ces raisons devraient être présentes lorsque vous faites le choix, et pas seulement comme une chose potentielle que vous voudrez peut-être plus tard. Ceci est une instance de YAGNI ( vous n'en aurez pas besoin ).

R. Martinho Fernandes
la source
13
Excellente réponse (+1). Ma seule critique est qu'il m'a fallu plusieurs lectures pour comprendre en quoi la «validation» dans le dernier paragraphe différait de la «validation» dans les premiers (que vous avez rejetée dans le dernier cas mais promue dans le premier); l'ajustement du libellé pourrait être utile à cet égard.
Courses de légèreté en orbite le
14
C'est une excellente réponse, mais hélas, l'ère actuelle a oublié ce qu'est la «dissimulation d'informations» ou à quoi elle sert. Ils n'ont jamais lu sur l'immuabilité et dans leur quête du plus agile, n'ont jamais dessiné ce diagramme de transition d'état qui définissait ce que les états légaux d'un objet étaient et donc ce qui ne l'était pas.
Darrell Teague
2
Une observation clé est que les getters sont beaucoup plus utiles que les setters. Un getter peut renvoyer une valeur calculée ou une valeur calculée mise en cache. Tout ce qu'un setter peut faire est de valider et de changer le privatechamp. Si une classe n'a pas de setters, il est facile de la rendre immuable.
Raedwald
7
Euh, je suppose que vous ne savez pas ce qu'est un invariant de classe. S'il s'agit d'une structure non triviale, cela signifie à peu près qu'elle a des invariants à respecter et que l'on ne peut tout simplement pas la concevoir sans ceux-là. "Il y a eu un bug, un membre a été mis à jour incorrectement." se lit également à peu près comme "Une mise à jour de membre a violé les invariants de classe". Une classe bien conçue ne permet pas au code client de violer ses invariants.
R. Martinho Fernandes
5
+1 pour 'les détenteurs de données stupides devraient ressembler à des détenteurs de données stupides' Malheureusement, tout le monde semble concevoir tout autour des détenteurs de données stupides ces jours-ci, et beaucoup de cadres l'exigent même ...
Joeri Hendrickx
93

Beaucoup de gens parlent des avantages des getters et setters mais je veux jouer l'avocat du diable. En ce moment, je débogue un très gros programme où les programmeurs ont décidé de tout faire getters et setters. Cela peut sembler agréable, mais c'est un cauchemar de rétro-ingénierie.

Imaginons que vous parcouriez des centaines de lignes de code et que vous tombiez sur ceci:

person.name = "Joe";

C'est un morceau de code magnifiquement simple jusqu'à ce que vous réalisiez que c'est un passeur. Maintenant, vous suivez ce setter et constatez qu'il définit également person.firstName, person.lastName, person.isHuman, person.hasReallyCommonFirstName et appelle person.update (), qui envoie une requête à la base de données, etc. Oh, c'est où votre fuite de mémoire se produisait.

Comprendre un morceau de code local à première vue est une propriété importante de bonne lisibilité que les getters et les setters ont tendance à casser. C'est pourquoi j'essaye de les éviter quand je le peux et de minimiser ce qu'ils font quand je les utilise.

Kai
la source
8
Oui. Refactoring actuellement une grande base de code, et cela a été un cauchemar. Les getters et setters en font beaucoup trop, y compris en appelant d'autres getters et setters très occupés qui finissent par réduire la lisibilité à rien. La validation est une excellente raison d'utiliser des accesseurs, mais les utiliser pour faire bien plus que cela semble supprimer tout avantage potentiel.
Fadecomic
31
C'est un argument contre le sucre syntaxique, pas contre les setters en général.
Phil
6
Vrai et vrai, mais en plus, il montre comment les particuliers qui établissent des propriétés ouvrent la porte à des états invalides sans aucun remède pour garantir l'intégrité d'un objet donné qui permet à l'abstraction de fuir.
Darrell Teague
"C'est un morceau de code magnifiquement simple jusqu'à ce que vous réalisiez que c'est un setter" - hé, était le post original sur Java ou sur C #? :)
Honza Zidek
Je suis entièrement d'accord sur la lisibilité. C'est totalement explicite ce qui se passe quand person.name = "Joe";est appelé. Il n'y a pas de cruauté dans la manière de l'information, la mise en évidence de la syntaxe montre que c'est un champ et le refactoring est plus simple (pas besoin de refactoriser deux méthodes et un champ). Deuxièmement, tous ces cycles cérébraux gaspillés pensant «getIsValid ()» ou devrait-il être «isValid ()» etc. peuvent être oubliés. Je ne peux pas penser à une seule fois où j'ai été sauvé d'un bug par un getter / setter.
Will
53

Il y a plusieurs raisons. Mon préféré est quand vous devez changer le comportement ou régler ce que vous pouvez définir sur une variable. Par exemple, supposons que vous disposiez d'une méthode setSpeed ​​(int speed). Mais vous voulez que vous ne puissiez définir qu'une vitesse maximale de 100. Vous feriez quelque chose comme:

public void setSpeed(int speed) {
  if ( speed > 100 ) {
    this.speed = 100;
  } else {
    this.speed = speed;
  }
}

Maintenant, que se passe-t-il si PARTOUT dans votre code vous utilisiez le domaine public et que vous vous rendez compte que vous avez besoin de l'exigence ci-dessus? Amusez-vous à traquer chaque utilisation du domaine public au lieu de simplement modifier votre setter.

Mes 2 cents :)

Peter D
la source
62
La chasse à tous les usages du domaine public ne devrait pas être si difficile. Rendez-le privé et laissez le compilateur les trouver.
Nathan Fellman
12
c'est vrai bien sûr, mais pourquoi le rendre plus difficile qu'il ne devait l'être. L'approche get / set est toujours la meilleure réponse.
Hardryv
28
@Nathan: Trouver d' autres usages n'est pas le problème. Les changer tous, c'est.
Graeme Perrow
22
@GraemePerrow devoir les changer tous est un avantage, pas un problème :( Et si vous aviez du code qui supposait que la vitesse pouvait être supérieure à 100 (parce que, vous savez, avant de rompre le contrat, il le pouvait!) ( while(speed < 200) { do_something(); accelerate(); })
R. Martinho Fernandes
29
C'est un TRÈS mauvais exemple! Quelqu'un devrait appeler: myCar.setSpeed(157);et après quelques lignes speed = myCar.getSpeed();Et maintenant ... Je vous souhaite un bon débogage tout en essayant de comprendre pourquoi speed==100quand cela devrait être157
Piotr Aleksander Chmielowski
53

Dans un monde purement orienté objet, les getters et setters sont un terrible anti-pattern . Lisez cet article: Getters / Setters. Mal. Période . En un mot, ils encouragent les programmeurs à penser les objets comme des structures de données, et ce type de pensée est purement procédural (comme dans COBOL ou C). Dans un langage orienté objet, il n'y a pas de structures de données, mais uniquement des objets qui exposent le comportement (pas les attributs / propriétés!)

Vous pouvez trouver plus d'informations à leur sujet dans la section 3.5 des objets élégants (mon livre sur la programmation orientée objet).

yegor256
la source
7
Les getters et setters suggèrent un modèle de domaine anémique.
Raedwald
7
Point de vue intéressant. Mais dans la plupart des contextes de programmation, nous avons besoin de structures de données. Prenons l'exemple "Dog" de l'article lié. Oui, vous ne pouvez pas changer le poids d'un chien du monde réel en définissant un attribut ... mais a new Dog()n'est pas un chien. C'est un objet qui contient des informations sur un chien. Et pour cet usage, il est naturel de pouvoir corriger un poids mal enregistré.
Stephen C
3
Eh bien, je vous suggère que la plupart des programmes utiles n'ont pas besoin de modéliser / simuler des objets du monde réel. OMI, il ne s'agit pas du tout de langages de programmation. Il s'agit de ce pour quoi nous écrivons des programmes.
Stephen C
3
Du vrai monde ou pas, yegor a tout à fait raison. Si ce que vous avez est vraiment un "Struct" et vous n'avez pas besoin d'écrire de code qui le référence par son nom, mettez-le dans une table de hachage ou une autre structure de données. Si vous avez besoin d'écrire du code pour lui, mettez-le en tant que membre d'une classe et placez le code qui manipule cette variable dans la même classe et omettez le setter & getter. PS. Bien que je partage principalement le point de vue de Yegor, j'en suis venu à croire que les beans annotés sans code sont des structures de données quelque peu utiles - des getters sont parfois nécessaires, les setters ne devraient jamais exister.
Bill K
1
Je me suis laissé emporter - toute cette réponse, bien que correcte et pertinente, ne répond pas directement à la question. Peut-être que cela devrait dire "Les setters / getters ET les variables publiques sont faux" ... Pour être absolument précis, les setters et les variables publiques inscriptibles ne devraient jamais être utilisés alors que les getters sont à peu près les mêmes que les variables finales publiques et sont parfois un mal nécessaire mais ni l'un ni l'autre n'est bien meilleur que l'autre.
Bill K
38

L'un des avantages des accesseurs et des mutateurs est que vous pouvez effectuer une validation.

Par exemple, s'il fooétait public, je pourrais facilement le définir nullet quelqu'un d'autre pourrait essayer d'appeler une méthode sur l'objet. Mais ce n'est plus là! Avec une setFoométhode, je pouvais m'assurer que cela foon'était jamais réglé null.

Les accesseurs et les mutateurs permettent également l'encapsulation - si vous n'êtes pas censé voir la valeur une fois qu'elle est définie (peut-être qu'elle est définie dans le constructeur puis utilisée par des méthodes, mais qu'elle n'est jamais censée être modifiée), elle ne sera jamais vue par personne. Mais si vous pouvez autoriser d'autres classes à le voir ou à le modifier, vous pouvez fournir l'accesseur et / ou le mutateur approprié.

Thomas Owens
la source
28

Cela dépend de votre langue. Vous avez marqué ce "orienté objet" plutôt que "Java", donc je voudrais souligner que la réponse de ChssPly76 dépend du langage. En Python, par exemple, il n'y a aucune raison d'utiliser des getters et des setters. Si vous devez modifier le comportement, vous pouvez utiliser une propriété, qui enveloppe un getter et un setter autour de l'accès aux attributs de base. Quelque chose comme ça:

class Simple(object):
   def _get_value(self):
       return self._value -1

   def _set_value(self, new_value):
       self._value = new_value + 1

   def _del_value(self):
       self.old_values.append(self._value)
       del self._value

   value = property(_get_value, _set_value, _del_value)
jcdyer
la source
2
Oui, je l'ai dit dans un commentaire ci-dessous ma réponse. Java n'est pas le seul langage à utiliser des getters / setters comme béquille tout comme Python n'est pas le seul langage capable de définir des propriétés. Le point principal, cependant, demeure - la «propriété» n'est pas le même «domaine public».
ChssPly76
1
@jcd - pas du tout. Vous définissez votre "interface" (l'API publique serait un meilleur terme ici) en exposant vos champs publics. Une fois cela fait, il n'y a plus de retour possible. Les propriétés ne sont PAS des champs car elles vous fournissent un mécanisme pour intercepter les tentatives d'accès aux champs (en les routant vers des méthodes si celles-ci sont définies); c'est, cependant, rien de plus que du sucre de syntaxe sur les méthodes getter / setter. C'est extrêmement pratique, mais cela ne modifie pas le paradigme sous-jacent - exposer des champs sans aucun contrôle sur leur accès viole le principe d'encapsulation.
ChssPly76
14
@ ChssPly76 — je ne suis pas d'accord. J'ai autant de contrôle que s'il s'agissait de propriétés, car je peux en faire des propriétés quand j'en ai besoin. Il n'y a pas de différence entre une propriété qui utilise des getters et setters standard et un attribut raw, sauf que l'attribut raw est plus rapide, car il utilise le langage sous-jacent, plutôt que d'appeler des méthodes. Fonctionnellement, ils sont identiques. L'encapsulation ne peut être violée que si vous pensez que les parenthèses ( obj.set_attr('foo')) sont intrinsèquement supérieures aux signes égaux ( obj.attr = 'foo'). L'accès public est un accès public.
jcdyer
1
@jcdyer autant de contrôle oui, mais pas autant de lisibilité, d'autres supposent souvent à tort que obj.attr = 'foo'ne définit que la variable sans que rien ne se passe
Timo Huovinen
9
@TimoHuovinen En quoi est-ce différent d'un utilisateur en Java en supposant que obj.setAttr('foo')"définit simplement la variable sans que rien d'autre ne se passe"? Si c'est une méthode publique, alors c'est une méthode publique. Si vous l'utilisez pour obtenir un effet secondaire et qu'il est public, vous feriez mieux de pouvoir compter sur tout ce qui fonctionne comme si seul cet effet secondaire prévu se produisait (avec tous les autres détails de mise en œuvre et autres effets secondaires, l'utilisation des ressources, etc.) , caché des préoccupations de l'utilisateur). Ce n'est absolument pas différent avec Python. La syntaxe de Python pour obtenir l'effet est simplement plus simple.
le
25

Eh bien, je veux juste ajouter que même si parfois elles sont nécessaires pour l'encapsulation et la sécurité de vos variables / objets, si nous voulons coder un vrai programme orienté objet, alors nous devons ARRÊTER DE SURUTILISER LES ACCESSOIRES , car parfois nous dépendons beaucoup sur eux quand ce n'est pas vraiment nécessaire et cela fait presque la même chose que si nous mettons les variables publiques.

Jorge Aguilar
la source
24

Merci, cela a vraiment clarifié ma pensée. Voici maintenant (presque) 10 (presque) bonnes raisons de NE PAS utiliser les getters et setters:

  1. Lorsque vous réalisez que vous devez faire plus que simplement définir et obtenir la valeur, vous pouvez simplement rendre le champ privé, qui vous indiquera instantanément où vous y avez directement accédé.
  2. Toute validation que vous y effectuez ne peut être que contextuelle, ce qui est rarement le cas dans la pratique.
  3. Vous pouvez modifier la valeur définie - c'est un cauchemar absolu lorsque l'appelant vous transmet une valeur qu'il [horreur de choc] veut que vous stockiez TELLE QUELLE.
  4. Vous pouvez masquer la représentation interne - fantastique, donc vous vous assurez que toutes ces opérations sont symétriques, n'est-ce pas?
  5. Vous avez isolé votre interface publique des changements sous les feuilles - si vous concevez une interface et n'êtes pas sûr que l'accès direct à quelque chose soit OK, alors vous devriez continuer à concevoir.
  6. Certaines bibliothèques s'y attendent, mais pas beaucoup - la réflexion, la sérialisation, les objets fictifs fonctionnent tous très bien avec les champs publics.
  7. Héritant de cette classe, vous pouvez remplacer la fonctionnalité par défaut - en d'autres termes, vous pouvez vraiment confondre les appelants en masquant non seulement l'implémentation, mais en la rendant incohérente.

Les trois derniers que je viens de quitter (N / A ou D / C) ...

StackedCrooked
la source
11
Je pense que l'argument crucial est que "si vous concevez une interface et n'êtes pas sûr que l'accès direct à quelque chose soit OK, alors vous devriez continuer à concevoir." C'est le problème le plus important avec les getters / setters: ils réduisent une classe à un simple conteneur de champs (plus ou moins) publics. Dans la vraie POO, cependant, un objet est plus qu'un conteneur de champs de données. Il encapsule l'état et les algorithmes pour manipuler cet état. Ce qui est crucial dans cette déclaration, c'est que l'état est censé être encapsulé et uniquement manipulé par les algorithmes fournis par l'objet.
sbi
22

Je sais qu'il est un peu tard, mais je pense qu'il y a des gens qui s'intéressent à la performance.

J'ai fait un petit test de performance. J'ai écrit une classe "NumberHolder" qui, bien, contient un nombre entier. Vous pouvez lire cet entier en utilisant la méthode getter anInstance.getNumber()ou en accédant directement au numéro en utilisant anInstance.number. Mon programme lit le nombre 1 000 000 000 fois, dans les deux sens. Ce processus est répété cinq fois et l'heure est imprimée. J'ai le résultat suivant:

Time 1: 953ms, Time 2: 741ms
Time 1: 655ms, Time 2: 743ms
Time 1: 656ms, Time 2: 634ms
Time 1: 637ms, Time 2: 629ms
Time 1: 633ms, Time 2: 625ms

(Le temps 1 est le chemin direct, le temps 2 est le getter)

Vous voyez, le getter est (presque) toujours un peu plus rapide. J'ai ensuite essayé avec différents nombres de cycles. Au lieu d'un million, j'ai utilisé 10 millions et 0,1 million. Les resultats:

10 millions de cycles:

Time 1: 6382ms, Time 2: 6351ms
Time 1: 6363ms, Time 2: 6351ms
Time 1: 6350ms, Time 2: 6363ms
Time 1: 6353ms, Time 2: 6357ms
Time 1: 6348ms, Time 2: 6354ms

Avec 10 millions de cycles, les délais sont quasiment les mêmes. Voici 100 000 (0,1 million) cycles:

Time 1: 77ms, Time 2: 73ms
Time 1: 94ms, Time 2: 65ms
Time 1: 67ms, Time 2: 63ms
Time 1: 65ms, Time 2: 65ms
Time 1: 66ms, Time 2: 63ms

Toujours avec des quantités de cycles différentes, le getter est un peu plus rapide que la manière habituelle. J'espère que cela vous a aidé.

kangalioo
la source
3
Il y a une surcharge "notable" ayant un appel de fonction pour accéder à la mémoire au lieu de simplement charger l'adresse d'un objet et d'ajouter un décalage pour accéder aux membres. Les chances sont que la machine virtuelle optimise votre getter de toute façon. Quoi qu'il en soit, les frais généraux mentionnés ne valent pas la peine de perdre tous les avantages des getters / setters.
Alex
16

N'utilisez pas de setters getters à moins que cela ne soit nécessaire pour votre livraison actuelle Ie Ne pensez pas trop à ce qui se passerait à l'avenir, si quelque chose à changer est une demande de changement dans la plupart des applications de production, des systèmes.

Pensez simple, facile, ajoutez de la complexité au besoin.

Je ne profiterais pas de l'ignorance des propriétaires d'entreprise possédant un savoir-faire technique approfondi simplement parce que je pense que c'est correct ou que j'aime l'approche.

J'ai un système massif écrit sans getters setters uniquement avec des modificateurs d'accès et quelques méthodes pour valider n exécuter la logique biz. Si vous en aviez absolument besoin. Utilisez n'importe quoi.

Mohamed
la source
16

Nous utilisons des getters et setters:

  • pour la réutilisabilité
  • pour effectuer la validation dans les étapes ultérieures de la programmation

Les méthodes getter et setter sont des interfaces publiques pour accéder aux membres de classe privés.


Mantra d'encapsulation

Le mantra de l'encapsulation est de rendre les champs privés et les méthodes publiques.

Getter Methods: Nous pouvons avoir accès à des variables privées.

Setter Methods: Nous pouvons modifier les champs privés.

Même si les méthodes getter et setter n'ajoutent pas de nouvelles fonctionnalités, nous pouvons changer d'avis revenir plus tard pour rendre cette méthode

  • meilleur;
  • plus sûr; et
  • plus rapide.

Partout où une valeur peut être utilisée, une méthode qui renvoie cette valeur peut être ajoutée. Au lieu de:

int x = 1000 - 500

utilisation

int x = 1000 - class_name.getValue();

En termes simples

Représentation de la classe "Personne"

Supposons que nous ayons besoin d'en stocker les détails Person. Cela Persona les champs name, ageet sex. Pour ce faire, vous devez créer des méthodes pour name, ageet sex. Maintenant , si nous avons besoin de créer une autre personne, il devient nécessaire de créer les méthodes pour name, age, sexencore une fois.

Au lieu de cela, nous pouvons créer un bean class(Person)avec des méthodes getter et setter. Donc, demain, nous pouvons simplement créer des objets de ce Bean class(Person class)chaque fois que nous devons ajouter une nouvelle personne (voir la figure). Ainsi, nous réutilisons les champs et les méthodes de la classe bean, ce qui est bien mieux.

Devrath
la source
15

J'ai passé un bon moment à réfléchir à cela pour le cas Java, et je crois que les vraies raisons sont:

  1. Code à l'interface, pas à l'implémentation
  2. Les interfaces spécifient uniquement les méthodes, pas les champs

En d'autres termes, la seule façon de spécifier un champ dans une interface est de fournir une méthode pour écrire une nouvelle valeur et une méthode pour lire la valeur actuelle.

Ces méthodes sont le tristement célèbre getter et setter ....

Thorbjørn Ravn Andersen
la source
1
D'accord, deuxième question; dans le cas où c'est un projet où vous n'exportez la source vers personne et que vous avez le contrôle total de la source ... gagnez-vous quelque chose avec les getters et les setters?
Dean J
2
Dans tout projet Java non trivial, vous devez coder en interfaces afin de rendre les choses gérables et testables (pensez aux maquettes et aux objets proxy). Si vous utilisez des interfaces, vous avez besoin de getters et de setters.
Thorbjørn Ravn Andersen
15

Il peut être utile pour le chargement paresseux. Supposons que l'objet en question soit stocké dans une base de données et que vous ne souhaitiez pas l'obtenir à moins d'en avoir besoin. Si l'objet est récupéré par un getter, alors l'objet interne peut être nul jusqu'à ce que quelqu'un le demande, alors vous pouvez aller le chercher lors du premier appel au getter.

J'ai eu une classe de page de base dans un projet qui m'a été remise qui chargeait des données à partir de quelques appels de service Web différents, mais les données de ces appels de service Web n'étaient pas toujours utilisées dans toutes les pages enfants. Les services Web, pour tous les avantages, ouvrent la voie à de nouvelles définitions de «lent», de sorte que vous ne voulez pas passer un appel de service Web si vous n'êtes pas obligé.

Je suis passé des champs publics aux getters, et maintenant les getters vérifient le cache, et s'il n'est pas là, appelez le service Web. Donc, avec un peu d'emballage, beaucoup d'appels de service Web ont été empêchés.

Ainsi, le getter me sauve d'essayer de comprendre, sur chaque page enfant, ce dont j'ai besoin. Si j'en ai besoin, j'appelle le getter, et il va le trouver pour moi si je ne l'ai pas déjà.

    protected YourType _yourName = null;
    public YourType YourName{
      get
      {
        if (_yourName == null)
        {
          _yourName = new YourType();
          return _yourName;
        }
      }
    }
brise-plume
la source
Alors, le getter appelle-t-il le setter?
icc97
J'ai ajouté un exemple de code de la façon dont je l'ai fait dans le passé - essentiellement, vous stockez la classe réelle dans un membre protégé, puis renvoyez ce membre protégé dans l'accesseur get, l'initialisant s'il n'est pas initialisé.
quillbreaker
13

Un aspect que j'ai manqué dans les réponses jusqu'à présent, la spécification d'accès:

  • pour les membres, vous n'avez qu'une seule spécification d'accès pour définir et obtenir
  • pour les setters et les getters, vous pouvez affiner et définir séparément
jdehaan
la source
13

EDIT: J'ai répondu à cette question car il y a beaucoup de gens qui apprennent la programmation qui le demandent, et la plupart des réponses sont très compétentes techniquement, mais elles ne sont pas aussi faciles à comprendre si vous êtes un débutant. Nous étions tous des débutants, alors j'ai pensé essayer une réponse plus amicale aux débutants.

Les deux principaux sont le polymorphisme et la validation. Même si ce n'est qu'une stupide structure de données.

Disons que nous avons cette classe simple:

public class Bottle {
  public int amountOfWaterMl;
  public int capacityMl;
}

Une classe très simple qui contient la quantité de liquide qu'il contient et sa capacité (en millilitres).

Que se passe-t-il quand je le fais:

Bottle bot = new Bottle();
bot.amountOfWaterMl = 1500;
bot.capacityMl = 1000;

Eh bien, vous ne vous attendriez pas à ce que cela fonctionne, non? Vous voulez qu'il y ait une sorte de contrôle de santé mentale. Et pire, que se passe-t-il si je n'ai jamais spécifié la capacité maximale? Oh mon cher, nous avons un problème.

Mais il y a aussi un autre problème. Et si les bouteilles n'étaient qu'un type de récipient? Et si nous avions plusieurs conteneurs, tous avec des capacités et des quantités de liquide remplies? Si nous pouvions simplement créer une interface, nous pourrions laisser le reste de notre programme accepter cette interface, et les bouteilles, les jerrycans et toutes sortes de choses fonctionneraient de manière interchangeable. Ce ne serait pas mieux? Comme les interfaces demandent des méthodes, c'est aussi une bonne chose.

Nous finirions avec quelque chose comme:

public interface LiquidContainer {
  public int getAmountMl();
  public void setAmountMl(int amountMl);
  public int getCapacityMl();
}

Génial! Et maintenant, nous changeons simplement la bouteille en ceci:

public class Bottle extends LiquidContainer {
  private int capacityMl;
  private int amountFilledMl;

  public Bottle(int capacityMl, int amountFilledMl) {
    this.capacityMl = capacityMl;
    this.amountFilledMl = amountFilledMl;
    checkNotOverFlow();
  }

  public int getAmountMl() {
    return amountFilledMl;
  }

  public void setAmountMl(int amountMl) {
     this.amountFilled = amountMl;
     checkNotOverFlow();
  }
  public int getCapacityMl() {
    return capacityMl;
  }

  private void checkNotOverFlow() {
    if(amountOfWaterMl > capacityMl) {
      throw new BottleOverflowException();
    }
}

Je laisse la définition de la BottleOverflowException comme un exercice au lecteur.

Maintenant, remarquez à quel point c'est plus robuste. Nous pouvons traiter tout type de conteneur dans notre code maintenant en acceptant LiquidContainer au lieu de Bottle. Et la façon dont ces bouteilles traitent ce genre de choses peut différer. Vous pouvez avoir des bouteilles qui écrivent leur état sur le disque lorsqu'il change, ou des bouteilles qui enregistrent sur des bases de données SQL ou GNU sait quoi d'autre.

Et tout cela peut avoir différentes façons de gérer diverses whoopsies. La bouteille vérifie simplement et si elle déborde, elle lance une RuntimeException. Mais ce n'est peut-être pas la bonne chose à faire. (Il y a une discussion utile à avoir sur le traitement des erreurs, mais je le maintiens très simple ici exprès. Les personnes dans les commentaires vont probablement souligner les défauts de cette approche simpliste.;))

Et oui, il semble que nous passions d'une idée très simple à des réponses bien meilleures rapidement.

Veuillez également noter que vous ne pouvez pas modifier la capacité d'une bouteille. Il est maintenant gravé dans la pierre. Vous pouvez le faire avec un int en le déclarant final. Mais s'il s'agissait d'une liste, vous pouvez la vider, y ajouter de nouvelles choses, etc. Vous ne pouvez pas limiter l'accès à toucher les entrailles.

Il y a aussi la troisième chose que tout le monde n'a pas abordée: les getters et les setters utilisent des appels de méthode. Cela signifie qu'ils ressemblent à des méthodes normales partout ailleurs. Au lieu d'avoir une syntaxe spécifique bizarre pour les DTO et autres choses, vous avez la même chose partout.

Haakon Løtveit
la source
2
Merci pour la première explication à moitié décente des interfaces que j'ai lues, sans aucune référence aux "télécommandes" ou aux "voitures"
djvs
1
"Vous pouvez avoir des bouteilles qui écrivent leur état sur le disque quand il change, ou des bouteilles qui enregistrent sur des bases de données SQL", ai-je lollé si fort xD. Mais bon, belle idée de le présenter!
Xerus
10

Dans les langages qui ne prennent pas en charge les «propriétés» (C ++, Java) ou qui nécessitent une recompilation des clients lors de la modification des champs en propriétés (C #), l'utilisation des méthodes get / set est plus facile à modifier. Par exemple, l'ajout d'une logique de validation à une méthode setFoo ne nécessitera pas de modifier l'interface publique d'une classe.

Dans les langages qui prennent en charge les propriétés "réelles" (Python, Ruby, peut-être Smalltalk?), Il est inutile d'obtenir des méthodes / set.

John Millikin
la source
1
Re: C #. Si vous ajoutez des fonctionnalités à un get / set, cela ne nécessiterait-il pas une recompilation?
steamer25
@ steamer25: désolé, mal tapé. Je voulais dire que les clients de la classe devront être recompilés.
John Millikin
5
L'ajout d'une logique de validation à une méthode setFoo ne nécessitera pas de changer l'interface d'une classe au niveau du langage , mais cela modifie l'interface réelle , alias contrat, car elle modifie les conditions préalables. Pourquoi voudrait-on que le compilateur ne traite pas cela comme un changement de rupture quand il l'est ?
R. Martinho Fernandes
@ R.MartinhoFernandes comment régler ce problème "cassé"? Le compilateur ne peut pas dire s'il se casse ou non. Ce n'est une préoccupation que lorsque vous écrivez des bibliothèques pour d'autres, mais vous le faites comme un zOMG universel ici, c'est des dragons!
Phil
3
Exiger une recompilation, comme mentionné dans la réponse, est une façon pour le compilateur de vous informer d'un éventuel changement de rupture. Et presque tout ce que j'écris est effectivement "une bibliothèque pour les autres", car je ne travaille pas seul. J'écris du code qui a des interfaces que d'autres personnes du projet utiliseront. Quelle est la différence? Enfer, même si je serai l'utilisateur de ces interfaces, pourquoi devrais-je maintenir mon code à des normes de qualité inférieures? Je n'aime pas travailler avec des interfaces gênantes, même si c'est moi qui les écrit.
R. Martinho Fernandes
6

L'un des principes de base de la conception OO: l' encapsulation!

Cela vous offre de nombreux avantages, dont l'un est que vous pouvez modifier la mise en œuvre du getter / setter dans les coulisses, mais tout consommateur de cette valeur continuera de fonctionner tant que le type de données reste le même.

Justin Niessner
la source
23
Les getters et setters d'encapsulation sont ridiculement minces. Voyez ici .
sbi
Si votre interface publique indique que «foo» est de type «T» et peut être défini sur n'importe quoi, vous ne pouvez jamais changer cela. Vous ne pouvez pas décider de le faire de type 'Y', ni imposer des règles telles que des contraintes de taille. Ainsi, si vous avez un get / set public qui ne fait pas beaucoup de set / get, vous ne gagnez rien qu'un champ public ne proposera pas et le rendra plus lourd à utiliser. Si vous avez une contrainte, telle que l'objet peut être défini sur null, ou que la valeur doit être dans une plage, alors oui, une méthode d'ensemble publique serait requise, mais vous présentez toujours un contrat de définition de cette valeur qui peut ' t change
thecoshman
Pourquoi un contrat ne peut-il pas changer?
Phil
5
Pourquoi les choses devraient-elles continuer à se compiler si les contrats changent?
R. Martinho Fernandes
5

Vous devez utiliser des getters et setters lorsque:

  • Vous avez affaire à quelque chose qui est conceptuellement un attribut, mais:
    • Votre langue n'a pas de propriétés (ou un mécanisme similaire, comme les traces de variables de Tcl), ou
    • La prise en charge des propriétés de votre langue n'est pas suffisante pour ce cas d'utilisation, ou
    • Les conventions idiomatiques de votre langue (ou parfois de votre framework) encouragent les getters ou setters pour ce cas d'utilisation.

Il s'agit donc très rarement d'une question d'ordre général; c'est une question spécifique à la langue, avec des réponses différentes pour différentes langues (et différents cas d'utilisation).


Du point de vue de la théorie OO, les getters et setters sont inutiles. L'interface de votre classe est ce qu'elle fait, pas quel est son état. (Sinon, vous avez écrit la mauvaise classe.) Dans des cas très simples, où ce que fait une classe est simplement, par exemple, représenter un point en coordonnées rectangulaires, * les attributs font partie de l'interface; les getters et les setters ne font qu'obscurcir cela. Mais dans tout sauf des cas très simples, ni les attributs ni les getters et setters ne font partie de l'interface.

Autrement dit: si vous pensez que les consommateurs de votre classe ne devraient même pas savoir que vous avez un spamattribut, et encore moins pouvoir le changer bon gré mal gré, alors leur donner une set_spamméthode est la dernière chose que vous voulez faire.

* Même pour cette classe simple, vous ne voudrez pas nécessairement autoriser la définition des valeurs xet y. Si cela est vraiment une classe, il ne devrait pas avoir des méthodes telles que translate, rotateetc.? Si ce n'est qu'une classe parce que votre langage n'a pas d'enregistrements / structures / tuples nommés, alors ce n'est pas vraiment une question d'OO…


Mais personne ne fait jamais de conception OO générale. Ils font la conception et l'implémentation dans un langage spécifique. Et dans certaines langues, les getters et setters sont loin d'être inutiles.

Si votre langue n'a pas de propriétés, alors la seule façon de représenter quelque chose qui est conceptuellement un attribut, mais qui est en fait calculé, ou validé, etc., est par le biais de getters et setters.

Même si votre langue a des propriétés, il peut y avoir des cas où elles sont insuffisantes ou inappropriées. Par exemple, si vous souhaitez autoriser les sous-classes à contrôler la sémantique d'un attribut, dans les langues sans accès dynamique, une sous-classe ne peut pas substituer une propriété calculée à un attribut.

Quant à la "que se passe-t-il si je veux changer ma mise en œuvre plus tard?" question (qui est répétée plusieurs fois dans une formulation différente à la fois dans la question du PO et dans la réponse acceptée): s'il s'agit vraiment d'un changement d'implémentation pur et que vous avez commencé avec un attribut, vous pouvez le changer en propriété sans affecter l'interface. À moins, bien sûr, que votre langue ne le prenne pas en charge. Donc, c'est vraiment encore le même cas.

De plus, il est important de suivre les idiomes du langage (ou du framework) que vous utilisez. Si vous écrivez un beau code de style Ruby en C #, tout développeur C # expérimenté autre que vous aura du mal à le lire, et c'est mauvais. Certaines langues ont des cultures plus fortes autour de leurs conventions que d'autres. - et ce n'est peut-être pas une coïncidence si Java et Python, qui sont aux extrémités opposées du spectre des getters idiomatiques, ont deux des cultures les plus fortes.

Au-delà des lecteurs humains, il y aura des bibliothèques et des outils qui s'attendent à ce que vous suiviez les conventions et rendiez votre vie plus difficile si vous ne le faites pas. Accrocher les widgets d'Interface Builder à autre chose que les propriétés ObjC, ou utiliser certaines bibliothèques de simulation Java sans getters, vous rend la vie plus difficile. Si les outils sont importants pour vous, ne les combattez pas.

abarnert
la source
4

Du point de vue de la conception orientée objet, les deux alternatives peuvent être préjudiciables à la maintenance du code en affaiblissant l'encapsulation des classes. Pour une discussion, vous pouvez consulter cet excellent article: http://typicalprogrammer.com/?p=23

andrers52
la source
3

Les méthodes getter et setter sont des méthodes d'accesseur, ce qui signifie qu'elles sont généralement une interface publique pour changer les membres d'une classe privée. Vous utilisez des méthodes getter et setter pour définir une propriété. Vous accédez aux méthodes getter et setter en tant que propriétés en dehors de la classe, même si vous les définissez dans la classe en tant que méthodes. Ces propriétés en dehors de la classe peuvent avoir un nom différent de celui de la classe.

L'utilisation des méthodes getter et setter présente certains avantages, tels que la possibilité de vous permettre de créer des membres avec des fonctionnalités sophistiquées auxquelles vous pouvez accéder comme des propriétés. Ils vous permettent également de créer des propriétés en lecture seule et en écriture seule.

Même si les méthodes getter et setter sont utiles, vous devez faire attention à ne pas en abuser car, entre autres, elles peuvent rendre la maintenance du code plus difficile dans certaines situations. En outre, ils donnent accès à votre implémentation de classe, comme les membres publics. La pratique de la POO décourage l'accès direct aux propriétés d'une classe.

Lorsque vous écrivez des classes, vous êtes toujours encouragé à rendre le plus grand nombre possible de variables d'instance privées et à ajouter des méthodes getter et setter en conséquence. En effet, il peut arriver à plusieurs reprises que vous ne souhaitiez pas permettre aux utilisateurs de modifier certaines variables au sein de vos classes. Par exemple, si vous disposez d'une méthode statique privée qui suit le nombre d'instances créées pour une classe spécifique, vous ne voulez pas qu'un utilisateur modifie ce compteur à l'aide de code. Seule l'instruction constructeur doit incrémenter cette variable chaque fois qu'elle est appelée. Dans cette situation, vous pouvez créer une variable d'instance privée et autoriser une méthode getter uniquement pour la variable compteur, ce qui signifie que les utilisateurs ne peuvent récupérer la valeur actuelle qu'en utilisant la méthode getter et qu'ils ne pourront pas définir de nouvelles valeurs en utilisant la méthode setter.

Sumit Singh
la source
3

Le code évolue . privateest idéal lorsque vous avez besoin d'une protection des membres de données . Finalement, toutes les classes devraient être en quelque sorte des "mini-programmes" qui ont une interface bien définie que vous ne pouvez pas simplement visser avec les internes de .

Cela dit, le développement logiciel ne consiste pas à installer cette version finale de la classe comme si vous appuyiez sur une statue en fonte du premier coup. Pendant que vous travaillez avec, le code ressemble plus à de l'argile. Il évolue au fur et à mesure que vous le développez et en apprenez davantage sur le domaine de problème que vous résolvez. Au cours du développement, les classes peuvent interagir les unes avec les autres comme elles le devraient (dépendance que vous prévoyez de prendre en compte), fusionner ou se séparer. Je pense donc que le débat se résume à des gens qui ne veulent pas écrire religieusement

int getVar() const { return var ; }

Vous avez donc:

doSomething( obj->getVar() ) ;

Au lieu de

doSomething( obj->var ) ;

Non seulement il est getVar()visuellement bruyant, mais il donne cette illusion qui gettingVar()est en quelque sorte un processus plus complexe qu'il ne l'est réellement. La façon dont vous (en tant qu'écrivain de classe) considérez le caractère sacré de varest particulièrement déroutante pour un utilisateur de votre classe s'il a un passeur - alors il semble que vous installez ces portes pour "protéger" quelque chose que vous insistez est précieux, (le caractère sacré de var) mais pourtant, même vous concédez que varla protection ne vaut pas grand-chose par la capacité de quiconque à entrer et set varà la valeur qu'il veut, sans même que vous jetiez un œil à ce qu'il fait.

Je programme donc comme suit (en supposant une approche de type "agile" - c'est-à-dire quand j'écris du code ne sachant pas exactement ce qu'il va faire / je n'ai pas le temps ou l'expérience pour planifier un ensemble d'interface de style cascade sophistiqué):

1) Commencez avec tous les membres publics pour les objets de base avec des données et un comportement. C'est pourquoi dans tout mon code "exemple" C ++, vous remarquerez que j'utilise à la structplace de classpartout.

2) Lorsque le comportement interne d'un objet pour un membre de données devient suffisamment complexe (par exemple, il aime conserver un interne std::listdans un certain ordre), des fonctions de type accesseur sont écrites. Parce que je programme par moi-même, je ne mets pas toujours le membre privatetout de suite, mais quelque part dans l'évolution de la classe, le membre sera "promu" à l'un protectedou à l'autre private.

3) Les classes qui sont entièrement étoffées et ont des règles strictes sur leurs internes (c'est-à-dire qu'elles savent exactement ce qu'elles font, et vous ne devez pas "baiser" (terme technique) avec ses internes) reçoivent la classdésignation, membres privés par défaut, et seuls quelques membres sélectionnés sont autorisés à l'être public.

Je trouve que cette approche me permet d'éviter de rester assis là et d'écrire religieusement des getter / setters lorsque beaucoup de membres de données sont migrés, déplacés, etc. pendant les premières étapes de l'évolution d'une classe.

bobobobo
la source
1
"... une interface bien définie que vous ne pouvez pas simplement visser avec les internes" et la validation dans les setters.
Agi Hammerthief
3

Il y a une bonne raison d'envisager l'utilisation d'accesseurs car il n'y a pas d'héritage de propriété. Voir l'exemple suivant:

public class TestPropertyOverride {
    public static class A {
        public int i = 0;

        public void add() {
            i++;
        }

        public int getI() {
            return i;
        }
    }

    public static class B extends A {
        public int i = 2;

        @Override
        public void add() {
            i = i + 2;
        }

        @Override
        public int getI() {
            return i;
        }
    }

    public static void main(String[] args) {
        A a = new B();
        System.out.println(a.i);
        a.add();
        System.out.println(a.i);
        System.out.println(a.getI());
    }
}

Production:

0
0
4
GeZo
la source
3

Les getters et setters sont utilisés pour implémenter deux des aspects fondamentaux de la programmation orientée objet qui sont:

  1. Abstraction
  2. Encapsulation

Supposons que nous ayons une classe Employé:

package com.highmark.productConfig.types;

public class Employee {

    private String firstName;
    private String middleName;
    private String lastName;

    public String getFirstName() {
      return firstName;
    }
    public void setFirstName(String firstName) {
       this.firstName = firstName;
    }
    public String getMiddleName() {
        return middleName;
    }
    public void setMiddleName(String middleName) {
         this.middleName = middleName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getFullName(){
        return this.getFirstName() + this.getMiddleName() +  this.getLastName();
    }
 }

Ici, les détails d'implémentation du nom complet sont cachés à l'utilisateur et ne sont pas accessibles directement à l'utilisateur, contrairement à un attribut public.

Pritam Banerjee
la source
1
Pour moi, avoir des tonnes de getters et de setters qui ne font rien d'unique est inutile. getFullName est une exception car il fait autre chose. Le fait de n'avoir que les trois variables publiques puis de garder getFullName rendra le programme plus facile à lire mais aura toujours cette chose de nom complet cachée. En général, je suis complètement d'accord avec les getters et setters si a. ils font quelque chose d'unique et / ou b. vous n'en avez qu'une, ouais vous pourriez avoir une finale publique et tout ça, mais non
FacelessTiger
1
L'avantage avec ceci est que vous pouvez changer les internes de la classe, sans changer l'interface. Disons qu'au lieu de trois propriétés, vous en aviez une - un tableau de chaînes. Si vous avez utilisé des getters et des setters, vous pouvez apporter cette modification, puis mettre à jour le getter / setters pour savoir que names [0] est prénom, names [1] est milieu, etc. Mais si vous venez d'utiliser des propriétés publiques , vous devrez également modifier chaque classe accédée Employee, car la propriété firstName qu'ils ont utilisée n'existe plus.
Andrew Hows
1
@AndrewHows de ce que j'ai vu dans la vraie vie, lorsque les gens changent les internes de la classe, ils changent également l'interface et font une refactorisation de tout le code
Eildosa
2

Une autre utilisation (dans les langages qui prennent en charge les propriétés) est que les setters et les getters peuvent impliquer qu'une opération n'est pas triviale. En règle générale, vous voulez éviter de faire quoi que ce soit de coûteux en calcul dans une propriété.

Jason Baker
la source
Je ne m'attendrais jamais à ce qu'un getter ou un setter soit une opération coûteuse. Dans ce cas , mieux utiliser une usine: utiliser tous les setters dont vous avez besoin, puis invoquer le cher executeou buildméthode.
Hubert Grzeskowiak
2

Un avantage relativement moderne des getters / setters est qu'il facilite la navigation dans les éditeurs de code balisés (indexés). Par exemple, si vous voulez voir qui définit un membre, vous pouvez ouvrir la hiérarchie des appels du passeur.

En revanche, si le membre est public, les outils ne permettent pas de filtrer l'accès en lecture / écriture au membre. Vous devez donc vous frayer un chemin dans toutes les utilisations du membre.

Rakesh Singh
la source
vous pouvez faire un clic droit> trouver une utilisation sur un membre exactement comme sur un getter / setter
Eildosa
2

Dans un langage orienté objet, les méthodes et leurs modificateurs d'accès déclarent l'interface de cet objet. Entre le constructeur et les méthodes d'accesseur et de mutateur, il est possible pour le développeur de contrôler l'accès à l'état interne d'un objet. Si les variables sont simplement déclarées publiques, il n'y a aucun moyen de réglementer cet accès. Et lorsque nous utilisons des setters, nous pouvons restreindre l'utilisateur pour l'entrée dont nous avons besoin. Signifier que le flux pour cette variable très passera par un canal approprié et le canal est prédéfini par nous. Il est donc plus sûr d'utiliser des setters.

Antz
la source
1

De plus, c'est pour "pérenniser" votre classe. En particulier, le passage d'un champ à une propriété est une rupture ABI, donc si vous décidez plus tard que vous avez besoin de plus de logique que simplement "définir / obtenir le champ", alors vous devez rompre ABI, ce qui crée bien sûr des problèmes pour quoi que ce soit sinon déjà compilé contre votre classe.

Pete
la source
2
Je suppose que changer le comportement du getter ou du setter n'est pas un changement de rupture alors. </sarcasm>
R. Martinho Fernandes
@ R.MartinhoFernandes ne doit pas toujours l'être. TBYS
Phil
1

Je voudrais juste lancer l'idée d'annotation: @getter et @setter. Avec @getter, vous devriez pouvoir obj = class.field mais pas class.field = obj. Avec @setter, vice versa. Avec @getter et @setter, vous devriez pouvoir faire les deux. Cela préserverait l'encapsulation et réduirait le temps en n'appelant pas de méthodes triviales à l'exécution.

fastcodejava
la source
1
Il serait implémenté lors de l'exécution avec des "méthodes triviales". En fait, probablement non trivial.
Phil
Aujourd'hui, cela peut être fait avec un préprocesseur d'annotation.
Thorbjørn Ravn Andersen