229 votes

Les getters et setters sont-ils mal conçus ? Conseils contradictoires

Je travaille actuellement sur un jeu simple en Java avec plusieurs modes différents. J'ai étendu une classe principale de jeu pour placer la logique principale dans les autres classes. Malgré cela, la classe principale du jeu reste assez lourde.

Après avoir jeté un coup d'œil rapide à mon code, j'ai constaté que la majorité était constituée de Getters et de Setters (60 %), alors que le reste était vraiment nécessaire à la logique du jeu.

Quelques recherches sur Google ont affirmé que les Getters et les Setters sont diaboliques, tandis que d'autres ont affirmé qu'ils sont nécessaires à une bonne pratique de l'OO et à de bons programmes.

Que dois-je faire ? Lequel choisir ? Dois-je modifier mes Getters et Setters pour mes variables privées, ou dois-je les conserver ?

0 votes

349voto

Zarkonnen Points 11086

Il y a aussi le point de vue selon lequel, la plupart du temps, l'utilisation de setters rompt toujours l'encapsulation en vous permettant de définir des valeurs qui n'ont pas de sens. Par exemple, si vous avez un compteur de score dans le jeu qui ne fait qu'augmenter, au lieu de

// Game
private int score;
public void setScore(int score) { this.score = score; }
public int getScore() { return score; }
// Usage
game.setScore(game.getScore() + ENEMY_DESTROYED_SCORE);

il devrait l'être

// Game
private int score;
public int getScore() { return score; }
public void addScore(int delta) { score += delta; }
// Usage
game.addScore(ENEMY_DESTROYED_SCORE);

Il s'agit peut-être d'un exemple un peu facile. Ce que j'essaie de dire, c'est qu'en discutant des getter/setters par rapport aux champs publics, on occulte souvent des problèmes plus importants, à savoir que les objets manipulent l'état interne des autres de manière intime et sont donc trop étroitement couplés.

L'idée est de créer des méthodes qui font directement ce que vous voulez faire. Un exemple serait de définir le statut "vivant" des ennemis. Vous pourriez être tenté d'avoir une méthode setAlive(boolean alive). Au lieu de cela, vous devriez avoir :

private boolean alive = true;
public boolean isAlive() { return alive; }
public void kill() { alive = false; }

La raison en est que si vous modifiez l'implémentation pour que les choses n'aient plus un booléen "vivant" mais plutôt une valeur de "points de vie", vous pouvez changer cela sans rompre le contrat des deux méthodes que vous avez écrites plus tôt :

private int hp; // Set in constructor.
public boolean isAlive() { return hp > 0; } // Same method signature.
public void kill() { hp = 0; } // Same method signature.
public void damage(int damage) { hp -= damage; }

4 votes

Qu'en est-il dans un langage comme c# ou actionscript, où get et set font partie de la syntaxe ? isAlive pourrait avoir un getter mais pas de setter.

5 votes

Il est important de noter ici que la validation peut être effectuée dans le cadre de ces méthodes d'action.

1 votes

Je ne pense pas que l'exemple "vivant" soit convaincant. Au lieu d'utiliser "public void damage(int damage) { hp -= damage ; }", on peut utiliser "public void damage(int damage) { hp -= damage ; if (hp <= 0) setAlive(false) ; }" avec "public void setAlive(boolean alive){if (!alive) hp = 0;}", qui suit toujours le modèle getter/setter et me semble plus logique.

186voto

Jon Skeet Points 692016
  • Très mauvais : les champs publics.
  • Quelque peu diabolique : Getters et setters là où ils ne sont pas nécessaires.
  • Bon : Getters et setters uniquement là où ils sont vraiment nécessaires - faire en sorte que le type expose un comportement "plus large" qui se trouve être utiliser son état, plutôt que de traiter le type comme un dépôt d'état à manipuler par d'autres types.

Cela dépend toutefois de la situation. faire veulent juste un objet de données stupide.

2 votes

Puisqu'il n'est pas évident que vos cas "quelque peu diaboliques" ne nécessiteront pas un "comportement plus large", j'opterais pour la "bonne" solution, juste au cas où, afin que vous n'ayez pas à changer votre code partout si vous changez d'avis !

