Blog Cargo culty v Jave: objektovo-orientované programovanie

Cargo culty v Jave: objektovo-orientované programovanie

Cargo cult

V južnom Pacifiku na ostrove Tanna žije primitívny kmeň, ktorý uctieva američana Johna Fruma. Aby ho k sebe privolali, postavili si lietadlá a obrovské vysielače. Vyzbrojili sa bajonetmi a pochodujú v šíkoch pod americkou zástavou. Presne takto to odpozorovali, keď bol John Frum na ostrove Tanna: stačí pochodovať s bajonetmi, otáčať s vysielačmi a mať pri sebe lietadlá. A stane sa zázrak! Pristane lietadlo naložené potravinami, oblečením a iným tovarom (cargo).

John však neprichádza. Možno preto, lebo lietadlá, vysielače a bajonety sú z bambusu a trávy. Takže členovia cargo cultu robia síce všetko správne, dokonale napodobňujú činnosť amerických jednotiek, ktoré kedysi na ostrove žili, ale výsledok sa nedostaví.

Mechanické napodobňovanie postupov bez porozumenia robí z programátorov členov cargo-cultu. Koľko bambusu a trávy obsahuje váš zdrojový kód?

Cargo cult objektovo-orientovaného programovania

„Každá trieda musí mať všetky svoje atribúty „private“ a k ním vygenerované get/set metódy.“

Dodržiavanie tejto zásady bez toho, aby programátori rozumeli jej dôvodom, robí z programátorov členov cargo cultu.
V niektorých situáciách je výhodnejšie používať dátové štruktúry namiesto tried. Pre ne však neplatia pravidlá o zapúzdrenosti atribútov v triedach.

Vysvetlením je objektovo-orientované programovanie

Základom objektovo-orientovaného prístupu je zodpovednosť. Aby trieda udržala zodpovednosť za svoje dáta, musí mať prístup k nim pod kontrolou – čiže dáta musia byť zapúzdrené. Preto je nutné definovať atribúty ako privátne.
Ak trieda poskytuje ku každému atribútu aj get/set metódy, k žiadnej zapúzdrenosti nedochádza. Samotná trieda potom nie je v zmysle dizajnu trieda, ale dátová štruktúra.

Trieda verzus dátová štruktúra

Trieda (class):

  • primárne definovaná cez zodpovednosť
  • obsahuje dáta, ale aj chovanie, ktorým implementuje zodpovednosť

Dátová štruktúra (record)

  • primárne definovaná cez ukladané dáta
  • obsahuje iba atribúty

Primárny rozdiel medzi triedou a dátovou štruktúrou spočíva v spôsobe návrhu. Ide o rozdiel, ktorý nemusí byť na prvý pohľad zrejmý. Mnohé triedy z objektovo-orientovaného dizajnu môžu v konečnom dôsledku skončiť s veľmi podobným zdrojovým kódom ako pri návrhu dátových štruktúr. Obvykle však ide o okrajové triedy – číselníky a pomocné pojmy.

Praktická ukážka – Osoba (Person)

Osoba má v doméne tieto atribúty:

  • registrationNumber – rodné číslo
  • birthDate – dátum narodenia
  • givenName – krstné meno
  • familyName – priezvisko

Pre účely identifikácie osoby slúži jednoznačný umelý identifikátor „personId“ typu Long.

Person ako dátová štruktúra (cargo cult)

import java.time.LocalDate;

public class Person {
	
	private Long personId;
	// rodne cislo
	private String registrationNumber;
	private LocalDate birthDate;
	private String givenName;
	private String familyName;
	
	//getters
	public Long getPersonId() {				return personId;	}
	public String getRegistrationNumber() {		return registrationNumber;}	
	public LocalDate getBirthDate() {			return birthDate;	}
	public String getGivenName() {				return givenName;	}
	public String getFamilyName() {				return familyName;	}

	// setters
	public void setBirthDate(LocalDate birthDate) {	this.birthDate = birthDate;}
	public void setGivenName(String givenName) {		this.givenName = givenName;}
	public void setFamilyName(String familyName) {	this.familyName=familyName;}
	public void setRegistrationNumber(String registrationNumber) {
		this.registrationNumber = registrationNumber;
	}	
}

(Kód je zámerne stlačený kvôli priestoru.)
Dátová štruktúra má všetky atribúty prístupné cez get/set metódy. Kód obsahuje veľa nadbytočného balastu („boilerplate code“) – sú to práve get/set metódy automaticky generované nástrojom. Z pohľadu dizajnu je lepšie dátovú štruktúru v Jave písať naozaj ako dátovú štruktúru, t.j.:

import java.time.LocalDate;

public class Person {
	
	public Long personId;
	// rodne cislo
	public String registrationNumber;
	public LocalDate birthDate;
	public String givenName;
	public String familyName;
		
}

