Amo questa domanda! Soprattutto perché non viene quasi mai risposto o risposto male. È come se nessuno l'avesse ancora capito. Territorio vergine :)
Prima di tutto, non pensare nemmeno all'utilizzo equals
. Il contratto di equals
, come definito nel javadoc, è una relazione di equivalenza (riflessiva, simmetrica e transitiva), non una relazione di uguaglianza. Per questo, dovrebbe anche essere antisimmetrico. L'unica implementazione di equals
ciò è (o potrebbe mai essere) una vera relazione di uguaglianza è quella in java.lang.Object
. Anche se hai usato equals
per confrontare tutto nel grafico, il rischio di rompere il contratto è piuttosto alto. Come ha sottolineato Josh Bloch in Effective Java , il contratto di uguaglianza è molto facile da rompere:
"Semplicemente non c'è modo di estendere una classe istanziabile e aggiungere un aspetto preservando il contratto uguale"
Inoltre, a cosa serve davvero un metodo booleano? Sarebbe bello incapsulare effettivamente tutte le differenze tra l'originale e il clone, non credi? Inoltre, presumo qui che tu non voglia essere disturbato dalla scrittura / manutenzione del codice di confronto per ogni oggetto nel grafico, ma piuttosto stai cercando qualcosa che si ridimensionerà con la fonte mentre cambia nel tempo.
Quindi, quello che vuoi veramente è una sorta di strumento di confronto statale. Il modo in cui questo strumento viene implementato dipende in realtà dalla natura del modello di dominio e dalle limitazioni delle prestazioni. Nella mia esperienza, non esiste una pallottola magica generica. E sarà lento per un gran numero di iterazioni. Ma per testare la completezza di un'operazione di clonazione, farà abbastanza bene il lavoro. Le tue due migliori opzioni sono la serializzazione e la riflessione.
Alcuni problemi che incontrerai:
- Ordine di raccolta: due raccolte devono essere considerate simili se contengono gli stessi oggetti, ma in un ordine diverso?
- Quali campi ignorare: transitori? Statico?
- Equivalenza del tipo: i valori dei campi devono essere esattamente dello stesso tipo? O va bene che uno estenda l'altro?
- C'è di più, ma dimentico ...
XStream è abbastanza veloce e combinato con XMLUnit farà il lavoro in poche righe di codice. XMLUnit è carino perché può segnalare tutte le differenze o semplicemente fermarsi alla prima che trova. E il suo output include l'xpath ai diversi nodi, il che è carino. Per impostazione predefinita, non consente raccolte non ordinate, ma può essere configurato per farlo. L'iniezione di un gestore delle differenze speciale (chiamato a DifferenceListener
) consente di specificare il modo in cui si desidera gestire le differenze, incluso l'ignorare l'ordine. Tuttavia, non appena si desidera fare qualcosa oltre la più semplice personalizzazione, diventa difficile scrivere ei dettagli tendono ad essere legati a un oggetto di dominio specifico.
La mia preferenza personale è quella di utilizzare la riflessione per scorrere tutti i campi dichiarati e approfondire ciascuno di essi, monitorando le differenze man mano che procedo. Parola di avvertimento: non utilizzare la ricorsione a meno che non ti piacciano le eccezioni di overflow dello stack. Mantieni le cose nell'ambito con uno stack (usa un fileLinkedList
o qualcosa). Di solito ignoro i campi transitori e statici e salto le coppie di oggetti che ho già confrontato, quindi non finisco in cicli infiniti se qualcuno decide di scrivere codice autoreferenziale (Tuttavia, confronto sempre i wrapper primitivi, non importa cosa , poiché gli stessi riferimenti oggetto vengono spesso riutilizzati). Puoi configurare le cose in anticipo per ignorare l'ordinamento della raccolta e per ignorare tipi o campi speciali, ma mi piace definire le mie politiche di confronto dello stato sui campi stessi tramite annotazioni. Questo, IMHO, è esattamente ciò a cui erano destinate le annotazioni, per rendere disponibili i metadati sulla classe in fase di esecuzione. Qualcosa di simile a:
@StatePolicy(unordered=true, ignore=false, exactTypesOnly=true)
private List<StringyThing> _mylist;
Penso che questo sia in realtà un problema davvero difficile, ma totalmente risolvibile! E una volta che hai qualcosa che funziona per te, è davvero, davvero, utile :)
Quindi buona fortuna. E se ti viene in mente qualcosa di puro genio, non dimenticare di condividerlo!