0 votes

@Benoît - Je pense que c'est le but recherché. Jon dit que les getters et setters inutiles sont à peine meilleurs que les champs publics - il y a de meilleurs choix - mais ils sont toujours meilleurs que les champs publics.

1 votes

Chacune de ces valeurs change au fur et à mesure que le jeu se déroule, de sorte qu'aucune constante n'est nécessaire. Cependant, ces variables ne sont réellement utilisées que pour contenir les paramètres du jeu (c'est-à-dire le mode, la limite de temps). C'est pourquoi je ne peux m'empêcher de penser qu'elles sont quelque peu inutiles.

47voto

rtperson Points 7065

Vous avez déjà eu beaucoup de bonnes réponses à ce sujet, alors je vais juste donner mes deux cents. Les Getters et les Setters sont très, très mauvais. Ils vous permettent essentiellement de prétendre cacher l'état interne de votre objet alors que la plupart du temps, tout ce que vous avez fait, c'est jeter du code redondant qui ne fait rien pour cacher l'état interne. Pour un simple POJO, il n'y a aucune raison pour que getName() et setName() ne soient pas remplacés par obj.name = "Tom".

Si l'appel de méthode remplace simplement l'affectation, tout ce que vous avez gagné en préférant l'appel de méthode, c'est un gonflement du code. Malheureusement, le langage a consacré l'utilisation des getters et setters dans la spécification JavaBeans, de sorte que les programmeurs Java sont obligés de les utiliser, même si cela n'a aucun sens.

Heureusement, Eclipse (et probablement d'autres IDE) vous permet de les générer automatiquement. Et pour un projet amusant, j'ai une fois construit un générateur de code pour eux en XSLT. Mais s'il y a une chose dont je me débarrasserais en Java, c'est la dépendance excessive à l'égard des getters et setters.

0 votes

Vous avez omis la raison pour laquelle ils sont mauvais.

1 votes

Je l'ai corrigé pour que mon objection soit plus claire.

6 votes

Ob.name = "Tom" peut fonctionner de la même manière que ob.setName("Tom"), mais il est inférieur parce qu'il ne vous permet pas d'ajouter un comportement plus tard sans modifier l'API. C'est la programmation orientée objet 101.

29voto

coobird Points 70356

Les Getters et les Setters mettent en œuvre le concept de encapsulation dans la programmation orientée objet.

En cachant les états de l'objet au monde extérieur, l'objet est véritablement responsable de lui-même et ne peut être modifié de manière non intentionnelle. Les seules façons de manipuler l'objet sont les méthodes publiques exposées, telles que les getters et setters.

Il y a quelques avantages à avoir des getters et des setters :

1. Permettre des changements futurs sans modifier le code qui utilise la classe modifiée.

L'un des grands avantages de l'utilisation d'un getter et d'un setter est qu'une fois que les méthodes publiques sont définies et qu'il arrive un moment où l'implémentation sous-jacente doit être modifiée (par exemple, découverte d'un bogue qui doit être corrigé, utilisation d'un algorithme différent pour améliorer les performances, etc.), le fait que les getters et setters soient le seul moyen de manipuler l'objet permet au code existant de ne pas être cassé et de fonctionner comme prévu même après le changement.

Par exemple, disons qu'il y a un setValue qui définit la méthode value variable privée dans un objet :

public void setValue(int value)
{
    this.value = value;
}

Mais une nouvelle exigence est apparue, qui nécessitait d'enregistrer le nombre de fois où l'on avait besoin d'une assistance technique. value a été modifié. Avec l'outil de définition en place, le changement est assez trivial :

public void setValue(int value)
{
    this.value = value;
    count++;
}

Si el value sont publics, il n'y a pas de moyen facile de revenir plus tard et d'ajouter un compteur qui garde la trace du nombre de fois où la valeur a été modifiée. Par conséquent, l'existence de getters et de setters est un moyen de "protéger" la classe contre les changements qui pourraient survenir ultérieurement.

2. Renforcer les moyens par lesquels l'objet peut être manipulé.

Les getters et setters sont également utiles pour imposer les manières dont l'objet peut être manipulé, ce qui permet à l'objet de contrôler son propre état. Si les variables publiques d'un objet sont exposées, elles peuvent facilement être corrompues.

Par exemple, un ImmutableArray contient un int tableau appelé myArray . Si le tableau était un champ public, il ne serait pas immuable :

ImmutableArray a = new ImmutableArray();
int[] b = a.myArray;
b[0] = 10;      // Oops, the ImmutableArray a's contents have been changed.

Pour mettre en œuvre un tableau réellement immuable, il est nécessaire d'ajouter un getter pour le tableau ( getArray ) doit être écrite de manière à ce qu'elle renvoie une copie de son tableau :

public int[] getArray()
{
    return myArray.clone();
}

Et même si le cas suivant se produit :

ImmutableArray a = new ImmutableArray();
int[] b = a.getArray();
b[0] = 10;      // No problem, only the copy of the array is affected.

Le ImmutableArray est en effet immuable. Exposer les variables d'un objet permettra de le manipuler d'une manière qui n'est pas prévue, mais ce n'est qu'en exposant certaines manières (getters et setters) que l'objet peut être manipulé d'une manière prévue.

Je suppose que le fait d'avoir des getters et des setters serait plus important pour les classes qui font partie d'une API qui va être utilisée par d'autres, car cela permet de garder l'API intacte et inchangée tout en autorisant des changements dans l'implémentation sous-jacente.

Compte tenu de tous les avantages des getters et setters, si le getter ne fait que renvoyer la valeur de la variable privée et que le setter ne fait qu'accepter une valeur et l'assigner à une variable privée, il semble que les getters et setter soient tout simplement superflus et constituent un véritable gaspillage. Si la classe est destinée à un usage interne par une application qui ne sera pas utilisée par d'autres, l'utilisation extensive des getters et des setters n'est peut-être pas aussi importante que lors de l'écriture d'une API publique.

2 votes

Non, vous pouvez garder le setter **privé** de toute façon. L'immutabilité est un autre sujet, par définition imuttable est quand vous ne pouvez pas changer sa valeur après sa création comme String par exemple. Donc dans votre exemple, c'est un bug. Regardez le C# ou le langage supportant les propriétés.

0 votes

@Simo - le tableau d'appui est immuable dans l'exemple.

0 votes

@coobird - même si les getters/setters ne font que renvoyer/mettre en place sans logique supplémentaire, cela ne protège-t-il pas l'API pour l'avenir lorsqu'une logique supplémentaire pourrait être nécessaire ?

15voto

Michael Borgwardt Points 181658

Votre classe Game suit probablement la méthode objet divin s'il expose autant de variables. Il n'y a rien de mal aux getters et setters (bien que leur verbosité en Java puisse être un peu agaçante) ; dans une application bien conçue où chaque classe a une fonctionnalité clairement séparée, vous n'aurez pas besoin de douzaines d'entre eux dans une seule classe.

Editer : Si l'objectif principal des getters et setters est de "configurer" la classe de jeu (c'est ainsi que je comprends votre commentaire), alors vous n'avez probablement pas besoin des getters (il est parfaitement possible pour une classe d'accéder à ses propres variables privées sans utiliser de méthodes get), et vous pouvez probablement regrouper de nombreux setters en "setters de groupe" qui définissent plusieurs variables qui vont ensemble d'un point de vue conceptuel.

0 votes

Le problème est que, bien qu'il existe plusieurs paramètres pour ce jeu, les différences sont minimes et il suffit d'appliquer quelques méthodes supplémentaires dans les classes sœurs pour obtenir des valeurs différentes.

0 votes

Merci de votre réponse. En réponse à votre édition, si j'appelle deux setters à l'intérieur de chaque classe sœur, la bonne pratique OO consisterait-elle à supprimer les deux méthodes setter dans la classe Game principale pour les transformer en un setter de groupe ?

1 votes

Exactement - si ces valeurs sont toujours définies ensemble, il est préférable de conserver cette information en n'ayant qu'un seul setter. Il serait même préférable de les regrouper dans une classe, s'il y en a plus de deux, ou s'il existe des méthodes qui n'opèrent que sur elles et qui pourraient également être placées dans cette classe.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X