Quand les Getters et les Setters sont-ils justifiés?

175

Les Getters et les setters sont souvent critiqués comme étant des OO non appropriés. D'autre part, la plupart du code OO que j'ai vu possède de nombreux accesseurs et régleurs.

Quand les getters et les setters sont-ils justifiés? Essayez-vous d'éviter de les utiliser? Sont-ils surutilisés en général?

Si votre langue préférée a des propriétés (la mienne), de telles choses sont également considérées comme des accesseurs pour la question. Ils sont la même chose du point de vue de la méthodologie OO. Ils ont juste une syntaxe plus agréable.

Sources de critiques Getter / Setter (certaines tirées de commentaires pour leur donner une meilleure visibilité):

Pour énoncer la critique simplement: Les Getters et Setters vous permettent de manipuler l’état interne des objets de l’extérieur. Cela viole l’encapsulation. Seul l'objet lui-même doit se soucier de son état interne.

Et un exemple de version procédurale du code.

struct Fridge
{
    int cheese;
}

void go_shopping(Fridge fridge)
{
     fridge.cheese += 5;
}

Version mutatrice du code:

class Fridge
{
     int cheese;

     void set_cheese(int _cheese) { cheese = _cheese; }
     int get_cheese() { return cheese; }
 }

void go_shopping(Fridge fridge)
{
     fridge.set_cheese(fridge.get_cheese() + 5);        
}

Les getters et les setters ont rendu le code beaucoup plus compliqué sans permettre une encapsulation correcte. Parce que l'état interne est accessible à d'autres objets, nous n'avons pas beaucoup à gagner en ajoutant ces accesseurs et ces setters.

La question a déjà été discutée sur Stack Overflow:

Winston Ewert
la source
21
Getters and setters are often criticized as being not proper OO- Citation, s'il te plaît.
Robert Harvey
45
@Job, pourquoi parce qu'il a du code? C'est une bonne question et ça pue la guerre sainte si on le prend au sérieux.
Peter Turner
2
@ Winston Ewert: "... la question est de savoir si vous devriez accéder aux données de la classe.": Eh bien, personne ne vous oblige à implémenter des getters et des setters pour toutes les variables membres, mais uniquement celles dont vous avez besoin. C'est pourquoi vous utilisez des accesseurs et des setters plutôt que des variables de membre public.
Giorgio
2
@ Winston Ewert: Je ne vois pas comment l'absence de getters / setters résoudrait ce problème: il doit exister un moyen d'accéder à toutes les données, sinon elles sont inutiles. Une bonne conception a pour tâche de décider quelle partie des données est accessible et quelle partie du code. Si l'on est un mauvais designer, il le sera avec ou sans accesseurs. Juste mes 2 cents.
Giorgio

Réponses:

162

Avoir des getters et des setters ne rompt pas en soi l'encapsulation. Ce qui ne fonctionne pas, c’est l’ajout automatique d’un getter et d’un setter pour chaque membre de données (chaque champ , en java lingo), sans y penser. Bien que cela soit mieux que de rendre tous les membres des données publiques, cela n’est qu’à un petit pas.

Le but de l'encapsulation n'est pas que vous ne devriez pas pouvoir savoir ou changer l'état de l'objet de l'extérieur de l'objet, mais que vous devriez avoir une politique raisonnable pour le faire.

  • Certains membres de données peuvent être entièrement internes à l'objet et ne doivent avoir ni getters ni setters.

  • Certains membres de données doivent être en lecture seule. Ils peuvent donc avoir besoin de getters, mais pas de setters.

  • Certains membres de données devront peut-être rester cohérents les uns avec les autres. Dans un tel cas, vous ne fourniriez pas de paramètre pour chacun, mais une méthode unique pour les configurer en même temps, afin de pouvoir vérifier la cohérence des valeurs.

  • Il est possible que certains membres de données ne doivent être modifiés que d'une certaine manière, par exemple incrémentés ou décrémentés d'un montant fixe. Dans ce cas, vous fourniriez une méthode increment()et / ou une decrement()méthode plutôt qu'un paramètre.

  • D'autres encore peuvent avoir besoin d'être en lecture-écriture et auraient à la fois un getter et un setter.

Considérons un exemple de class Person. Supposons qu'une personne ait un nom, un numéro de sécurité sociale et un âge. Disons que nous ne permettons pas aux gens de changer leur nom ou leur numéro de sécurité sociale. Cependant, l'âge de la personne devrait être incrémenté de 1 chaque année. Dans ce cas, vous devez fournir un constructeur qui initialise le nom et le SSN sur les valeurs données et initialise l'âge à 0. Vous fournissez également une méthode incrementAge()permettant d'augmenter l'âge de 1. Vous indiquez également Getters pour les trois. Aucun ajusteur n'est requis dans ce cas.

