Część 0
Część 1
Część 2
Część 3
Część 4
Część 5
Część 6
Część 7
Część 8

Piątek, a zatem słuchamy Listy w Trójce. Przed nami ostatnia z zasad Jeff’a Bay’a

Nie używaj getterów/setterów/własności

Niewątpliwie jest to najbardziej obiektowa z zasad. By zrozumieć o co w niej chodzi porównajmy takie oto dwie klasy

Listing 1. klasa bardziej „obiektowa”

class Chłopiec{
	
	private String imię;
	
	private String nazwisko;
	
	private String wiek;

	public String getImię() {
		return imię;
	}

	public void setImię(String imię) {
		this.imię = imię;
	}

	public String getNazwisko() {
		return nazwisko;
	}

	public void setNazwisko(String nazwisko) {
		this.nazwisko = nazwisko;
	}

	public String getWiek() {
		return wiek;
	}

	public void setWiek(String wiek) {
		this.wiek = wiek;
	}
	
}

Listing 2. klasa mniej „obiektowa”

class Dziewczynka {
	
	public String imię;
	
	public String nazwisko;
	
	public String wiek;

}

Czym różnią się te dwie klasy?

Obiektowość to nie metody

Pomijając różne specyficzne elementy języka związane z współbieżną zmianą stanu obiektów tych klas to te dwa twory niczym się nie różnią. Mogę jednak założyć się, że jeżeli pokażemy je „nagie i bezbronne” to pierwsza z nich zostanie zakwalifikowana jako bardziej obiektowa. Druga będzie mniej obiektowa. Dlaczego? Jakoś tak się utarło, że o obiektowości decydują metody.
W Javie jest to szczególnie potęgowane przez specyfikację Java Beans:

Properties are always accessed via method calls on their owning object. For readable properties
there will be a getter method to read the property value. For writable properties there will be a
setter method to allow the property value to be updated.

Cóż… Ma to pewien cel i jest tu pewien pomysł, ale tylko gdy przyjrzymy się tej specyfikacji jako całości (szczególnie z obsługa zdarzeń i wzorcem obserwatora). Niestety jak to w życiu bywa, ktoś z całego dokumentu wyciągnął jeden fragment i tak oto znaleźliśmy się w życi.

Czym jest obiektowość?

Obiektowość to przede wszystkim enkapsulacja. Inaczej mówiąc obiekty nie powinny ujawniać swojego wnętrza, a jedynie udostępniać pewien interfejs, który będzie odwzorowywał operacje dokonywane przez obiekt. Oczywiście może się tak zdarzyć, że operacja polega na ustawieniu jakiejś wartości obiektu. Jednak są to specyficzne przypadki. W żadnym wypadku nie należy pozwalać na swobodną manipulację właściwościami obiektu.

Co zamiast?

Skoro nie powinniśmy mieć możliwości swobodnej manipulacji jak tworzyć i konfigurować obiekty? Z każdym z elementów zajmiemy się z osobna.

Settery

Generalnie obiekty powinny być niezmienne. Ma to wiele zalet. Najważniejszą jest możliwość swobodnego programowania współbieżnego bez obawy, że wątki wzajemnie pozmieniają sobie dane. W takim wypadku settery zostaną automatycznie wyrugowane przez mechanizmy języka wymuszające ustawienie pól w konstruktorze.
Jeżeli jednak z jakiegoś powodu nie możemy użyć pól finalnych to należy ustawić je z użyciem jakiegoś kontenera DI. Zazwyczaj mamy dostęp do tego typu bibliotek i nie ma problemów z ich wykorzystaniem.
Co jednak gdy z jakiś powodów kontener DI nie jest dostępny? W takim wypadku zostaje nam wykorzystanie wzorca fabryki wzbogaconego o użycie refleksji.

Właściwości

Unikamy sytuacji kiedy zachowanie obiektu zależy od właściwości. To właściwość powinna dostarczać odpowiedniego zachowania. Przykładowo w poprzedniej części mówiłem, że opakowane kolekcje powinny dostarczać metod „kontekstowych”. Podobnie w trzeciej części gdzie mowa była o opakowaniu typów prymitywnych wspominałem o możliwości zamknięcia w typie opakowującym pewnych prostych operacji np. walidacji czy operacji arytmetycznych.

Gettery

Najbardziej kłopotliwy element. Koniec końców zawsze musimy gdzieś pokazać uzyskaną wartość. Najprościej jest przesłonić toString. Nie zawsze się to da zrobić. O ile zadziała to w przypadku prostych obiektów to już obiekty złożone stanową duży problem. Pewnym rozwiązaniem może być użycie odpowiedniego prezentera, czyli wzorca MVP. Prezenter może dostać się do obiektu via refleksja względnie odpowiednio zinterpretować wyniki z metody toString. Wzorzec ten ma w tym przypadku wiele zalet. Jeżeli jednak chcemy zrobić coś małego to można użyć czegoś co roboczo nazywam obiektem „samopezentującym się”. W takim przypadku obiekt posiada metodę, do której można przekazać docelowe miejsce prezentacji np. strumień i tyle. Jest to rozwiązanie o tyle wygodne, że czasami chcemy ograniczyć możliwość prezentacji obiektu. W takim wypadku metoda pełni rolę swoistego „pasa cnoty” ponieważ ogranicza kanały prezentacji.

Podsumowanie

Uff… przebrnąłem. Nie było łatwo. Paweł Lipiński w komentarzu do siódmej części zwrócił uwagę, że są to przede wszystkim pewne heurystyki. W części zerowej wspomniałem, że zasady te są świetne jeżeli chcemy refaktoryzować kod. Są też doskonałym narzędziem do ćwiczenia. Zatem czy stosować zasady Jeff’a Bay’a? Moim zdaniem warto dążyć do kodu napisanego zgodnie z tymi zasadami. Ponieważ kod taki promuje przemyślane konstrukcje, pozwala na odnalezienie potencjalnie niebezpiecznych fragmentów i karkołomnych konstrukcji w architekturze oraz wymusza pisanie testów. Ja napisałem projekt zgodny z tymi zasadami mający około 1000 linii. Teraz czas byś usiadł i napisał swój projekt.