web-dev-qa-db-fra.com

Pourquoi utiliser des getters et des setters / accessors?

Quel est l’avantage d’utiliser des getters et des setters - qui ne font qu’obtenir et définir - au lieu d’utiliser simplement des champs publics pour ces variables?

Si les getters et les setters font autre chose que le simple get/set, je peux résoudre celui-ci très rapidement, mais je ne suis pas sûr à 100% de savoir 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.

1440
Dean J

Il y a en fait beaucoup de bonnes raisons d'envisager d'utiliser des accesseurs plutôt que d'exposer directement les champs d'une classe - au-delà du simple argument de l'encapsulation et rendre les changements futurs plus faciles.

Voici certaines des 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) ultérieurement.
  • Masquer 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 - permet à l'interface publique de rester constante pendant que la mise en œuvre change sans affecter les consommateurs existants.
  • Contrôle de la sémantique de la propriété sur la durée de vie et la gestion de la mémoire (élimination) - 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 le moment où une propriété est modifiée au moment de l'exécution - le débogage du moment et du lieu où une propriété a été modifiée pour une valeur particulière peut être assez difficile sans cela dans certaines langues.
  • Une interopérabilité améliorée avec les bibliothèques conçues pour lutter contre les getter/setters de propriétés - Mocking, Serialization et WPF me viennent à l’esprit.
  • Permettre aux héritiers de changer la sémantique du comportement et de l'exposition de la propriété en redéfinissant les méthodes getter/setter.
  • Permettre au getter/setter d'être transmis sous forme d'expressions lambda plutôt que de valeurs.
  • Les Getters et les setters peuvent autoriser différents niveaux d'accès. Par exemple, get peut être public, mais l'ensemble peut être protégé.
903
LBushkin

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

440
ChssPly76

Un champ public n’est pas pire qu’un couple getter/setter qui ne fait que retourner le champ et l’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 n'est pas. Il existe cette affirmation selon laquelle vous pouvez modifier la mise en œuvre et que vos clients ne doivent pas être recompilés. Soi-disant, les configurateurs vous permettent d’ajouter des fonctionnalités telles que la validation par la suite et vos clients n’ont même pas besoin de le savoir. Cependant, ajouter une validation à un setter est un changement de ses conditions préalables, ne 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, vous devriez avoir envie de faire, de ne pas éviter, de changer tous les fichiers de la base de code. Si vous l'évitez, vous supposez que tout le code suppose 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 tout le contraire de l'encapsulation Si ce champ ne pouvait pas vraiment être défini depuis le début, pourquoi la validation n'y était-elle pas au départ?

