Question Pourquoi utiliser des getters et des setters / accesseurs? [fermé]


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

Si les getters et setters font plus que simplement le get / set simple, je peux comprendre celui-ci très rapidement, mais je ne suis pas 100% clair sur 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 standard.


1303
2017-10-14 18:20


origine


Réponses:


Il y a en fait beaucoup de bonnes raisons 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 les raisons pour lesquelles je suis au courant:

  • Encapsulation du comportement associé à l'obtention ou à la définition de la propriété - ceci permet d'ajouter plus facilement des fonctionnalités supplémentaires (comme la validation) plus tard.
  • Masquage de la représentation interne de la propriété tout en exposant une propriété à l'aide d'une représentation alternative.
  • Isoler votre interface publique du changement - permettre à l'interface publique de rester constante pendant que l'implémentation change sans affecter les consommateurs existants.
  • Contrôler la durée de vie et la gestion de la mémoire (élimination) de la sémantique 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 lorsqu'une propriété change au moment de l'exécution - déboguer quand et où une propriété a changé en une valeur particulière peut être assez difficile sans cela dans certaines langues.
  • Interopérabilité améliorée 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 de la propriété et de l'exposer en remplaçant les méthodes getter / setter.
  • Permettre au getter / setter d'être transmis comme des expressions lambda plutôt que comme des valeurs.
  • Les getters et setters peuvent autoriser différents niveaux d'accès - par exemple, get peut être public, mais l'ensemble pourrait être protégé.

794
2017-10-14 18:47



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


389
2017-10-14 18:23



Un champ public n'est pas pire qu'une paire getter / setter qui ne fait rien sauf retourner le champ et lui assigner. 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 être liée à d'autres facteurs, tels que la maintenabilité ou la lisibilité.

Un avantage souvent mentionné des paires getter / setter ne l'est pas. Il y a cette affirmation que vous pouvez changer l'implémentation et que vos clients n'ont pas besoin d'être recompilés. Supposément, les setters vous permettent d'ajouter des fonctionnalités comme la validation plus tard et vos clients n'ont même pas besoin de savoir à ce sujet. Cependant, ajouter une validation à un setter est un changement à ses conditions préalables, une violation du contrat précédent, qui était, tout simplement, "vous pouvez mettre quelque chose ici, et vous pouvez obtenir la même chose plus tard du getter".

Donc, maintenant que vous avez rompu le contrat, changer tous les fichiers dans le code est quelque chose que vous devriez faire, ne 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, l'interface permettait aux clients de placer l'objet dans des états non valides. C'est exactement le contraire de l'encapsulation Si ce champ ne pouvait pas vraiment être défini depuis le début, pourquoi la validation n'a-t-elle pas été faite dès le départ?

Ce même argument s'applique aux autres avantages supposés de ces paires de getter / setter pass-through: si vous décidez par la suite de changer la valeur définie, vous rompez le contrat. Si vous substituez la fonctionnalité par défaut dans une classe dérivée, au-delà de quelques modifications inoffensives (comme la journalisation ou tout autre comportement non observable), vous brisez 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 d'OO.

Si une classe a ces getters et setters stupides pour chaque champ, alors c'est une classe qui n'a aucun invariant, pas de contrat. Est-ce vraiment une conception orientée objet? Si tout le monde a ces getters et setters, c'est juste un support de données stupide, et les détenteurs de données stupides devraient ressembler à des supports 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 doivent 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 setters, mais si ces raisons n'existent pas, faire des paires getter / setter au nom de dieux d'encapsulation fausse n'est pas une bonne chose. Les raisons valables pour faire des getters ou setters incluent les choses souvent mentionnées comme les changements potentiels que vous pouvez faire plus tard, comme la validation ou les différentes représentations internes. Ou peut-être que la valeur devrait être lisible par les clients mais pas inscriptible (par exemple, en lisant la taille d'un dictionnaire), donc un getter simple est un bon choix. Mais ces raisons devraient être là quand vous faites le choix, et pas seulement comme une chose potentielle que vous pourriez vouloir plus tard. Ceci est une instance de YAGNI (Vous n'en aurez pas besoin).


307
2017-08-24 10:55



Beaucoup de gens parlent des avantages des getters et des setters, mais je veux jouer le rôle de défenseur du diable. En ce moment je débogue un très gros programme où les programmeurs ont décidé de faire tout ce que les getters et setters. Cela peut sembler bien, mais c'est un cauchemar d'ingénierie inverse.

Dites que vous regardez à travers des centaines de lignes de code et vous tombez sur ceci:

person.name = "Joe";

C'est un morceau de code magnifiquement simple jusqu'à ce que vous réalisiez qu'il est un setter. Maintenant, vous suivez ce setter et trouvez qu'il définit aussi 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 d'une bonne lisibilité que les getters et les setters ont tendance à casser. C'est pourquoi j'essaie de les éviter quand je peux, et de minimiser ce qu'ils font quand je les utilise.


75
2017-10-14 21:00



Il y a plusieurs raisons. Mon préféré est quand vous avez besoin de changer le comportement ou de régler ce que vous pouvez définir sur une variable. Par exemple, disons que vous avez une méthode setSpeed ​​(int speed). Mais vous voulez que vous ne pouvez définir 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, 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 à chasser chaque utilisation du champ public au lieu de simplement modifier votre setter.

Mes 2 cents :)


43
2017-10-14 18:27



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

Vous pouvez en trouver plus à ce sujet dans la section 3.5 de Objets élégants (mon livre sur la programmation orientée objet).


43
2017-09-23 16:39



Un avantage des accesseurs et des mutateurs est que vous pouvez effectuer une validation.

Par exemple, si foo était public, je pourrais facilement le mettre à null et alors quelqu'un d'autre pourrait essayer d'appeler une méthode sur l'objet. Mais ce n'est plus là! Avec un setFoo méthode, je pourrais m'assurer que foo n'a jamais été mis à 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 (elle est peut-être définie dans le constructeur puis utilisée par les méthodes, mais jamais supposée être modifiée), elle ne sera jamais vue. Mais si vous pouvez permettre à d'autres classes de le voir ou de le modifier, vous pouvez fournir l'accesseur et / ou le mutateur approprié.


36
2017-10-14 18:25



Cela dépend de votre langue. Vous avez étiqueté ce "orienté objet" plutôt que "Java", donc je tiens à souligner que la réponse de ChssPly76 dépend de la langue. En Python, par exemple, il n'y a aucune raison d'utiliser des getters et setters. Si vous avez besoin de modifier le comportement, vous pouvez utiliser une propriété qui enveloppe un accesseur et un accesseur 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)

28
2017-10-14 18:32