… ale niestety konieczny. Tak się w życiu złożyło, że technologie popularne i zachwalane przez wszystkich jako przykłady doskonałego oprogramowania mało mają w sobie z rzeczywiście dobrego softu. Względnie ich mechanizmy, które pozwalają na odpowiednie zastosowanie zasad są zbyt skomplikowane.
Sławek Sobótka opisywał ten problem w dość ogólny sposób. Ja natknąłem się dziś na jego realny egzemplarz.

Dziedzina

Piszę sobie dla wprawy narzędzie do tworzenia testów. Ma pozwalać na zarządzanie testami. Ich tworzenie, dodawanie i usuwanie pytań oraz odpowiedzi. Generalnie związki pomiędzy obiektami wyglądają w następujący sposób. Test ma listę pytań, a każde pytanie ma listę odpowiedzi. Można to dość prosto przedstawić za pomocą takiego oto diagramu:
Model idealny
Jak widać, o ile nie walnąłem się w strzałkach, model idealny jest prosty. Olałem metody dostępowe na rzecz upublicznienia pól. Należy pamiętać, że diagram UML to tylko sugestia i sposób realizacji niektórych oznaczeń może być różny w zależności od języka. Jedynymi metodami, które pozostawiłem są metody pozwalające na pobieranie listy pytań i odpowiedzi. Kwestia praktyczna.

Problem

Prawdziwy problem polega na tym, że chcąc użyć tego modelu musimy go gruntownie przebudować. Niestety wymagania technologii takich jak JPA czy ogólniej baz danych wymuszają na nas zastosowanie pewnych rozwiązań zaśmiecających kod.
Pierwszym z nich jest konieczność dodania identyfikatorów do obiektów. Niby ułatwia to życie, ale nie jest niezbędne. Tak naprawdę jeżeli używamy Javy i jej mechanizmów składowania (serializacja i zapis obiektów do pliku) nie ma potrzeby używania żadnych identyfikatorów. Mamy je gratis tworząc obiekt. W tym miejscu poraz pierwszy zaśmiecamy kod. Nie jest to takie straszne, ale jak to ze śmieciami bywa ciągnie za sobą nieprzyjemny zapach…
Drugim punktem, który musimy spełnić by móc składować obiekty w bazie danych jest przymus stworzenia metod dostępowych dla wszystkich pól, które będą składowane. Metody muszą być publiczne co od razu powoduje chęć ich używania przez klientów wykorzystujących nasz kod. Dodatkowo niszczy enkapsulację i hermetyzację obiektów ponieważ pola, które mają być prywatne lub tylko do odczytu stają się dostępne dzięki publicznym metodom get i set. Chcąc uniknąć powielania kodu musimy też częściowo zrezygnować z samoopisujących się nazw metod. Po tych zmianach nasz diagram wygląda w ten sposób:
Model idealny po poprawkach
Jest już źle. Dodaliśmy masę nowych metod w dodatku pola tylko do odczytu stały się też polami do zapisu. Metody addQuestion(Question), removeQuestion(Question),addAnswer(Answer), removeAnswer(Answer) gubią się ponieważ możemy użyć metod „masowych” getQuestions(), setQuestions(List),getAnswers(), setAnswers(List). Tracimy tym samym kontrolę nad tym co dokładnie znajduje się na naszych listach. Zaraz będzie jeszcze gorzej 🙂
Musimy powiązać nasze obiekty. Niech będą to relacje jednokierunkowe wiele do jednego. Tak będzie najprościej i jednocześnie najmniej śmiecia wyprodukujemy. Nasze finałowe rozwiązanie wygląda w następujący sposób:
Model idealny finalnie
Jak widać chęć użycia JPA wymusiła na nas dodanie dużej ilości zbędnego kodu. Jeżeli jeszcze weźmiemy pod uwagę, że 99,99% programistów, którzy dostaną ten diagram wykorzysta adnotacje do implementacji związków i mapowań to kaplica. Podobno JPA jest nieinwazyjna…

Podsumowanie

Nasuwają się pewne wnioski:

  • Nie ma technologii nieinwazyjnych.
  • Wprowadzenie technologii wymusza zmiany na poziomie modelu biznesowego, a nie tylko kodu
  • Zakres zmian zależy tylko od programisty. Może użyć XMLa zamiast adnotacji.

To ostatnie zdanie ma na celu pokazanie, że adnotacje nie są idealnym rozwiązaniem naszych problemów, nie dość, że jary z nimi muszą wlec się a projektem to jeszcze zmiana wymaga powtórnej kompilacji kodu. Można oczywiście nadpisać ustawienia adnotacji w XMLu, ale od razu dostaniemy niespójność konfiguracji. Ot taki gratis…