Jaki jest sens getterów i setterów dla zmiennych prywatnych?

0

Załóżmy, że mamy prostą klasę Point. Ma dwie zmienne private double x, y. Dwa proste konstruktory, jeden bez argumentów, drugi dla dwóch double'ów i metody publiczne setX, setY, getX, getY, toString.

Z takim podejściem spotykam się w wielu kursach w internecie, tutorialach, w książkach. Czy jest w tym jakiś sens poza purytanizmem i nadgorliwością? Skoro zmienne są private to chyba po to, żeby były niewidoczne na zewnątrz. Skoro sami tworzymy takie mechanizmy by potem można je było zmienić, zedytować, zwrócić to czy nie prościej byłoby po prostu zadeklarować je jako public?

Z krytyką takiego podejścia spotkałem się w książce Martina "Czysty kod".

`dodanie znaczników ``` - @furious programming

1

Takie podejście pozwala przedefiniować gettery/settery.

Popularny przykład z javy:
Tworzysz klasę encji JPA dla tabeli Pracownik. Posiada ona pola oznaczone jako EAGER LAZY czyli nie są Od razu pobierane. Gdy używasz obiektu Pracownik, nie zawsze potrzebujesz listy jego podwładnych, czyli nie zawsze jest sens wykonywania dość kosztownej operacji pobierania całej listy. Dlatego gdy operujesz na encji to nie jest to obiekt Twojej klasy, tylko obiekt proxy który po niej dziedziczył i udekorował gettery/settery. Gdy pobierasz imię pracownika to wywoływany jest bazowy getter. Gdy jednak chcesz listę podwładnych to najpierw wartość jest pobierana z bazy danych a dopiero później zwracana.

Inny popularny przykład leniwej inicjalizacji to Singleton który nie jest tworzony dopóki pierwszy raz go nie zażądasz. Bardzo oczywisty przykład, ale zauważ że tam też masz getter który nie zwraca po prostu wartości tylko robi coś w międzyczasie.

0

W przypadku tak elementarnych typów publiczne pola są super.
Do przełknięcia są atrybuty rodem z C#.

Za używanie getterów/setterów w takim przypadku powinno się biczować.

4

W Javie 8 obecność getterów umożliwi odwołanie się do nich w stylu: Point::getX. To da możliwość np mapowania strumieni. Oczywiście dałoby się i bez metod handle na gettera, ale byłoby brzydsze.

Gettery i settery są częścią konwencji JavaBeans: http://en.wikipedia.org/wiki/JavaBeans
Ta konwencja może być stosowana np w Swingu, gdzie w setterze jest logika informująca, że obiekt się zmienił, a to umożliwi eleganckie (?) zbindowanie obiektu z GUI.
Opakowanie pól w gettery i settery umożliwia także zmianę zachowania obiektu w locie, np tak jak wspomniał poprzednik, może umożliwić leniwe ładowanie obiektów z bazy dzięki tworzeniu podklas z ekstra logiką w akcesorach.

Istnieje http://projectlombok.org/ który robi gettery, settery, toStringi, hashCode, equalsy, konstruktory, itd z automatu.

Powszechne mniemanie jest takie, że w zdecydowanej większości przypadków akcesory i reszta boliterplate jest niepotrzebna, ale i tak się to robi. Myślę, że użycie Lomboka jest najlepszym kompromisem.

3

JavaBeans wspomniane przez @Wibowit to pewna konwencja, która rozpełzła się po języku i jest bezkrytycznie stosowana. W dodatku wiele frameworków opiera się o przedstawione w niej rozwiązania nazewnicze, ale już nie o pewne elementy, które za nimi idą.

Jeżeli:

  • nie potrzebujesz powiadamiania o zmianie stanu obiektu (binding)
  • nie używasz proxy w celu dodania logiki do setterów/getterów (lazy loading, jakieś dziwne walidacje)
  • nie musisz zmieniać stanu obiektu

to nie twórz tych metod. Jak potrzebujesz odczytywać wartości z pól to zrób je publiczne i finalne. Ustawiane w konstruktorze. Coś na wzór struktury z C.

Jeżeli z jakiegoś powodu chcesz mieć tylko mutowalność to zobacz czy ona jest konieczna. Dodawanie settera tylko dlatego, że chcemy zmieniać stan obiektu jest podejrzane. Można to zrobić lepiej przez np. STM z Clojure.

1 użytkowników online, w tym zalogowanych: 0, gości: 1