Question

Cette question concerne spécifiquement le remplacement de la méthode equals () pour les objets comportant un grand nombre de champs. Tout d’abord, permettez-moi de dire que cet objet volumineux ne peut pas être divisé en plusieurs composants sans enfreindre les principes de OO. Par conséquent, en me disant "aucune classe ne devrait avoir plus de x champs". ne va pas aider.

Ensuite, le problème a pris forme lorsque j'ai oublié de vérifier l’un des champs pour vérifier l’égalité. Par conséquent, ma méthode equals était incorrecte. Alors j'ai pensé à utiliser la réflexion:

--code removed because it was too distracting--

Le but de cet article n'est pas nécessairement de refactoriser le code (ce n'est même pas le code que j'utilise), mais plutôt de savoir si c'est une bonne idée ou non.

Avantages:

  • Si un nouveau champ est ajouté, il est automatiquement inclus
  • La méthode est beaucoup plus concise que 30 instructions if

Inconvénients:

  • Si un nouveau champ est ajouté, il est automatiquement inclus, ce n'est parfois pas souhaitable
  • Performance: cela doit être plus lent, je ne ressens pas le besoin de sortir un profileur
  • La liste de certains champs à ignorer dans la comparaison est un peu moche

Avez-vous des idées?

Était-ce utile?

La solution

Si vous souhaitez ajouter des éléments à la liste blanche pour des raisons de performances, utilisez une annotation pour indiquer les champs à comparer. De plus, cette implémentation ne fonctionnera pas si vos champs ne disposent pas de bonnes implémentations pour equals () .

P.S. Si vous suivez cette route pour equals () , n'oubliez pas de faire quelque chose de similaire pour hashCode () .

P.P.S. J'espère que vous avez déjà pris en compte HashCodeBuilder . et EqualsBuilder .

Autres conseils

Utilisez Eclipse, FFS!

Supprimez les méthodes hashCode et equals que vous avez.

Cliquez avec le bouton droit sur le fichier.

Sélectionnez Source- > Générer un hashcode et est égal à ...

Fait! Fini les soucis de réflexion.

Répétez cette opération pour chaque champ ajouté. Il vous suffit d'utiliser la vue hiérarchique pour supprimer vos deux méthodes, puis laissez Eclipse les générer automatiquement.

Si vous optez pour la réflexion, EqualsBuilder reste votre ami:

 public boolean equals(Object obj) {
    return EqualsBuilder.reflectionEquals(this, obj);
 }

Voici une pensée si vous vous inquiétez de:

1 / Oublier de mettre à jour votre grande série d'instructions if pour vérifier l'égalité lorsque vous ajoutez / supprimez un champ.

2 / La performance obtenue dans la méthode equals ().

Essayez ce qui suit:

a / Revenez à l'utilisation de la longue séquence d'instructions if dans votre méthode equals ().

b / Avoir une fonction unique contenant une liste des champs (dans un tableau de chaînes) et vérifiant cette liste par rapport à la réalité (c'est-à-dire les champs réfléchis). Il lancera une exception s’ils ne correspondent pas.

c / Dans votre constructeur pour cet objet, lancez un appel à la fois synchronisé à cette fonction (semblable à un modèle singleton). En d’autres termes, s’il s’agit du premier objet construit par cette classe, appelez la fonction de vérification décrite en (b) ci-dessus.

L'exception le rendra immédiatement évident lorsque vous exécuterez votre programme si vous n'avez pas mis à jour vos instructions if pour qu'elles correspondent aux champs reflétés; alors vous corrigez les instructions if et mettez à jour la liste de champs de (b) ci-dessus.

Cette construction d’objets n’effectuera pas cette vérification et votre méthode equals () fonctionnera à la vitesse maximale possible.

Malgré tous mes efforts, je n’ai trouvé aucun problème réel avec cette approche (il se peut que StackOverflow suscite de plus en plus d’esprit). Il existe une vérification supplémentaire des conditions de chaque construction d’objet pour le comportement d’exécution unique, mais cela semble assez. mineur.

Si vous essayez assez fort, vous pouvez toujours obtenir vos déclarations if décalées par rapport à votre liste de champs et aux champs réfléchis, mais cette exception garantira que votre liste de champs correspond aux champs reflétés et vous devez simplement vous assurer de mettre à jour le champ if. instructions et liste de champs en même temps.

Vous pouvez toujours annoter les champs que vous voulez / ne voulez pas dans votre méthode equals, cela devrait être un changement simple et direct.

Les performances sont évidemment liées à la fréquence à laquelle l’objet est réellement comparé, mais de nombreux frameworks utilisent des cartes de hachage. Il est donc possible que vos équivalents soient utilisés plus souvent que vous ne le pensez.

De plus, en parlant de hash maps, vous avez le même problème avec la méthode hashCode.

Enfin, avez-vous vraiment besoin de comparer tous les champs pour l’égalité?

Votre code contient quelques bugs.

  1. Vous ne pouvez pas supposer que this et obj appartiennent à la même classe. En effet, il est explicitement permis que obj soit une autre classe. Vous pouvez commencer par si (! Obj instanceof myClass) renvoie false; , mais n'est toujours pas correct car obj pourrait être une sous-classe de ce avec des champs supplémentaires qui pourraient avoir de l'importance.
  2. Vous devez prendre en charge les valeurs null pour obj avec un simple if (obj == null) renvoie false;
  3. Vous ne pouvez pas traiter null et la chaîne vide comme égaux. Traitez plutôt null spécialement. Le plus simple est de commencer par comparer Field.get (obj) == Field.get (this) . Si les deux sont égaux ou s’ils pointent tous deux vers le même objet, la procédure est rapide. (Remarque: il s'agit également d'une optimisation dont vous avez besoin puisqu'il s'agit d'une routine lente.) Si cela échoue, vous pouvez utiliser le rapide if (Field.get (obj) == null || Field.get (this ) == null) retourne false; pour traiter les cas où exactement un est null . Enfin, vous pouvez utiliser l'habituel equals () .
  4. vous n'utilisez pas foundMismatch

Je suis d'accord avec Hank pour dire que [HashCodeBuilder] [1] et [EqualsBuilder] [2] est une meilleure solution. Il est facile à gérer, pas beaucoup de code standard, et vous évitez tous ces problèmes.

Vous pouvez utiliser les annotations pour exclure des champs de la vérification

par exemple

@IgnoreEquals
String fieldThatShouldNotBeCompared;

Et puis, bien sûr, vous vérifiez la présence de l'annotation dans votre méthode générique égal à.

Si vous avez accès aux noms des champs, pourquoi ne pas en faire une norme, à savoir que les champs que vous ne souhaitez pas inclure doivent toujours commencer par " local " ou " nochk " ou quelque chose comme ça.

Ensuite, vous listez tous les champs qui commencent par celui-ci (le code n’est alors pas si laid).

Je ne doute pas que ce soit un peu plus lent. Vous devez décider si vous souhaitez échanger la facilité de mise à jour contre la vitesse d'exécution.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top