czwartek, 27 września 2007

Twierdzenie Defecińskiego (generic toString())

Przy pisaniu jUnit-ów nadpisałem sobie metodę assertEquals(MyObject expected, MyObject actual) dla moich obiektów (właściwie to używana jest assertEquals(Object, Object) a nadpisana jest metoda equals). Jeśli ta assercja jest nieprawdziwa to dostaje się coś w rodzaju expected: MyObject@23123 but got MyObject@323232Oczywiście dlatego, że używana jest metoda toString() z Object.

Napisanie własnej metody toString() jest dobrym rozwiązaniem, ale jeśli mamy kilka różnych obiektów a obiekty mają dużo parametrów to pisanie swoich toString może być męczące. Jeśli dodamy jeszcze do tego to, że podczas developmentu nasz obiekt ciągle się zmienia tj. dochodzą nowe pola, stare znikają to utrzymywanie toString() zsynchronizowanego ze stanem naszej klasy jest trudne.

Pogooglałem i znalazłem szybko ToStringHelper. Oparte o refleksję wypisuje klarownie nazwę i wartości wszystkich pól w obiekcie oraz klas nadrzędnych. Działało to fajnie dopóki jedna klasa nie zmieniła się i zyskała jedno pole wskazujące na swego rodzica (w sensie referencji). Powstała pętla obiektów i toString powodował StackOverflowError :-(.

Posiedziałem nad tym troszkę i napisałem wersję, która byłaby na to odporna. Spodobało mi się, że rozwiązałem problem i chciałem "sprzedać" mój pomysł koledze. Jak wysyłałem mu maila to jeszcze raz zagooglałem o "generic to string" żeby podać link do ToStringHelper ...

... a znalazłem ReflectionToStringBuilder z commons-lang, który robił dokładnie to samo co "mój" toString (nawet ten sam output!) i także nie powodował przeciążenia stosu. Zawsze twierdziłem, że cokolwiek nie wymyślę to istnieje ktoś w sieci kto już to napisał. Myślę, że nazwę to kiedyś twierdzeniem Defecińskiego :D (jak tylko przeprowadzę dowód).

Brak komentarzy: