J'adore cette question! Principalement parce qu'il est rarement répondu ou mal répondu. C'est comme si personne ne l'avait encore compris. Territoire vierge :)
Tout d'abord, ne pensez même pas à utiliser equals
. Le contrat de equals
, tel que défini dans le javadoc, est une relation d'équivalence (réflexive, symétrique et transitive), pas une relation d'égalité. Pour cela, il faudrait aussi qu'il soit antisymétrique. La seule implémentation de equals
cela est (ou pourrait être) une vraie relation d'égalité est celle dans java.lang.Object
. Même si vous avez utilisé equals
pour comparer tout dans le graphique, le risque de rupture du contrat est assez élevé. Comme l'a souligné Josh Bloch dans Effective Java , le contrat d'égal à égal est très facile à rompre:
"Il n'y a tout simplement aucun moyen d'étendre une classe instanciable et d'ajouter un aspect tout en préservant le contrat égal"
En plus à quoi sert une méthode booléenne de toute façon? Ce serait bien d'encapsuler réellement toutes les différences entre l'original et le clone, vous ne pensez pas? De plus, je suppose ici que vous ne voulez pas être dérangé par l'écriture / la maintenance du code de comparaison pour chaque objet du graphique, mais plutôt que vous recherchez quelque chose qui s'adapte à la source à mesure qu'elle change au fil du temps.
Soooo, ce que vous voulez vraiment, c'est une sorte d'outil de comparaison d'état. La manière dont cet outil est mis en œuvre dépend vraiment de la nature de votre modèle de domaine et de vos restrictions de performances. D'après mon expérience, il n'y a pas de solution magique générique. Et il sera lent sur un grand nombre d'itérations. Mais pour tester l'exhaustivité d'une opération de clonage, cela fera assez bien le travail. Vos deux meilleures options sont la sérialisation et la réflexion.
Quelques problèmes que vous rencontrerez:
- Ordre des collections: deux collections doivent-elles être considérées comme similaires si elles contiennent les mêmes objets, mais dans un ordre différent?
- Quels champs ignorer: transitoire? Statique?
- Équivalence de type: les valeurs de champ doivent-elles être exactement du même type? Ou est-ce que l'un peut prolonger l'autre?
- Il y a plus, mais j'oublie ...
XStream est assez rapide et combiné avec XMLUnit fera le travail en seulement quelques lignes de code. XMLUnit est agréable car il peut signaler toutes les différences, ou simplement s'arrêter à la première qu'il trouve. Et sa sortie inclut le xpath vers les différents nœuds, ce qui est bien. Par défaut, il n'autorise pas les collections non ordonnées, mais il peut être configuré pour le faire. Injection d'un gestionnaire de différence spécial (appelé unDifferenceListener
) vous permet de spécifier la façon dont vous souhaitez traiter les différences, y compris en ignorant l'ordre. Cependant, dès que vous voulez faire quelque chose au-delà de la personnalisation la plus simple, cela devient difficile à écrire et les détails ont tendance à être liés à un objet de domaine spécifique.
Ma préférence personnelle est d'utiliser la réflexion pour parcourir tous les champs déclarés et explorer chacun d'eux, en suivant les différences au fur et à mesure. Mot d'avertissement: n'utilisez pas la récursivité sauf si vous aimez les exceptions de dépassement de capacité de pile. Gardez les choses dans la portée avec une pile (utilisez unLinkedList
ou quelque chose). J'ignore généralement les champs transitoires et statiques, et je saute les paires d'objets que j'ai déjà comparées, donc je ne me retrouve pas dans des boucles infinies si quelqu'un décide d'écrire du code auto-référentiel (Cependant, je compare toujours les wrappers primitifs quoi qu'il arrive , puisque les mêmes références d'objet sont souvent réutilisées). Vous pouvez configurer les choses à l'avance pour ignorer l'ordre des collections et pour ignorer les types ou les champs spéciaux, mais j'aime définir mes politiques de comparaison d'état sur les champs eux-mêmes via des annotations. Ceci, à mon humble avis, est exactement ce à quoi les annotations étaient destinées, pour rendre les métadonnées sur la classe disponibles au moment de l'exécution. Quelque chose comme:
@StatePolicy(unordered=true, ignore=false, exactTypesOnly=true)
private List<StringyThing> _mylist;
Je pense que c'est en fait un problème vraiment difficile, mais totalement résoluble! Et une fois que vous avez quelque chose qui fonctionne pour vous, c'est vraiment, vraiment, pratique :)
Alors bonne chance. Et si vous trouvez quelque chose de pur génie, n'oubliez pas de partager!