Herézia! Atribúty triedy nesmú byť „public“! Domorodci z ostrova Tannu sú zdesení!
Presne tak: atribúty triedy musia byť private. Ale ak z pohľadu dizajnu nie je Person navrhnutá ako trieda, ale ako dátová štruktúra, tak sa na ňu pravidlá pre triedy nevzťahujú. A z pohľadu zrozumiteľnosti je tento kratší zápis bez get/set metód lepšie čitateľný. Z pohľadu používania sa nijako nelíši od triedy Person, ktorá má síce všetky atribúty private, ale zároveň prístupné cez get/set metódy.

Person ako trieda

import java.time.LocalDate;

public final class Person {
	
	private final PersonId personId;
	// rodne cislo
	private final RegistrationNumber registrationNumber;
	private LocalDate birthDate;
	private String givenName;
	private String familyName;
	
	public Person(PersonId personId, RegistrationNumber registrationNumber) {
		this.personId = personId;
		this.registrationNumber = registrationNumber;
		this.birthDate = registrationNumber.date();
	}

	public PersonId getPersonId() {		return personId;	}

	public String getRegistrationNumber() {
		return registrationNumber.text();
	}

	public String getGivenName() {		return givenName;	}
	public String getFamilyName() {		return familyName;	}
	public LocalDate getBirthDate() {	return birthDate;	}
	public void setGivenName(String givenName) {	this.givenName = givenName;}
	public void setFamilyName(String familyName) {this.familyName = familyName;	}
	
}

Pri objektovo-orientovanom dizajne si dizajnér neustále kladie otázku: „Čo je zodpovednosťou danej triedy?“ Triedy, ktoré predstavujú objekty z reálneho sveta (ako je Person), obvykle majú jednoduchú zodpovednosť: zabezpečiť dátovú konzistenciu. Táto zodpovednosť je v prípade triedy Person rozbitá na toto chovanie:

  1. Nesmie existovať objekt typu Person, ak nemá nastavený identifikátor a rodné číslo
  2. Dátum narodenia osoby musí zodpovedať nastavenému rodnému číslu
  3. Rodné číslo osoby sa nesmie zmeniť.
  4. Z bodov 1-3 vyplýva, že ani dátum narodenia osoby sa nesmie zmeniť.

Rozbíjanie zodpovednosti v objektovo-orientovanom dizajne viedlo k definovaniu triedy RegistrationNumber, ktorá v doméne reprezentuje rodné číslo. Ako vidno z návrhu, má metódy date() a text() na vrátenie dátumu z rodného čísla.

Triedu Person by bolo možné navrhnúť tak ako v prípade dátovej štruktúry – čiže s atribútom registrationNumber typu String. Tým pádom by trieda Person na seba prevzala zodpovednosti súvisiace s vyhodnocovaním rodného čísla – t.j. načítanie dátumu.

Lenže: single responsibility principle sa v dobrom dizajne neporušuje!

Formálny pohľad na rozdiely

Hlavný rozdiel medzi dátovou štruktúrou a triedou spočíva v prístupe k ich dizajnu. Rozdielny prístup v dizajne však vedie aj k formálnym odlišnostiam:

  1. Trieda má obvykle konštruktor, ktorý definuje povinné atribúty, bez ktorých nemá zmysel inštanciu triedy vytvárať.
  2. Trieda obvykle nemá všetky set-metódy, lebo niektoré atribúty sa počas životného cyklu objektu nesmú zmeniť. Povinné atribúty sú častokrát nastavené iba raz na začiatku.
  3. Trieda môže mať set-metódy doplnené o ďalšie vstupné parametre – napr. o používateľa, ktorý atribút zmenil.
  4. Trieda častokrát nemá ani všetky get-metódy, lebo niektoré atribúty slúžia iba na interné spracovanie logiky vo vnútri triedy a nie sú publikované.
  5. Niekedy majú get/set metódy v triede nastavenú nižšiu viditeľnosť, t.j. nie sú public.
  6. Niekedy sú návratové typy get-metód iné než je interný typ daného atribútu v triede (pozri registrationNumber).

Pre get/set metódy teda platí, že zatiaľ čo pri dátových štruktúrach sú tieto metódy automaticky generované (resp. neexistujú), pri objektovom dizajne je každá get/set metóda dôsledkom požiadavky v doméne, t.j. dôsledkom implementácie use-case-u alebo user-story. A signatúra metódy nemusí mechanicky zodpovedať typu atribútu.

Dátové štruktúry

Dátové štruktúry majú v programovaní svoje miesto. Nemá zmysel programovať každú množinu atribútov automaticky ako triedu. Príkladom používania dátových štruktúr sú tzv. „Data Transfer Object“. Ide o dátové štruktúry, ktoré sú súčasťou API pri programovaní REST-ových alebo SOAP-ových služieb a ich účelom je zabezpečiť jednoduchú konverziu dát na JSON alebo XML. Tieto dátové štruktúry je vhodné implementovať pomocou public atribútov, čiže bez get/set metód.

Používate JavaBeans?