Dans cette conception, vous permettez à l'état de l'objet d'être inspecté de l'extérieur de la classe, et vous permettez qu'il soit modifié de l'extérieur de la classe. Cependant, vous ne permettez pas que l'état soit modifié de manière arbitraire. Il existe une politique qui stipule en effet que le nom et le SSN ne peuvent pas être modifiés du tout et que l'âge peut être incrémenté d'une année à la fois.

Maintenant, disons qu'une personne a aussi un salaire. Et les gens peuvent changer d'emploi à leur guise, ce qui signifie que leur salaire va également changer. Pour modéliser cette situation, nous n'avons pas d'autre moyen que de fournir une setSalary()méthode! Permettre que le salaire soit modifié à volonté est une politique parfaitement raisonnable dans ce cas.

En passant, dans votre exemple, je donnerais à la classe Fridgeles méthodes putCheese()and takeCheese(), au lieu de get_cheese()et set_cheese(). Ensuite, vous auriez toujours l'encapsulation.


public class Fridge {
  private List objects;
  private Date warranty;

  /** How the warranty is stored internally is a detail. */
  public Fridge( Date warranty ) {
    // The Fridge can set its internal warranty, but it is not re-exposed.
    setWarranty( warranty );
  }

  /** Doesn't expose how the fridge knows it is empty. */
  public boolean isEmpty() {
    return getObjects().isEmpty();
  }

  /** When the fridge has no more room... */
  public boolean isFull() {
  }

  /** Answers whether the given object will fit. */
  public boolean canStore( Object o ) {
    boolean result = false;

    // Clients may not ask how much room remains in the fridge.
    if( o instanceof PhysicalObject ) {
      PhysicalObject po = (PhysicalObject)o;

      // How the fridge determines its remaining usable volume is a detail.
      // How a physical object determines whether it fits within a specified
      // volume is also a detail.
      result = po.isEnclosedBy( getUsableVolume() );
    }

     return result;
  }

  /** Doesn't expose how the fridge knows its warranty has expired. */
  public boolean isPastWarranty() {
    return getWarranty().before( new Date() );
  }

  /** Doesn't expose how objects are stored in the fridge. */
  public synchronized void store( Object o ) {
    validateExpiration( o );

    // Can the object fit?
    if( canStore( o ) ) {
      getObjects().add( o );
    }
    else {
      throw FridgeFullException( o );
    }
  }

  /** Doesn't expose how objects are removed from the fridge. */
  public synchronized void remove( Object o ) {
    if( !getObjects().contains( o ) ) {
      throw new ObjectNotFoundException( o );
    }

    getObjects().remove( o );

    validateExpiration( o );
  }

  /** Lazily initialized list, an implementation detail. */
  private synchronized List getObjects() {
    if( this.list == null ) { this.list = new List(); }
    return this.list;
  }

  /** How object expiration is determined is also a detail. */
  private void validateExpiration( Object o ) {
    // Objects can answer whether they have gone past a given
    // expiration date. How each object "knows" it has expired
    // is a detail. The Fridge might use a scanner and
    // items might have embedded RFID chips. It's a detail hidden
    // by proper encapsulation.
    if( o implements Expires && ((Expires)o).expiresBefore( today ) ) {
      throw new ExpiredObjectException( o );
    }
  }

  /** This creates a copy of the warranty for immutability purposes. */
  private void setWarranty( Date warranty ) {
    assert warranty != null;
    this.warranty = new Date( warranty.getTime() )
  }
}
Dave Jarvis
la source
4
S'il vous plaît ne prenez pas mon exemple Fridge trop au sérieux. :) Un vrai frigo doit être un objet conteneur, je suppose qu'il sache tenir les objets sans se soucier de leur nature exacte. IE ce serait comme un ArrayList. En ce qui concerne ce qui en fait un réfrigérateur, disons qu'il sérialise les objets sur le disque lorsqu'ils sont stockés de manière à ce qu'ils survivent à une défaillance du système. D'accord. Assez pris mon exemple de réfrigérateur au sérieux.
Winston Ewert
6
Mais le réfrigérateur ne devrait-il pas connaître les dates de péremption pour pouvoir vous dire que le fromage a mal tourné et doit être jeté? :) Ok, il faut arrêter ça! :)
Dima
10
Une discussion assez intéressante sur la logique du frigo que vous avez ici ...
Mason Wheeler
35
Haha, j'aimerais voir l’annonce pour cela: "C’est un nouveau type de réfrigérateur: il vous jette des objets lorsque vous essayez de prendre quelque chose qui n’est pas là! Ainsi, vous ne le ferez qu’une seule fois! fourrer le frigo et laisser le frigo s'inquiéter d'un comportement illégal! "
Gablin
42
L'exemple a tout faux. Il ne devrait pas y avoir de champ Age ni de méthode setAge (). L'âge est une fonction de la date de naissance de la personne par rapport à un moment donné. Bien qu’apparemment trivial, c’est exactement ce qui cloche dans la pratique moderne de la mutabilité totale des objets et autres mauvais designs, telle que vue par les méthodes get / set pour les champs privés, par rapport à une analyse attentive de ce qui est réellement mutable, de ce qu’est un objet devrait connaître ses comportements et quels changements d'état sont réellement valides (quelles méthodes setX détruisent complètement).
Darrell Teague
41

La raison fondamentale pour les accesseurs en Java est très simple:

  • Vous ne pouvez spécifier que des méthodes , pas des champs, dans une interface.

Par conséquent, si vous souhaitez permettre à un champ de passer à travers l'interface, vous aurez besoin d'un lecteur et d'une méthode d'écriture. Celles-ci s'appellent traditionnellement getX et setX pour le champ x.

utilisateur1249
la source
44
C'est une limitation de la langue. La vraie question est de savoir si nous devrions autoriser d'autres objets à manipuler cet état.
Winston Ewert
3
@ Peter Turner, rien n'empêche manifestement un langage d'avoir des interfaces incluant des propriétés. Sous les couvertures qui pourraient bien être implémentées en tant que getter / setter mais il serait assez facile d'ajouter un support aux définitions d'interface pour les propriétés.
Winston Ewert
2
@ Winston, vous devrez éventuellement permettre aux classes de passer des informations entre elles pour que le travail soit réellement effectué. Que suggéreriez-vous à la place?
6
Un objet est censé fournir une interface de niveau supérieur à ses entrailles. Les Getters et les Setters ont tendance à être une interface de bas niveau. Par exemple, supposons que vous ayez une classe qui implémente un arbre binaire. Vous pouvez avoir des fonctions telles que GetLeft (), GetRight (), GetValue () et GetKey () afin de naviguer dans l’arborescence. Mais c'est l'approche complètement fausse. Votre classe d'arborescence binaire doit fournir des opérations telles que Rechercher, Insérer, Supprimer.
Winston Ewert
6
Pour prendre un autre exemple, considérons un morceau de tetris. La pièce tetris a un état interne tel que block_type, rotation, position. Vous pourriez avoir des accesseurs tels que GetBlockType (), GetRotation (), GetPosition (). Mais vous ne devriez vraiment pas. Ce que vous devriez avoir est une méthode GetSquares () qui renvoie une liste de tous les carrés occupés par cette pièce. Vous ne devriez pas non plus avoir des choses comme SetPosition () ou SetRotation (). Au lieu de cela, vous devriez avoir des opérations comme MoveLeft (), MoveRight (), Rotate (), Fall ().
Winston Ewert
20

De http://www.adam-bien.com/roller/abien/entry/encapsulation_violation_with_getters_and

Style JavaBean:

connection.setUser("dukie");
connection.setPwd("duke");
connection.initialize();

OO-style:

connection.connect("dukie","duke");

Eh bien, je préfère clairement la dernière approche; elle ne saigne pas les détails de l'implémentation, elle est plus simple et plus concise et toutes les informations nécessaires sont incluses dans l'appel de méthode. Il est donc plus facile de les comprendre correctement. Je préfère également définir des membres privés à l'aide de paramètres dans le constructeur, dans la mesure du possible.

Votre question est la suivante: quand un getter / setter est-il justifié? Peut-être lorsqu'un changement de mode est nécessaire ou que vous devez interroger un objet pour obtenir des informations.

myObject.GetStatus();
myObject.SomeCapabilitySwitch = true;

En y réfléchissant, lorsque j'ai commencé à coder en C #, j'ai écrit beaucoup de code dans le style Javabeans illustré ci-dessus. Mais au fur et à mesure que j'ai acquis de l'expérience dans la langue, j'ai commencé à définir davantage de membres dans le constructeur et à utiliser des méthodes qui ressemblaient davantage au style OO ci-dessus.