Ce même argument s'applique à d'autres avantages supposés de ces paires getter/setter directes: si vous décidez plus tard de changer la valeur définie, vous rompez le contrat. Si vous remplacez 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 rompez le contrat de la classe de base. C'est une violation du principe de la 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 que ce soit, pas de contrat . Est-ce vraiment une conception orientée objet? Si toute la classe a ces accesseurs et ces configurateurs, il ne s'agit que d'un détenteur de données stupide, et les détenteurs de données stupides doivent 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 doivent fournir des opérations significatives, pas seulement des opérations déjà fournies par les champs. 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 accesseurs et des setters, mais si ces raisons n'existent pas, faire des paires getter/setter au nom de faux dieux de l'encapsulation n'est pas une bonne chose. Les raisons valables pour créer des getters ou des setters incluent les choses souvent mentionnées en tant que modifications potentielles que vous pouvez apporter ultérieurement, telles que la validation ou différentes représentations internes. Ou peut-être que la valeur devrait être lisible par les clients mais pas accessible en écriture (par exemple, lire la taille d'un dictionnaire), de sorte qu'un simple getter est un bon choix. Mais ces raisons devraient exister lorsque vous faites votre choix, et pas seulement en tant que chose potentielle que vous voudrez peut-être plus tard. Ceci est une instance de YAGNI ( Vous n'en aurez pas besoin ).

340

Beaucoup de gens parlent des avantages des Getters et des Setters, mais je veux me faire l'avocat du diable. En ce moment, je débogue un très gros programme dans lequel les programmeurs ont décidé de tout créer. Cela peut sembler gentil, mais c’est un cauchemar d’ingénierie inverse.

Supposons que vous parcouriez des centaines de lignes de code et que vous rencontriez ceci:

person.name = "Joe";

C'est un morceau de code tout simplement magnifique jusqu'à ce que vous réalisiez que c'est un configurateur. Maintenant, vous suivez ce paramètre et constatez qu'il définit également person.firstName, person.lastName, person.isHuman, person.hasReallyCommonFirstName et appelle person.update (), ce 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 au premier abord est une propriété importante de bonne lisibilité que les accesseurs 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.

84
Kai

Il y a plusieurs raisons. Mon préféré est lorsque vous devez modifier le comportement ou régler ce que vous pouvez définir sur une variable. Par exemple, supposons que vous utilisiez 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, dans votre code, vous utilisiez le champ public et que vous réalisiez que vous aviez besoin de l'exigence ci-dessus? Amusez-vous à traquer chaque utilisation du domaine public au lieu de modifier simplement votre configurateur.

Mes 2 centimes :)

51
Peter D

Dans un monde purement orienté objet, les getters et les setters constituent un anti-motif terrible . Lisez cet article: Getters/Setters. Evil. Period . En résumé, ils encouragent les programmeurs à considérer les objets comme des structures de données, et ce type de réflexion est purement procédural (comme en COBOL ou en C). Dans un langage orienté objet, il n'y a pas de structure de données, mais uniquement des objets qui exposent le comportement (pas les attributs/propriétés!)

Vous trouverez plus d'informations à leur sujet dans la section 3.5 de Elegant Objects (mon livre sur la programmation orientée objet).

48
yegor256

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

Par exemple, si foo était public, je pourrais facilement le définir sur null, puis quelqu'un d'autre pourrait essayer d'appeler une méthode sur l'objet. Mais ce n'est plus là! Avec une méthode setFoo, je pouvais m'assurer que foo n'était jamais défini sur 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 définie dans le constructeur et ensuite utilisée par des méthodes, mais jamais supposé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és.

37
Thomas Owens

Cela dépend de votre langue. Vous avez identifié cette balise comme étant "orientée objet" plutôt que "Java". J'aimerais donc souligner que la réponse de ChssPly76 dépend de la langue. En Python, par exemple, il n’ya aucune raison d’utiliser les getters et les 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 d'attribut 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
jcdyer

Bien, je veux juste ajouter que même si parfois ils 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 - ARRETEZ DE SURUTILISER LES ACCESSOIRES, car nous dépendons parfois beaucoup d’eux quand ce n’est pas vraiment nécessaire et cela fait presque la même chose que si nous mettions les variables en public.

24
Jorge Aguilar

Merci, cela a vraiment clarifié ma pensée. Maintenant, voici (presque) 10 (presque) bonnes raisons de ne pas utiliser de getters et de setters:

  1. Lorsque vous réalisez que vous devez faire plus que définir et obtenir la valeur, vous pouvez simplement rendre le champ privé, ce qui vous indiquera instantanément où vous y avez directement accédé.
  2. Toute validation que vous effectuez dans ce répertoire ne peut être que sans contexte, ce qui est rarement le cas en pratique.
  3. Vous pouvez modifier la valeur définie - il s'agit d'un cauchemar absolu lorsque l'appelant vous transmet une valeur qu'il [horreur du choc] souhaite que vous stockiez tel quel.
  4. Vous pouvez masquer la représentation interne - fantastique, afin de vous assurer que toutes ces opérations sont symétriques, non?
  5. Vous avez isolé votre interface publique des modifications apportées sous les feuilles. Si vous conceviez une interface et ne saviez pas si l'accès direct à quelque chose était correct, vous auriez dû continuer à concevoir.
  6. Certaines bibliothèques s'attendent à cela, mais pas beaucoup - la réflexion, la sérialisation, les objets fictifs fonctionnent parfaitement avec les champs publics.
  7. En héritant de cette classe, vous pouvez remplacer la fonctionnalité par défaut - autrement dit, 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) ...

22
user595447

Je sais que c'est un peu tard, mais je pense que certaines personnes s'intéressent à la performance.

