Uwielbiam to pytanie! Głównie dlatego, że prawie nigdy nie ma na nie odpowiedzi ani odpowiedzi źle. To tak, jakby nikt jeszcze tego nie rozgryzł. Dziewicze terytorium :)
Po pierwsze, nawet nie myśl o używaniu equals
. Kontrakt equals
, zgodnie z definicją w javadoc, jest relacją równoważności (zwrotną, symetryczną i przechodnią), a nie relacją równości. W tym celu musiałby być również antysymetryczny. Jedyną implementacją equals
tego jest (lub kiedykolwiek mogłaby być) prawdziwa relacja równości to ta w java.lang.Object
. Nawet jeśli kiedyś equals
porównywałeś wszystko na wykresie, ryzyko zerwania umowy jest dość wysokie. Jak zauważył Josh Bloch w Effective Java , kontrakt równych jest bardzo łatwy do zerwania:
„Po prostu nie ma sposobu na rozszerzenie instancji klasy i dodanie aspektu przy zachowaniu kontraktu równości”
Poza tym, po co tak naprawdę metoda boolowska? Nie sądzisz, że byłoby miło ująć wszystkie różnice między oryginałem a klonem? Zakładam również, że nie chcesz przejmować się pisaniem / utrzymywaniem kodu porównawczego dla każdego obiektu na wykresie, ale raczej szukasz czegoś, co będzie skalowane wraz ze źródłem, gdy zmienia się w czasie.
Soooo, to, czego naprawdę chcesz, to jakieś narzędzie do porównywania stanów. Sposób implementacji tego narzędzia zależy w rzeczywistości od charakteru modelu domeny i ograniczeń wydajności. Z mojego doświadczenia wynika, że nie ma ogólnej magicznej kuli. I to będzie to powolne przez dużą liczbę iteracji. Ale jeśli chodzi o testowanie kompletności operacji klonowania, wykona to zadanie całkiem dobrze. Dwie najlepsze opcje to serializacja i odbicie.
Niektóre problemy, które napotkasz:
- Kolejność kolekcji: czy dwie kolekcje należy uważać za podobne, jeśli zawierają te same przedmioty, ale w innej kolejności?
- Które pola ignorować: przejściowe? Statyczny?
- Równoważność typów: czy wartości pól powinny być dokładnie tego samego typu? A może jedno może przedłużyć drugie?
- Jest więcej, ale zapominam ...
XStream jest dość szybki i w połączeniu z XMLUnit wykona zadanie w zaledwie kilku wierszach kodu. XMLUnit jest fajny, ponieważ może zgłosić wszystkie różnice lub po prostu zatrzymać się na pierwszym, który znajdzie. A jego dane wyjściowe obejmują ścieżkę xpath do różnych węzłów, co jest miłe. Domyślnie nie zezwala na nieuporządkowane kolekcje, ale można je tak skonfigurować. Wstrzyknięcie specjalnego modułu obsługi różnic (nazywanego aDifferenceListener
modułu ) pozwala określić sposób radzenia sobie z różnicami, w tym ignorowanie kolejności. Jednak gdy tylko zechcesz zrobić cokolwiek poza najprostszym dostosowaniem, napisanie staje się trudne, a szczegóły są zwykle powiązane z określonym obiektem domeny.
Osobiście wolę używać refleksji, aby przeglądać wszystkie zadeklarowane pola i analizować każde z nich, śledząc różnice. Słowo ostrzeżenia: nie używaj rekursji, chyba że lubisz wyjątki przepełnienia stosu. Trzymaj rzeczy w zakresie ze stosem (użyj plikuLinkedList
lub coś). Zwykle ignoruję pola przejściowe i statyczne oraz pomijam pary obiektów, które już porównałem, więc nie kończę w nieskończonych pętlach, jeśli ktoś zdecydował się napisać samoodwołujący się kod (jednak zawsze porównuję prymitywne opakowania bez względu na wszystko , ponieważ te same referencje obiektów są często używane ponownie). Możesz skonfigurować rzeczy z góry, aby ignorować porządkowanie kolekcji i ignorować specjalne typy lub pola, ale ja lubię definiować moje zasady porównywania stanów na samych polach za pomocą adnotacji. Właśnie do tego, IMHO, służyły adnotacje, aby metadane o klasie były dostępne w czasie wykonywania. Coś jak:
@StatePolicy(unordered=true, ignore=false, exactTypesOnly=true)
private List<StringyThing> _mylist;
Myślę, że to naprawdę trudny problem, ale całkowicie możliwy do rozwiązania! A kiedy już masz coś, co działa dla Ciebie, jest to naprawdę bardzo przydatne :)
Więc powodzenia. A jeśli wymyślisz coś, co jest po prostu geniuszem, nie zapomnij o tym!