Robert Harvey
la source
6
Pourquoi le "donner tous les arguments en tant que paramètres" style OO?
5
Bien que votre "style OO" convient pour 2 ou 3 options, je préférerais le style JavaBean une fois que vous aurez dépassé ce niveau. Avoir 20 méthodes surchargées pour toutes les combinaisons possibles de paramètres a vraiment l'air horrible
TheLQ
1
@TheLQ: C # 4.0 facilite cette tâche en autorisant les paramètres facultatifs et nommés dans les appels de constructeur et de méthode.
Robert Harvey
7
@TheLQ C'est à cela que sert Builder avec une syntaxe fluide, voir par exemple MapMaker de Guava .
Maaartinus
5
@ Thorbjørn Ravn Andersen, je dirais que "donner tous les arguments en tant que paramètres" est préférable au style OO que "appeler des modulateurs JavaBean" car un donneur implique qu'un attribut sous-jacent soit défini, ce qui par définition expose les détails internes. L'utilisation de la méthode connect (..) dans l'exemple ne fait aucune hypothèse de ce type; vous définissez peut-être un utilisateur et un mot de passe, peut-être que non. Ce qui est important, c’est de vous concentrer sur le bon concept d’OA: le message "connect".
Andres F.
13

Quand les getters et les setters sont-ils justifiés?

Lorsque les comportements "get" et "set" correspondent en réalité au comportement de votre modèle, ce qui n'est pratiquement jamais .

Toute autre utilisation n’est qu’une fraude, car le comportement du domaine métier n’est pas clair.

Modifier

Cette réponse aurait peut-être semblé désinvolte, alors permettez-moi de développer. Les réponses ci-dessus sont pour la plupart correctes, mais nous nous concentrons sur les paradigmes de programmation de la conception orientée objet et sur ce qui manque vraiment. D'après mon expérience, cela amène les gens à penser qu'éviter les gettings et les setters est une règle académique pour les langages de programmation OO (par exemple, les gens pensent que remplacer des getters par des propriétés est une tâche fastidieuse)

En fait, c'est une conséquence du processus de conception. Il n'est pas nécessaire d'entrer dans les interfaces, l'encapsulation et les modèles, en se demandant si cela enfreint ou non ces paradigmes et ce qui constitue ou non une bonne programmation OO. Le seul point qui compte finalement est que, si rien dans votre domaine ne fonctionne de la sorte en les plaçant dans, vous ne modélisez pas votre domaine.

En réalité, il est hautement improbable qu'un système de votre domaine ait des accesseurs. Vous ne pouvez pas vous adresser à l'homme ou à la femme responsable de la paie et simplement dire "Réglez ce salaire sur X" ou "Recevez-moi ce salaire" . Un tel comportement n'existe tout simplement pas

Si vous mettez cela dans votre code, vous ne concevez pas votre système pour qu'il corresponde au modèle de votre domaine. Oui, ça casse les interfaces et l'encapsulation, mais ce n'est pas la question. Le fait est que vous modélisez quelque chose qui n'existe pas.

De plus, il vous manque probablement une étape ou un processus important, car il y a probablement une raison pour laquelle je ne peux pas me contenter de payer et de dire de fixer ce salaire à X.

Quand les gens utilisent des accesseurs et des passeurs, ils ont tendance à pousser les règles de ce processus au mauvais endroit. Cela s'éloigne encore plus de votre domaine. En utilisant l’exemple du monde réel, c’est comme un salaire en supposant que la personne aléatoire qui entre est autorisée à obtenir ces valeurs, sinon elle ne les demanderait pas. Non seulement ce n'est pas ainsi que le domaine est, mais il ment en fait sur la façon dont le domaine est.

Cormac Mulhall
la source
cela ne semble rien offrir de substantiel sur les points soulevés et expliqués dans les 17 réponses précédentes
gnat
1
Les autres réponses parlent d'objets, d'interfaces et d'autres paradigmes de langage. Ce qui est bien, mais ce n'est pas la question centrale. Les Getters et les setters ne sont pas seulement mauvais parce qu'ils enfreignent certaines conventions de codage, mais surtout parce que vous rencontrez rarement ce problème dans le modèle que vous représentez. Ce point a tendance à se perdre dans la discussion sur les meilleures pratiques de codage.
Cormac Mulhall
À votre avis, quelle est l'interface avec la paie si ce n'est pas un set/getsalaire?
Winston Ewert
1
S'ils sont entourés de couches d'autorisation et limités à certaines entités externes, ils ne sont pas des accesseurs. Je ne dis pas que le service de la paie ne dispose d'aucune méthode permettant de modifier le salaire, mais que cette méthode doit s'aligner sur les processus internes en place dans l'entreprise elle-même. Par exemple, la plupart des entreprises fixent le salaire en fonction du contrat du personnel, qui est autorisé par un responsable. Si le salaire de l'employé change, un nouveau contrat est établi et un responsable doit le signer. Par conséquent, la masse salariale devrait s'attendre à ce qu'un objet du contrat et un objet de l'autorisation modifient la liste de paie
Cormac Mulhall
3
Autrement dit, il y a toute une différence entre set_salary (new_salary, employee_id) et allowed_salary_update (employee_contract, authorising_manager). Le processus devrait modéliser le processus commercial interne
Cormac Mulhall le
11