J'ai fait un petit test de performance. J'ai écrit une classe "NumberHolder" qui, ainsi, contient un Integer. Vous pouvez soit 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 de fois, de deux manières. 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 la voie directe, le temps 2 est le getter)

Vous voyez, le getter est (presque) toujours un peu plus rapide. Ensuite, j'ai 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 temps sont presque les mêmes. Voici 100 000 (0,1 million) de 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

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

22
kangalioo

N'utilisez pas de getters setters sauf si cela est nécessaire pour votre livraison actuelle I.e. Ne pensez pas trop à ce qui se passera dans le futur, 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 pour leur 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 les réglages de getters, uniquement avec des modificateurs d'accès et des méthodes pour valider n effectuer une logique biz. Si vous aviez absolument besoin du. Utilisez n'importe quoi.

16
Mohamed

Cela 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 voulez pas le récupérer sauf si vous en avez 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, vous pouvez aller le récupérer lors du premier appel au getter.

Un projet de classe de page de base m'a chargé de charger des données à partir de plusieurs appels de service Web, mais ces données n'étaient pas toujours utilisées dans toutes les pages enfants. Les services Web, pour tous les avantages, définissent de nouvelles définitions du terme "lent" dans Pioneer. Vous ne souhaitez donc pas émettre d'appel de service Web si vous n'en avez pas besoin.

Je suis passé des champs publics aux getters, et maintenant les getters vérifient le cache et, s'il n'y en a pas, appelez le service Web. Donc, avec un peu d'emballage, beaucoup d'appels de service Web ont été évités.

Donc, le getter m'évite d'essayer de déterminer, sur chaque page enfant, ce dont j'ai besoin. Si j'en ai besoin, j'appelle le getter et il me le trouve si je ne l'ai pas déjà.

    protected YourType _yourName = null;
    public YourType YourName{
      get
      {
        if (_yourName == null)
        {
          _yourName = new YourType();
          return _yourName;
        }
      }
    }
15
quillbreaker

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

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

En d'autres termes, le seul moyen de spécifier un champ dans une interface consiste à fournir une méthode pour écrire une nouvelle valeur et une méthode pour lire la valeur actuelle.

Ces méthodes sont le getter et le setter infâme ....

Nous utilisons des getters et des setters:

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

Les méthodes d'accès et de définition sont des interfaces publiques permettant d'accéder aux membres de classes privées.


Mantra d'encapsulation

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

Méthodes Getter: Nous pouvons avoir accès aux variables privées.

Méthodes Setter: 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

  • mieux;
  • 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

Representation of "Person" class

Supposons que nous ayons besoin de stocker les détails de cette Person. Cette Person a les champs name, age et sex. Cela implique la création de méthodes pour name, age et sex. Maintenant, si nous devons créer une autre personne, il devient nécessaire de créer à nouveau les méthodes pour name, age, sex.

Au lieu de cela, nous pouvons créer un bean class(Person) avec les méthodes getter et setter. Ainsi, demain, nous pouvons simplement créer des objets de ce haricot 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 de haricots, ce qui est bien meilleur.

14
Devrath

Un aspect qui me manquait jusque là dans les réponses, la spécification d'accès:

  • pour les membres, vous n’avez qu’une seule spécification d’accès pour le réglage et l’obtention
  • pour les setters et les getters, vous pouvez affiner et définir séparément
