Czysty kod vs. java.util.Map

Pewnego dnia pięknego, robiąc przegląd kodu natknąłem się na sygnaturę metody:

public Map<String, Map<String, List<String>>> getProvinces();

Konia z rzędem temu, kto na podstawie takiej sygnatury jest w stanie powiedzieć, co poeta miał na myśli. A cóż ja, jako czytelnik miałem zrobić? Niestety nie dysponowałem źródłami. Testy, debugger do ręki – aż po godzinie odkryłem cóż tam siedzi w środku:

Map<String, Map<String, List<String>>> provinces = getProvinces();

    for(String province  : provinces.keySet()) {
      for(String city : provinces.get(province).keySet()) {
        for(String postalCode : provinces.get(province).get(city)) {
          log.info(province + " -> "+ city + " -> "+ postalCode);
        }
      }
    }

Wiem że bez pakietu java.util.* trudno cokolwiek sensownego zaprogramować, ale beztroskie korzystanie z takich wielokrotnie zagnieżdżonych struktur danych potrafi zdrowo namieszać.

Nic nie widać, nic nie słychać, za to czuć smród

Bez czasochłonnej analizy implementacji nie wiadomo co siedzi w środku. Gdy taka struktura danych pełni rolę odpowiedzi, to nie jesteśmy w stanie bezpiecznie jej interpretować, zaś jeśli w ten sposób ktoś zaprojektował argumenty metody to sytuacja jest równie nieciekawa. Skazani jesteśmy na empiryczne odkrywanie tego co skrywa przed nami implementacja. Zwykle takie doświadczenie przyjemne nie jest, wszak chcemy tylko skorzystać z kawałka istniejącego kodu by zaoszczędzić sobie czasu, bebechy powinny nas guzik interesować.

Utrata tożsamości

Często widzę kod, mający wyraźnie zarysowane obiekty biznesowe z jasno określoną odpowiedzialności, które gdzieś między kolejnymi warstwami aplikacji miękną i roztapiają się niczym kostka masła pozostawiona na słońcu. Tyle że zamiast tłustej plamy otrzymujemy tłustą Map’ę Map. To znak że albo architektura naszej aplikacji traci swą pojemność, albo że nie mamy odwagi stworzyć kilku prostych ValueObject’ów, drżąc ze strachu przed utratą wyimaginowanej wydajności. Taka mapa jest jak bohater filmu „Pamięć absolutna” – kryje w sobie wspomnienia zepchnięte głęboko do podświadomości.

Koniec z ukrywaniem „oczywistych oczywistości”

To ValueObject’y przychodzą na ratunek. Rezygnując z typów prostych może i stworzymy kilka klas więcej, ale zyski przeważają szalę:

class Province {

  public final String name;

  public Province(String name) {
    this.name = name;
  }
}

class City {
  public final String name;

  public City(String name) {
    this.name = name;
  }
}

class PostalCode {
  public final String value;

  public PostalCode(String value) {
    this.value = value;
  }
}

i możemy zrefaktoryzować naszą przebrzydłą Map’ę:

public Map<Province, Map<City, List<PostalCode>>> getProvinces();

Czujecie różnicę? Taka sygnatura wygląda o niebo lepiej i jest czytelna już na pierwszy rzut oka. Jest jak wzrok przywrócony ślepcowi. Jak erekcja u impotenta. Nikt nie musi zastanawiać się nad strukturą danych – wszystko jest podane jak na tacy. Jedyne o co trzeba zadbać, to poprawne przysłonięcie metod hashcode()/equals() w klasach Province/City/PostalCode. Zwykle w przypadku prostych ValueObject’ów wystarczy standardowe HashCodeBuilder/EqualsBuilder ze stajni apachowych commons-lang:

  @Override
  public int hashCode() {
   return HashCodeBuilder.reflectionHashCode(this);
  }

  @Override
  public boolean equals(Object obj) {
   return EqualsBuilder.reflectionEquals(this, obj);
  }

Epilog

Uporawszy się z problemem, otarłem pot z czoła i z czystej ciekawości postanowiłem sprawdzić w historii svn’a któż to taki przysporzył mi tyle dodatkowej pracy. Chciałem wiedzieć kogo winić za stracony czas. O ironio! Twórcą metody getProvinces() byłem ja sam, własnoręcznie sobie zgotowałem ten los kilka lat temu. Co po raz kolejny potwierdza że surowa Map’a to kiepski przyjaciel Programisty.

Opublikowano w api, clean code, code review, java, legacy code, Value Object | 2 komentarzy

Czysty kod – studium przypadku