En règle générale, les getters et les setters sont une mauvaise idée. Si un champ ne fait pas logiquement partie de l'interface et que vous le rendez privé, c'est bien. Si cela fait logiquement partie de l'interface et que vous le rendez public, c'est bien. Mais si vous le rendez privé, puis que vous vous retournez et le rendez effectivement public à nouveau en fournissant un getter et un setter, vous revenez à votre point de départ sauf que votre code est maintenant plus détaillé et plus obscur.

Évidemment, il y a des exceptions. En Java, vous devrez peut-être utiliser des interfaces. La bibliothèque standard Java a des exigences de compatibilité ascendante si extrêmes qu’elles dépassent les mesures normales de la qualité du code. Il est même possible que vous traitiez réellement le cas légendaire mais rare où il y a de bonnes chances que vous remplaciez ultérieurement un champ stocké par un calcul à la volée sans casser l'interface. Mais ce sont des exceptions. Getters et setters sont un anti-modèle qui nécessite une justification spéciale.

muraille
la source
6
Un getter et un setter signifie que l'attribut, et non le champ, fait partie de l'interface. Supposons que getBirthDate () et SetBirthDate () prennent "aaaa / mm / jj" mais que le champ stocké soit le nombre de jours depuis le 01/01/1000. Vous pouvez changer cela en 01/01/0001 et le getter et le setter garderont l'interface identique. Surtout parce que «public» signifie publiquement traçable, les variables ne doivent jamais être publiques; utilisez toujours un setter pour valider la valeur entrante, mettez à jour les champs associés, effectuez la conversion si nécessaire, etc. Essayez d'utiliser un getter au cas où la mémoire interne changerait.
Andy Canfield
@ AndyCanfield, c'est un peu fort de dire que public veut dire publiquement traçable .. Ce n'est le cas si vous programmez mal. Si vous avez un setter et que la mauvaise valeur lui est envoyée, alors vous pourriez lever une exception que vous pourriez appeler détruire le programme, mais c'est quitter avec une erreur. Aussi, si l'utilisateur de l'objet peut donner une valeur à une variable et peut lui donner la mauvaise valeur la valeur alors vous pouvez garder cela à l'esprit et le tester.u peut dire "ah, mais je n'ai qu'à faire 1 test si j'utilise un setter", mais peut-être que votre propre code donne à la variable une mauvaise valeur ... alors le setter pourrait ne pas le faire réduisez vos tests.n avec des tests, votre prog n'est pas "détruit"
barlop
3

que le champ soit accessible directement ou par la méthode n'est pas vraiment important.

Les invariants de classe (utiles) sont importants. Et pour les préserver, nous avons parfois besoin de ne pas pouvoir changer quelque chose de l'extérieur. Par exemple. si nous avons la classe Square avec une largeur et une hauteur séparées, changer l’une d’elles en fait une place autre que carrée. Nous avons donc besoin de la méthode changeSide Si c'était Rectangle, nous pourrions avoir un champ setters / public. Mais un testeur vérifierait si sa valeur supérieure à zéro serait meilleure.

Et dans des langages concrets (par exemple, Java), il existe des raisons pour lesquelles nous avons besoin de ces méthodes (interfaces). Et une autre raison de la méthode est la compatibilité (source et binaire). Il est donc plus facile de les ajouter, puis de se demander si un champ public suffirait.

btw. J'aime utiliser des classes de valeurs immuables simples avec des champs finaux publics.

utilisateur470365
la source
C'est à peu près la seule chose à éviter. Aller d’un champ à l’autre / getter / setter est un changement radical dans la plupart des langues.
MrDosu
2

Vous voudrez peut-être modifier vos éléments internes en changeant les interfaces tout en conservant les mêmes. Si vos interfaces ne varient pas, votre code ne cassera pas. Vous pouvez toujours changer vos internes comme vous le souhaitez.

George Silva
la source
1
Je suis surpris que cette réponse soit si loin. Les getters et les setters vous permettent d’en faire plus à l’avenir (par exemple, déclencher un événement, valider davantage les entrées, effectuer une comptabilité interne) sans casser votre API existante. Même avec du code non public, moins vous avez à changer d'API, moins de dépendances à mettre à jour et moins de bogues introduits lors de ces mises à jour.
AmadeusDrZaius
0

Mon approche est la suivante -

Lorsque je compte utiliser les données plus tard, un getter / setter est justifié. De plus, si des changements surviennent, j’insère souvent les données dans un getter / setter.

Si c'est une structure POD, je laisse les créneaux disponibles.

Sur un plan plus abstrait, la question est "qui gère les données", et cela dépend du projet.

Paul Nathan
la source
0

Si utiliser des accesseurs et des setters semble compliqué, le problème pourrait être la langue, pas le concept lui-même.