12
jdehaan

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 de champs en propriétés (C #), il est plus facile de modifier les méthodes get/set. Par exemple, l'ajout d'une logique de validation à une méthode setFoo ne nécessite pas de modification de l'interface publique d'une classe.

Dans les langages qui supportent des propriétés "réelles" (Python, Ruby, peut-être Smalltalk?), Il n'y a aucun intérêt à obtenir/définir des méthodes.

10
John Millikin

EDIT: J'ai répondu à cette question parce que de nombreuses personnes apprennent à programmer, et la plupart des réponses sont très techniques, mais elles sont moins faciles à comprendre si vous êtes un débutant. Nous étions tous des débutants, alors j'ai pensé essayer une réponse plus conviviale pour les débutants.

Les deux principaux sont le polymorphisme et la validation. Même s'il ne s'agit que d'une structure de données stupide.

Disons que nous avons cette classe simple:

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

Une classe très simple qui contient combien de liquide il contient et quelle est sa capacité (en millilitres).

Qu'est-ce qui se passe quand je 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 vérification de la santé mentale. Et pire, si je ne spécifiais jamais la capacité maximale? Oh mon Dieu, nous avons un problème.

Mais il y a aussi un autre problème. Et si les bouteilles n'étaient qu'un type de conteneur? 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 des bouteilles, des jerrycans et toutes sortes de choses fonctionneraient simplement de manière interchangeable. Ne serait-ce pas mieux? Puisque les interfaces exigent des méthodes, c'est aussi une bonne chose.

Nous nous retrouverions avec quelque chose comme:

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

Génial! Et maintenant nous changeons Bottle pour 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 laisserai au lecteur la définition de l’exception BottleOverflowException.

Remarquez maintenant à quel point cela est plus robuste. Nous pouvons maintenant traiter n'importe quel type de conteneur dans notre code en acceptant LiquidContainer au lieu de Bottle. Et comment ces bouteilles traitent ce genre de choses peuvent toutes différer. Vous pouvez avoir des bouteilles qui écrivent leur état sur le disque lorsqu’elles changent, ou des bouteilles qui économisent sur des bases de données SQL ou GNU sait quoi d’autre.

Et tous ceux-ci peuvent avoir différentes façons de gérer différentes whoopsies. La bouteille se contente de vérifier et si elle déborde, elle renvoie une exception RuntimeException. Mais cela pourrait être la mauvaise chose à faire. (Il existe une discussion utile sur la gestion des erreurs, mais je garde cela très simple, ici, à dessein. Les personnes qui commenteront vont probablement souligner les failles de cette approche simpliste.;))

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

Veuillez noter également que vous ne pouvez pas modifier la capacité d'une bouteille. C'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 pourriez la vider, y ajouter de nouvelles choses, etc. Vous ne pouvez pas limiter l'accès au toucher des 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 aux méthodes normales partout ailleurs. Au lieu d'avoir une syntaxe spécifique étrange pour les DTO et autres, vous avez la même chose partout.

9
Haakon Løtveit

L'un des principes de base de OO design: Encapsulation!

Cela vous offre de nombreux avantages, dont l’un est que vous pouvez modifier l’implémentation du getter/setter en arrière-plan, mais tout consommateur de cette valeur continuera à fonctionner tant que le type de données reste le même.

6
Justin Niessner

Vous devriez utiliser des getters et des setters quand:

  • 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 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 les setters pour ce cas d'utilisation.

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


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

En d'autres termes: si vous pensez que les consommateurs de votre classe ne devraient même pas savoir que vous avez un attribut spam, et encore moins pouvoir le changer à volonté, donnez-leur une méthode set_spam la dernière chose que vous voulez faire.

* Même pour cette classe simple, vous ne souhaiterez peut-être pas nécessairement autoriser la définition des valeurs x et y. Si c'est vraiment une classe, ne devrait-il pas y avoir de méthodes comme translate, rotate, etc.? Si c'est seulement une classe parce que votre langue n'a pas d'enregistrements/structs/tuples nommés, alors ce n'est pas vraiment une question de OO…


Mais personne ne fait jamais de design général OO. Ils conçoivent et implémentent un langage spécifique. Et dans certaines langues, les Getters et les setters sont loin d’être inutiles.

Si votre langue n'a pas de propriétés, le seul moyen de représenter quelque chose qui est conceptuellement un attribut, mais qui est en fait calculé, ou validé, etc., consiste à accéder à des getters et des 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.

En ce qui concerne le "et si je veux changer mon implémentation plus tard?" question (qui est répétée plusieurs fois dans des formulations différentes à la fois dans la question du PO et dans la réponse acceptée): s'il s'agit vraiment d'un changement de mise en œuvre 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 supporte pas. Donc, c'est vraiment le même cas encore.

En outre, 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 expérimenté en C #, autre que vous, aura du mal à le lire, ce qui est grave. 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 se trouvent à l'opposé de la façon dont les getters sont idiomatiques, ont peut-être deux des cultures les plus fortes .

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

5
abarnert

Du point de vue de la conception de l'orientation des objets, les deux alternatives peuvent être dommageables pour la maintenance du code en affaiblissant l'encapsulation des classes. Pour une discussion, vous pouvez examiner cet excellent article: http://typicalprogrammer.com/?p=2