Písanie get/set metód zaviedol JavaBeans štandard, ktorý sa dnes už veľmi nepoužíva. Práve get/set metódy je jediné, čo z tohto štandardu zostalo. Štandard definoval pravidlá, ktoré má programátor dodržiavať, ak píše JavaBean. Dnes sa takmer všetky triedy automaticky píšu ako JavaBean-y bez toho, aby programátori tušili, že to robia.
Takže tu je ten bambus …

 



Sumár

Používanie get/set metód je najrozšírenejší cargo cult v Jave. Ich použitie samo o sebe nie je zlé ani chybné, pokiaľ programátori rozumejú jeho účelu:

  • Pri triedach v zmysle objektovo-orientovaného dizajnu je každá get/set metóda dôsledkom používateľskej požiadavky. Tieto metódy preto nie je možné automaticky generovať pre všetky atribúty naraz.
  • Pri dátových štruktúrach je prípustné a vhodné mať všetky atribúty verejne prístupné.

Dobrý článok? Chceš dostávať ďalšie?

Už viac ako 6 200 ITečkárov dostáva správy e-mailom. Nemusíš sa báť, nie každé ráno. Len občasne.

Súhlasím so spracovaním mojich osobných údajov. ( Viac informácií. )

Tvoj email neposkytneme 3tím stranám. Posielame naňho len informácie z robime.it. Kedykoľvek sa môžeš odhlásiť.

Zdeno Jašek
Zdeno Jašek
Pracujem ako Solution Architect vo firme PosAm a programovaním sa zaoberám takmer 30 rokov. Prešiel som jazykmi Basic, Assembler, Pascal, Object Pascal, Lisp, Prolog, Magic, MUMPS, Clipper, Paradox a Java, z ktorých najmilšia mi je Java. Pracoval som hlavne ako softvérový architekt, ale aj ako programátor, analytik, dizajnér a projektový manažér. Pri vývoji softvéru sa mi najviac páči navrhovanie objektového dizajnu – obzvlášť pre zložité aplikácie. Svoje blogy chcem zamerať na postupy pri vytváraní objektového návrhu aplikácie a ich technologickej realizácii v podobe hexagonálnej architektúry a microservices.

Head of Engineering | REMOTE | Gaming | Americkí mentori

Pre mladý startup vyvíjajúci softvér, ktorý prispieva hráčom k lepšiemu zážitku z hrania, hľadáme Head of Engineering. Založili ho dvaja...

Medior Backend Developer

Chceš sa podieľať na rozvoji digitálnych služieb určených pre zdravotníctvo? Poď robiť veci, ktoré dávajú zmysel! Pátrame po Medior...

DevOps Engineer | REMOTE | Gaming | Americkí mentori

Pre mladý startup vyvíjajúci softvér, ktorý prispieva hráčom k lepšiemu zážitku z hrania, hľadáme DevOps Engineera. Založili ho dvaja...

Solution Architect

Pozícia: Solution Architect Pracovný pomer: TPP, Kontrakt Miesto práce: Bratislava Plat: TPP: 4000+ EUR/Brutto Kontrakt: 200+ EUR/MD Pracuješ ako Architekt alebo seniorný developer? Máme pre...

GIS/Lidar Špecialist

Chceš robiť niečo, čo robí len málokto na Slovensku? Jediná firma na Slovensku, ktorá prevádzkuje vlastné lietadlo pre zber údajov...

Automation Tester

Máš skúsenosti s automatizovaným testovaním? Pre Automatizovaného testera máme príležitosť v oblasti digitálneho bankovníctva. Ide o projekt na kontrakt s odmenou...

MS BI Developer / REMOTE

Sprav krok vpred s novým projektom v oblasti bankovníctva. Ide o projekt na kontrakt s dĺžkou trvania 2 roky. Odmena...

Živé IT projekty: Indoor Navigation

https://www.youtube.com/watch?v=OzN5K856TZg&feature=youtu.be Váš projekt jednou vetou? Naša mobilná aplikácia naviguje používateľa vo vnútri priestoru, v ktorom sú rozmiestnené Bluetooth beacony. Pre koho je...

Čo chcú ITčkári v časoch COVID-19

Koncom roka 2020 sme zrealizovali našu pravidelnú anketu “Čo chcú ITčkári?”. Na otázky odpovedalo rekordných 742 ITčkárov a ITčkáriek....

Jarné večerné školenia pre ITčkárov

Na jar 2021 sme pre vás pripravili novú sériu večerných školenie. Špeciálne pre juniorov programátorov tu máme bezplatné školenia...

Čítaj ďalej:

Dobrý článok? Chceš dostávať ďalšie?

Už viac ako 6 200 ITečkárov dostáva správy e-mailom. Nemusíš sa báť, nie každé ráno. Len občasne.

Súhlasím so spracovaním mojich osobných údajov. ( Viac informácií. )

Tvoj email neposkytneme 3tím stranám. Posielame naňho len informácie z robime.it. Kedykoľvek sa môžeš odhlásiť.