Każdy kod ma swój zapach. Czasem pachnie jest jak poranny nieświeży oddech, czasem zalatuje jak spocona starsza pani w tramwaju, a czasem śmierdzi pijackimi rzygowinami. Wyczulony węch to u programisty bardzo przydatna cecha, która może nie ratuje życia w spektakularny sposób, ale po pewnym czasie okazuje się że ma niebagatelny wpływ na jakość tworzonego oprogramowania. Czytaj dalej

Opublikowano w clean code, code review, java | 2 komentarzy

Przeglądy kodu – mój punkt widzenia

Przez cały dzień czytasz, analizujesz, testujesz, czytasz dokumentację – tylko po to by napisać dwie, góra trzy linijki kodu rozwiązującego problem. Brzmi znajomo? Z pewnością tak. W dojrzałych (vel zapuszczonych) projektach dominującą kompetencją programisty staje się nie będące największą przyjemnością klepanie kodu, lecz umiejętność przedzierania się przez kod wyklepany przez innych programistów. Takie czasy i nic na to nie poradzimy, trzeba poszukiwać technik które pomogą nam się w tym wszystkim odnaleźć. Jedną z podstawowych technik jakie mamy do dyspozycji są przeglądy kodu (code review). W regularnie przeprowadzanych przeglądach tkwi duża moc i tylko od nas zależy czym one będą: narzędziem tortur czy też użytecznym narzędziem codziennej pracy? Czytaj dalej

Opublikowano w code review, java | Dodaj komentarz

Anatomia strachu

Strach, jako jedna z naszych podstawowych cech pierwotnych towarzyszy nam każdego dnia. To siła napędowa i hamulcowa zarazem. To pierwsze uczucie jakie nas ogarnia gdy stykamy się z Legacy Code. Strach paraliżuje. Tylko zdeterminowany i wyposażony w solidny warsztat Programista jest w stanie go pokonać. Czytaj dalej

Opublikowano w legacy code | Jeden komentarz

Dlaczego nie lubię Class.newInstance()

Gdy tylko widzę w kodzie mnogość konstrukcji Class.newInstance() zaczynam być czujny. Dla mnie jest to zapach, a nawet smrodek sygnalizujący bajzel wymagający zachowania daleko idącej ostrożności. Czytaj dalej

Opublikowano w java | Dodaj komentarz

Domain-Driven Design – Entity

Skoro Value Object (VO) został pokrótce omówiony, czas na kolejny artefakt którego cykl życia jest nierozerwalnie spleciony z VO. Nadszedł czas Encji (Entity). Czytaj dalej

Opublikowano w DDD, Entity, Repository, Specification, Value Object | Dodaj komentarz

Domain-Driven Design – Value Object

Nikogo do Domain-Driven Design nie chcę namawiać, nie mnie nawracać rzesze niewiernych. Wszak ewangelizatorów oraz ich akolitów jest mnóstwo. Uważam że na każdego przyjdzie czas i odpowiedni projekt. Każdy musi sam odnaleźć swoją drogę którą kroczyć będzie ku chwale wysokiej jakości kodu. Czytaj dalej

Opublikowano w DDD, Entity, Repository, Specification, Value Object | Jeden komentarz

Do czego można wykorzystać enumy?

Jedną z ciekawszych cech które są z nami począwszy od jdk1.5 jest wsparcie dla typów wyliczeniowych. Dawno dawno temu, trzeba było stosować takie wynalazki jak org.apache.commons.lang.enums.Enum lub samodzielnie (czytaj: nieudolnie) wymyślać koło od nowa. Czytaj dalej

Opublikowano w java | Dodaj komentarz

Co refaktoryzować panie premierze? Co refaktoryzować?

Dobre pytanie. Każda refaktoryzacja powinna mieć swój jasno określony cel. Niestety Programiści mają tendencję do refaktoryzowania miejsc które są im wygodne, co w szerszej perspektywie prawie nigdy nie przynosi wymiernych korzyści. Pół biedy jeśli taka refaktoryzacja przejdzie bez echa, gorzej jeśli oprócz straty czasu stracimy z oczu horyzont zdrowego rozsądku, a aplikacja zerwie się z kotwicy w porcie „stabilność” i zacznie niekontrolowany dryf w kierunku portu „katastrofa”. Więc co warto refaktoryzować? Czytaj dalej

Opublikowano w java, legacy code | Dodaj komentarz

Quo Vadis programisto?

Współczesny świat programistów nieustannie się zmienia. Biblioteki i frameworki o których jeszcze kilka lat temu było głośno w blogosferze i na branżowych konferencjach, dzisiaj są już passé i tylko twórcy o nich pamiętają, czasem łezkę uronią… Na szczęście czas leczy rany. Czytaj dalej

Opublikowano w java | Jeden komentarz