4
andrers52

Le code évolue . private est idéal lorsque vous avez besoin de la protection des données . Finalement, toutes les classes devraient être en quelque sorte des "mini-programmes" ayant une interface bien définie que vous ne pouvez pas visser avec les éléments internes de .

Cela dit, le développement logiciel ne consiste pas à définir cette version finale de la classe comme si vous appuyiez sur une statue en fonte du premier coup. Pendant que vous travaillez avec cela, le code ressemble plus à de l'argile. Il évolue à mesure que vous le développez et que vous en apprenez plus sur le problème que vous résolvez. Pendant le développement, les classes peuvent interagir les unes avec les autres qu'elles ne le devraient (dépendance que vous comptez exclure), se fusionner ou se séparer. Donc, je pense que le débat se résume à des personnes qui ne veulent pas écrire religieusement

int getVar() const { return var ; }

Donc vous avez:

doSomething( obj->getVar() ) ;

Au lieu de

doSomething( obj->var ) ;

getVar() n'est pas seulement visuellement bruyant, cela donne à cette illusion que gettingVar() est en quelque sorte un processus plus complexe qu'il ne l'est réellement. Votre conception (en tant qu’écrivain de classe) du caractère sacré de var est particulièrement déconcertante pour un utilisateur de votre classe si elle est dotée d’un passeur fixe - il semble alors que vous ouvrez ces portes pour "protéger" quelque chose vous insistez sur la valeur (la sainteté de var) mais vous concédez quand même que la protection de var ne vaut pas grand-chose de par la possibilité pour quiconque de simplement entrer et setvar à la valeur qu'ils veulent, sans même que vous jetiez un coup d'œil à ce qu'ils font.

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/ne pas faire avoir le temps ou l'expérience de planifier un jeu d'interface complexe de style cascade):

1) Commencez avec tous les membres publics pour les objets de base avec données et comportement. C'est pourquoi, dans tout mon code "exemple" C++, vous remarquerez que j'utilise struct au lieu de class partout.

2) Lorsque le comportement interne d'un objet pour un membre de données devient suffisamment complexe (par exemple, il aime conserver un std::list interne dans un ordre quelconque), des fonctions de type accesseur sont écrites. Comme je programme moi-même, je ne mets pas toujours le membre private immédiatement, mais quelque part dans l'évolution de la classe, le membre sera "promu" en protected ou private.

3) Les classes qui sont complètement étoffées et qui ont des règles strictes concernant leurs internes (c'est-à-dire , elles savent exactement ce qu'elles font et vous ne devez pas "baiser" "(terme technique) avec ses composants internes) se voient attribuer la désignation class, membres privés par défaut, et seuls quelques membres sélectionnés sont autorisés à être public.

Je trouve que cette approche me permet d'éviter de m'asseoir là et d'écrire religieusement les getter/setters quand beaucoup de données membres sont migrées, transférées, etc. au début de l'évolution d'une classe.

3
bobobobo

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 d'employés:

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 directement accessibles à l'utilisateur, contrairement à un attribut public.

3
Pritam Banerjee

Les méthodes d'accès et de définition sont des méthodes d'accès, ce qui signifie qu'elles constituent généralement une interface publique permettant de modifier les membres de la classe privée. Vous utilisez les 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 extérieures à la classe, même si vous les définissez dans la classe en tant que méthodes. Les propriétés en dehors de la classe peuvent avoir un nom différent du nom de la propriété dans la classe.

L'utilisation de 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, veillez à ne pas en abuser car, entre autres, elles peuvent compliquer la maintenance du code dans certaines situations. En outre, ils fournissent un accès à l'implémentation de votre classe, à l'instar des membres du public. OOP cette pratique décourage l'accès direct aux propriétés d'une classe.

Lorsque vous écrivez des classes, vous êtes toujours encouragé à rendre privées autant que possible vos variables d'instance et à ajouter les méthodes getter et setter en conséquence. En effet, il arrive parfois que vous ne souhaitiez pas laisser les utilisateurs modifier certaines variables dans vos classes. Par exemple, si vous disposez d'une méthode statique privée qui surveille le nombre d'instances créées pour une classe spécifique, vous ne souhaitez pas qu'un utilisateur modifie ce compteur à l'aide de code. Seule la déclaration du constructeur doit incrémenter cette variable à chaque appel. Dans ce cas, vous pouvez créer une variable d'instance privée et autoriser une méthode getter uniquement pour la variable counter, ce qui signifie que les utilisateurs peuvent extraire la valeur actuelle uniquement à l'aide de la méthode getter et ne peuvent pas définir de nouvelles valeurs. en utilisant la méthode setter. Créer un getter sans setter est un moyen simple de rendre certaines variables de votre classe en lecture seule.

3
Sumit Singh

Il y a une bonne raison d'envisager d'utiliser des accesseurs s'il n'y a pas d'héritage de propriété. Voir 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());
    }
}