Voici le code du deuxième exemple écrit en Ruby:

class Fridge
  attr_accessor :cheese
end

def go_shopping fridge
  fridge.cheese += 5
end

Notez que cela ressemble beaucoup au premier exemple en Java? Lorsque les accesseurs et les régleurs sont traités comme des citoyens de premier ordre, ils ne constituent pas une corvée et la flexibilité supplémentaire peut parfois être une véritable aubaine. Par exemple, nous pourrions décider de renvoyer une valeur par défaut pour le fromage sur un nouveau réfrigérateur:

class Fridge
  attr_accessor :cheese

  def cheese
    @cheese || 0
  end
end

Bien sûr, il y aura beaucoup de variables qui ne devraient pas du tout être exposées publiquement. Exposer inutilement des variables internes aggravera votre code, mais vous pouvez difficilement blâmer cela pour les getters et les setters.

Tobias Cohen
la source
C'est magnifiquement dit. Des fonctions de construction ont été ajoutées car nous souhaitons créer des objets immuables tout en construisant progressivement la recette: peut-être que si vous ajoutez un édulcorant artificiel, vous n'avez pas besoin d'ajouter de sucre. À l’aide de paramètres nommés, et même en cas de surcharge, il est très difficile de créer une méthode pour tout faire. Et bien sûr, java n’a pas de paramètres nommés, d’où une raison supplémentaire pour laquelle les utilisateurs utilisent le modèle Builder.
YoYo
0

Prenons une Sizeclasse qui englobe la largeur et la hauteur. Je peux éliminer les setters en utilisant le constructeur, mais en quoi cela m'aide-t-il à dessiner un rectangle Size? La largeur et la hauteur ne sont pas des données internes à la classe; ce sont des données partagées qui doivent être disponibles pour les consommateurs de Size.

Les objets sont constitués de comportements et d’états ou d’attributs. S'il n'y a pas d'état exposé, seuls les comportements sont publics.

Sans état, comment trieriez-vous une collection d'objets? Comment rechercheriez-vous une instance spécifique d'un objet? Si vous utilisez uniquement des constructeurs, que se passe-t-il lorsque votre objet possède une longue liste d'attributs?

Aucun paramètre de méthode ne doit être consommé sans validation. C'est donc par paresse d'écrire:

setMyField(int myField){
    this.myField = myField;
}

Si vous écrivez de cette façon, vous vous êtes au moins préparé à utiliser le setter pour la validation; c'est mieux qu'un terrain public - mais à peine. Mais au moins, vous avez une interface publique cohérente sur laquelle vous pouvez revenir et mettre des règles de validation sans enfreindre le code de vos clients.

Getters, setters, properties, mutators, appelez-les comme vous voulez, mais ils sont nécessaires.

réduction
la source
Mais au moins, vous avez une interface publique cohérente sur laquelle vous pouvez revenir et mettre des règles de validation sans enfreindre le code de vos clients. Ce n'est pas vrai. Ajouter des règles de validation plus tard pourrait très bien "casser le code de vos clients". Prenez une intvariable d'instance, par exemple, et imaginez que vous décidiez d'ajouter une règle de validation pour n'accepter que des valeurs non négatives. Le problème est que le code de votre client peut déjà compter sur la possibilité de lui attribuer une valeur négative ...
jub0bs
0

Si les getters et les setters violent l’encapsulation et le vrai OO, alors je suis sérieusement en difficulté

J'ai toujours senti qu'un objet représentait tout ce qui vous venait à l'esprit et dont vous aviez besoin.

Je viens de finir d'écrire un programme qui génère des labyrinthes en Java, et j'ai une classe qui représente les "carrés de labyrinthe". J'ai des données dans cette classe qui représentent des coordonnées, des murs et des booléens, etc.

Je dois avoir un moyen de changer / manipuler / accéder à ces données! Qu'est-ce que je fais sans les geters et les setters? Java n'utilise pas les propriétés et la définition de toutes mes données locales de cette classe sur public constitue DEFINITIVEMENT une violation de l'encapsulation et de OO.

Bryan Harrington
la source
6
Voici la question: Si vous avez rendu tous ces champs publics et avez cessé d'utiliser les accesseurs et les setters, en quoi votre code serait-il différent?
Winston Ewert
En théorie, ce n'est pas. Pourquoi même faire un cours à ce moment-là? Le même argument pourrait être fait avec n'importe quoi. Mais le message ci-dessous le mien semble l'avoir dit plus élégamment. (Les accesseurs et les setters sont un moyen de donner en toute sécurité une valeur à un objet, ou de récupérer une valeur de cet objet.) La publication continue ..
Bryan Harrington
3
Je ferais remarquer que cette affiche a finalement été d'accord avec moi et a publié une autre réponse à cet effet. L'utilisation de getters et de setters revient essentiellement à manipuler les données directement. Vous ne gagnez pas vraiment OOness en faisant cela. Pour être OO, vous devez fournir une interface de niveau supérieur aux données.
Winston Ewert
4
Je pense que la meilleure façon de le décrire serait de dire que vous devez concevoir vos interfaces de l'extérieur vers l'intérieur et non de l'intérieur. L'interface fournie par votre objet doit être dictée par la manière dont l'objet est utilisé et non par la manière dont il est implémenté. Donc, n'écrivez pas de getters et permettez à un objet externe d'interpréter les données. Trouvez la question à laquelle ils doivent répondre et écrivez une méthode qui répond à cette question. N'autorisez pas les objets externes à modifier l'état d'un setter, découvrez le type de manipulations qu'ils doivent effectuer et écrivez des méthodes qui le font.
Winston Ewert
2
Dans certains cas, les Getters et les Setters sont la meilleure méthode. Parfois, ils sont la meilleure interface que vous puissiez fournir. Cependant, dans de nombreux cas, ils ne le sont pas. Dans de nombreux cas, vous ne faites que divulguer des détails d'implémentation dans d'autres classes.
Winston Ewert
-1

Premièrement, il existe en gros deux types d’objets que je commenterai, les types de valeur et de service.

Les types de services ne devraient jamais avoir de paramètres, les dépendances dont ils ont besoin ne devraient pas être négligeables. Le meilleur moyen de passer des dépendances est de passer par un constructeur ou une fabrique, de sorte que toutes les instances soient entièrement formées dès le début, de manière claire et simple.

Les types de valeur doivent également être immuables. D'un autre côté, il peut arriver que ce ne soit pas pratique, comme un ORM, ou un autre mappage, par exemple d'un widget à un objet. Tous les autres types de valeur transmis dans le système d'une couche ou d'une partie à une autre doivent être immuables et ne doivent pas comporter de paramètres.

mp01
la source
Je suggérerais un troisième type: un conteneur mutable, dont le but est de contenir une ou plusieurs valeurs; dans de nombreux cas, un conteneur ne devrait pas avoir beaucoup de logique ou de validation. Le code nécessitant une méthode pour calculer six nombres entiers positifs peut transmettre un conteneur de six nombres entiers à la méthode, qui stockera les résultats. La responsabilité de s’assurer que les chiffres sont positifs devrait généralement incomber à l’appelant ou à la méthode appelée, et non au conteneur.
Supercat
-1

Un Getter / Setter est justifié comme n'importe quelle autre méthode publique. La justification est principalement parce que nous voulons fournir une interface vers le monde extérieur qui définit la manière dont notre classe interagit avec d'autres classes. Nous le faisons principalement parce que nous voulons réduire le couplage entre entités distinctes. Réduire le couplage est une bonne chose pour plusieurs raisons:

  • Je peux utiliser à partir de mon code actuel la même interface de classe bien que, dans l'intervalle, cette classe ait ajouté de nouvelles méthodes (tout en préservant l'ancienne interface)
  • Peut changer la représentation interne d'une classe sans affecter ses consommateurs
  • Réduisez les risques de bugs: si vous rendez vos données internes privées, vous pouvez être sûr que le code externe ne gâchera pas vos données internes.
Ghita
la source
-1

Oui, les getters et les setters sont un anti-modèle dans la programmation orientée objet et ne doivent jamais être utilisés: http://www.yegor256.com/2014/09/16/getters-and-setters-are-evil.html . En un mot, ils ne rentrent pas dans le paradigme orienté objet car ils vous incitent à traiter un objet comme une structure de données. Ce qui est une idée fausse majeure.

yegor256
la source
2
cela ne semble rien offrir de substantiel sur les points soulevés et expliqués dans les 18 réponses précédentes
gnat
4
C'est aussi un peu fort de ne jamais dire .
Winston Ewert
-4

J'espère que tout ce que l'EDI développé par la société qui développe votre langage de programmation généré automatiquement n'enfreint pas les "Principes d'orientation de l'objet".

Cela étant dit, chaque fois que vous avez une variable de portée publique, utilisez un getter et un setter et vous êtes en or. Cela me semble être l’élément nécessaire du principe d’encapsulation extrêmement orienté objet, même s’il ressemble à beaucoup de code.

La raison de les utiliser est que l'encapsulation appropriée peut encore avoir lieu et tant que vous avez extrait le calque qui permet à la personne qui manipule votre objet de sentir votre objet, vous pouvez l'enlever sans même le savoir.