Sortie:

0
0
4
3
GeZo

Dans un langage orienté objet, les méthodes et leurs modificateurs d'accès déclarent l'interface pour cet objet. Entre le constructeur et les méthodes accesseur et mutateur, le développeur peut contrôler l'accès à l'état interne d'un objet. Si les variables sont simplement déclarées publiques, il n’ya aucun moyen de réglementer cet accès. Et lorsque nous utilisons des réglages, nous pouvons limiter l'utilisateur à la saisie dont nous avons besoin. Cela signifie que le flux pour cette variable passera par un canal approprié et que le canal est prédéfini par nous. Donc, il est plus sûr d'utiliser des setters.

2
Antz

Une autre utilisation (dans les langages qui supportent les propriétés) est que les setters et les getters peuvent impliquer qu'une opération est non triviale. En règle générale, vous souhaitez éviter toute activité coûteuse en termes de calcul dans une propriété.

2
Jason Baker

Les getters/setters présentent un avantage relativement moderne: ils facilitent la navigation dans le code des é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 configurateur.

En revanche, si le membre est public, les outils ne permettent pas de filtrer les accès en lecture/écriture au membre. Vous devez donc vous débrouiller avec toutes les utilisations du membre.

2
Rakesh Singh

Getters et setters venant de masquage de données. Moyens de masquage des données Nous sommes masquons les données des personnes extérieures ou une personne/chose extérieure ne peut pas accéder à nos données. Il s'agit d'une fonctionnalité utile de la programmation orientée objet.

À titre d'exemple:

Si vous créez une variable publique, vous pouvez accéder à cette variable et modifier la valeur n'importe où (n'importe quelle classe). Mais si vous créez en privé, cette variable ne peut voir/accéder dans aucune classe sauf la classe déclarée.

public et private sont des modificateurs d'accès.

Alors, comment pouvons-nous accéder à cette variable à l'extérieur:

C'est l'endroit getters et setters venant de. Vous pouvez déclarer la variable privée, puis implémenter le getter et le setter pour cette variable.

Exemple (Java):

private String name;

public String getName(){
   return this.name;
}

public void setName(String name){
   this.name= name;
}

avantage:

Lorsque quelqu'un veut accéder ou modifier/définir la valeur sur la variable balance, il/elle doit disposer d'une permission.

//assume we have person1 object
//to give permission to check balance
person1.getName()

//to give permission to set balance
person1.setName()

Vous pouvez également définir une valeur dans le constructeur, mais lorsque vous souhaitez mettre à jour/modifier une valeur ultérieurement, vous devez implémenter la méthode setter.

1
Blasanka

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

1
Pete

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 au moment de l'exécution.

1
fastcodejava

Je peux penser à une raison pour laquelle vous ne voulez pas que tout soit public.

Par exemple, les variables que vous n’avez jamais eu l’intention d’utiliser en dehors de la classe sont accessibles, même directement via l’accès aux variables en chaîne (par exemple, object.item.Origin.x).

En ayant essentiellement tout ce qui est privé, et uniquement les éléments que vous souhaitez étendre et que vous pouvez éventuellement appeler des sous-classes comme étant protégés, et ayant généralement uniquement des objets finaux statiques comme publics, vous pouvez alors contrôler ce que les autres programmeurs et programmes peuvent utiliser dans l'API et ce que cela permet. peut accéder et ce qu’il ne peut pas en utilisant des paramètres et des accesseurs pour accéder aux éléments que vous voulez que le programme, ou même d’autres programmeurs qui utilisent simplement votre code, peuvent modifier dans votre programme.

1
MongooseLover

Les getters/setters présentent un avantage relativement moderne: ils facilitent la navigation dans le code des é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 configurateur.

En revanche, si le membre est public, les outils ne permettent pas de filtrer les accès en lecture/écriture au membre. Vous devez donc vous débrouiller avec toutes les utilisations du membre.

0
jfritz42

Bien que cela ne soit pas courant pour les getter et les setter, l'utilisation de ces méthodes peut également être utilisée dans les utilisations de modèles AOP/proxy. Par exemple, pour la variable d'audit, vous pouvez utiliser AOP pour auditer la mise à jour de toute valeur. Sans getter/setter, ce n'est pas possible, sauf de changer le code partout. Personnellement, je n'ai jamais utilisé AOP pour cela, mais cela montre un avantage supplémentaire de l'utilisation de getter/setter.

Je laisserai le code parler pour lui-même:

Mesh mesh = new Mesh();
BoundingVolume vol = new BoundingVolume();
mesh.boundingVolume = vol;
vol.mesh = mesh;
vol.compute(); 

Aimez-vous? Voici avec les setters:

Mesh mesh = new Mesh();
BoundingVolume vol = new BoundingVolume();
mesh.setBoundingVolume(vol);
0
luke1985

Si vous voulez une variable en lecture seule mais ne voulez pas que le client doive changer la façon dont il y accède, essayez cette classe basée sur un modèle:

template<typename MemberOfWhichClass, typename primative>                                       
class ReadOnly {
    friend MemberOfWhichClass;
public:
    template<typename number> inline bool   operator==(const number& y) const { return x == y; } 
    template<typename number> inline number operator+ (const number& y) const { return x + y; } 
    template<typename number> inline number operator- (const number& y) const { return x - y; } 
    template<typename number> inline number operator* (const number& y) const { return x * y; }  
    template<typename number> inline number operator/ (const number& y) const { return x / y; } 
    template<typename number> inline number operator<<(const number& y) const { return x << y; }
    template<typename number> inline number operator^(const number& y) const  { return x^y; }
    template<typename number> inline number operator~() const                 { return ~x; }
    template<typename number> inline operator number() const                  { return x; }
protected:
    template<typename number> inline number operator= (const number& y) { return x = y; }       
    template<typename number> inline number operator+=(const number& y) { return x += y; }      
    template<typename number> inline number operator-=(const number& y) { return x -= y; }      
    template<typename number> inline number operator*=(const number& y) { return x *= y; }      
    template<typename number> inline number operator/=(const number& y) { return x /= y; }      
    primative x;                                                                                
};      

Exemple d'utilisation:

class Foo {
public:
    ReadOnly<Foo, int> cantChangeMe;
};

N'oubliez pas que vous aurez également besoin d'ajouter des opérateurs au niveau du bit et unaires! Ceci est juste pour vous aider à démarrer

0
Jonathan

Je voulais poster un exemple concret que je viens de terminer:

background - Je mets en veille des outils pour générer les mappages de ma base de données, une base de données que je modifie au fur et à mesure que je développe. Je modifie le schéma de la base de données, affichez les modifications, puis exécutez les outils de veille prolongée pour générer le code Java. Tout va bien jusqu'à ce que je veuille ajouter des méthodes à ces entités mappées. Si je modifie les fichiers générés, ils seront écrasés chaque fois que je modifierai la base de données. Donc j'étends les classes générées comme ceci:

package com.foo.entities.custom
class User extends com.foo.entities.User{
     public Integer getSomething(){
         return super.getSomething();             
     }
     public void setSomething(Integer something){
         something+=1;
         super.setSomething(something); 
     }
}

Ce que j'ai fait ci-dessus est de remplacer les méthodes existantes sur la super classe par ma nouvelle fonctionnalité (quelque chose + 1) sans jamais toucher à la classe de base. Même scénario si vous avez écrit un cours il y a un an et souhaitez passer à la version 2 sans modifier vos cours de base (test cauchemar). J'espère que ça t'as aidé.

0
ebt