Qu'il suffise de dire, une maison divisée ne peut subsister, l'un des locataires de OO ne va pas s'enflammer et vous ne pouvez pas servir à la fois l'encapsulation et l'optimisation prématurée.

Peter Turner
la source
4
Et qu'est-ce que j'ai gagné en ajoutant beaucoup de code pour appeler setFoo () et getFoo () chaque fois que je traite de la variable?
Winston Ewert
7
Voici la chose, je ne pense pas que vous obtenez l'encapsulation. Vous manipulez toujours l'état de l'objet depuis d'autres endroits. Vous le faites plus verbalement. Pour obtenir une encapsulation, vous devez centraliser la manipulation des valeurs dans la classe propriétaire de cette valeur. Tout le reste n'est que du code de procédure dans les vêtements OO. (Rien de mal à cela, mais c'est ce que c'est).
Winston Ewert
2
L'avantage de l'encapsulation est que toute la logique liée à des valeurs particulières se trouve à un endroit (plus ou moins). Je ne comprends pas cela avec les Getters et les Setters. Par conséquent, je ne pense pas avoir beaucoup d’avantages pour eux.
Winston Ewert
1
Cependant, je conviens que je préférerais avoir du code avec des getters et des setters, puis un code manipulant librement des données via un accès public. De cette façon, je peux au moins joindre une validation des données entrantes.
Winston Ewert
3
Je ne suis certainement pas un fan de verbosité. Mais mon objection concerne en réalité les personnes qui rendent leurs variables privées, puis qui appellent des accesseurs et des setters, se retrouvant sans aucune différence. Si vous voulez utiliser des getters / setters dans votre classe, c'est très bien. La question est pourquoi? À ma connaissance, le principal avantage est que vous pouvez valider les données pour détecter les erreurs plus tôt. Cela est moins utile lorsque tout le code manipulant les données se trouve au même endroit. Mais cela peut toujours être utile. Idéalement, vous avez un langage qui prend en charge les propriétés et peut éviter la verbosité.
Winston Ewert
-4

Je pensais que nous aurions des réponses plus habituelles ici?

Les Getters et les setters sont généralement extrêmement efficaces (quiconque dit autre chose a tort, aussi simple que cela).

Bien que vous deviez vous rappeler de ne pas avoir trop d'ingénieur, ne faites jamais un getter ou un setter inutile. Toute information que vous n'allez pas utiliser par une autre classe et pour laquelle vous ne devriez avoir aucun getter ou setter.

Cependant, la raison pour laquelle vous ne rendez pas les champs publics et que vous avez plutôt des accesseurs et des setters sont principalement:

  • Il n'est pas possible de faire des tests appropriés en utilisant des champs. Les Getters / setters sont bien meilleurs à cet égard.

  • Il est impossible d'utiliser correctement les champs dans un contexte de programmation en temps réel. Getters / setters synchronisés est la voie à suivre.

  • Le sujet de l'interface (déjà expliqué donc je ne le ferai pas).

  • Il est plus facile à utiliser lorsque vous réutilisez correctement le système.

  • Il est beaucoup plus difficile de savoir quelles classes utilisent votre classe et ce qui se brisera si vous changez de champ plutôt qu'un getter / setter.

Par conséquent, le seul moment où vous utilisez un champ public au lieu d’un getter / setter, c’est quand vous ne créez pas de projet officiel, mais que vous le faites simplement pour le plaisir et que vous ne pouvez pas vous soucier du getter / setters.

Skarion
la source
2
+1 pour la testabilité. Choqué que personne n'en ait parlé plus tôt. Une autre fonctionnalité utile des propriétés est leur débogage. (Il est beaucoup plus facile d'insérer un point d'arrêt dans le code que d'exiger des points d'arrêt matériel / mémoire pour les champs).
Mark H
9
L'objection à l'encontre des getters et setters est qu'ils sont souvent habitués à suivre la lettre du droit de la programmation orientée objet tout en ignorant l'esprit et les intentions. OOP dit que vous ne devriez pas laisser d'autres objets se mêler à vos objets internes. Si vous définissez des getters et des setters qui vous permettent de le faire de toute façon, vous finissez par violer la POO.
Winston Ewert
6
Je conviens que les getters et les setters ont de nombreux avantages sur les simples terrains publics. Cependant, je ne vois pas en quoi les tests sont meilleurs avec eux. Pourriez-vous expliquer?
Winston Ewert
4
@Skarion: Je ne suis pas d'accord avec votre réponse. Vous semblez présenter deux alternatives: soit en utilisant des accesseurs, soit en rendant les champs publics. Mais il existe une troisième option: ne pas utiliser de getters / setters ni de champs exposés! Pourquoi auriez-vous besoin de tester l'état d'un champ interne? Ne devriez-vous pas tester l'API publique de votre objet?
Andres F.