Tömbök, mint value-objectek

A programozási nyelvek legtöbbje lehetőséget biztosít függvények (metódusok, procedurák) alkalmazására. A függvények hívásakor paramétereket adunk át. Ezen átadások tipikusan kétféle: referencia szerinti (vagy cím szerinti, by reference), ill. érték szerinti átadás (by value). (Vannak más fajták is.)

Az érték szerinti átadás

Ez az egyszerűbben megérhető. Vegyünk egy Java vagy C# metódust, amely paraméterként egy int-et vár:

Az f(z) hívás közben a z értéke lemásolódik, és amíg az f függvény végrehajtása tart, az x változóval erre a másolatra (lokálisan) hivatkozunk. Ha az x értékét megváltoztatjuk a függvény belsejében (ami amúgy refaktorálási szempontból nem egy jó szokás), akkor a másolatot módosítjuk, ennek közvetlenül nincs hatása az eredeti z változóban lévő értékre.

Referencia vagy cím szerinti átadás

A következő kód C++-ban írodott:

Itt az f(z) híváskor a z változót tároló memóriarekesz (kezdő)címe adódik át. Ennek az a következménye, hogy a függvényen belüli x-re irányuló változtatás közvetlenül megváltoztatja az eredeti változó értékét is. Az f(5) hívás egyenesen fordítási hibát eredményez, mert szemantikailag helytelen. (Hogyan változtatható meg az 5 konstans értéke? Kell változó.)

Na, ez most melyik?

A következő kód Java-ban íródott:

Tudjuk, hogy a buy metódus végrehajtása közben ugyanazzal az objektummal dolgozunk, mint ami a híváskor jelen van, de a metódusban a customer változó null-ra állítása nem befolyásolja a c értékét. Úgy tűnik, mintha kettőséggel szembesülnénk: bár ugyanazon az objektumon dolgozunk, és ez referencia szerinti átadást sejtet, viszont ha magát a customer változó értékét változtatom meg, az eredeti c változó nem változik. Ez utóbbi viszont érték szerinti átadást mutat.

Valójában Java-ban minden érték szerint adódik át. A c ill. customer változók referenciák (mintha C-beli mutatók lennének), ezen referenciák értéke másolódik le függvényhíváskor. Így a függvényhívás belsejében a referencia változik meg (már máshová mutat).

Value object-ek

Láthatjuk, hogy Java-ban objektumra történő hivatkozást (referenciát adunk át). A primitív típusokon (pl. intlong) kívül mindent referencián keresztül érünk el, azonban bizonyos esetekben célszerű a referenciákon keresztül elért objektumokra ún. “value object”-ként gondolni. Value object-re tipikus példa a dátum, ill. sztring, vagy a komplex számok és a pénzmennyiséget reprezentáló struktúrák. Ezekre általában úgy tekintünk, mint a primitív típusokra, műveleteket is hasonlóképpen végzünk velük. (A C#-ban a string primitív tipus, Java-ban az osztály metódusai minden módosítás után egy új string-et adnak vissza. Ennek eredményeképpen az eredeti String objektumon nem tudunk változtatást végezni, egy konkatenáció pl. mindig egy új String-et ad vissza.)

A tömbök, mint value object-ek

(Megjegyzés: a továbbiakban – pongyolán – a tömbök kifejezést a Collection szinonimájaként is használom.)

Képzeljük el, hogy van egy Customer osztályunk. Ebben az osztályban deklaráltunk egy addresses mezőt, amely az ügyfél címeit reprezentálja.

Ezzel a kóddal kapcsolatban a legnagyobb gond az, hogy ha valaki elkéri egy ügyfél címeit, akkor a visszakapott Set<Address> referencián keresztül közvetlenül módosíthatja azokat (magát a halmazt). Ezt OO szempontból “nem szokták szeretni”, mivel az objektumnak kell megvalósítania az egységbezárást, az objektumon történő megfelelő metódushívásoknak kell elvégezniük a módosítást (ez így olyan mintha a mezőt public-ká tennénk.) Miért akarná valaki módosítani a visszakapott referencián keresztül a halmazt?

  • véletlenül: előfordult már,
  • szándékosan: az ilyen emberektől védeni akarjuk a kódot.

Én minden ilyen esetben, amikor egy halmaz, lista stb. -szerű objektumot adok vissza, lemásolom. Tehát a getter-jeim így néznek ki:

Ennek következménye, hogy aki megkapja az új halmazt, vígan módosíthat benne, és nem cseszi el – se szándékosan, se véletlenül – az eredeti halmazt. Ennek a módszernek annyi a hátulütője, hogy lassít az alkalmazáson, és bizonyos típusú szoftvereknél ez tényleg problémát jelenthet. (Meggyőződésem viszont, hogy ORM-környezetben vagy más “enterprise-környezetben” ha egy halmaznak eleve sok eleme van, és Java-kódból kell feldolgozást végezni rajta, az eleve lassú lesz annak lemásolása nélkül is.)

A tömb lemásolása ilyen szempontból úgy viselkedik, mintha egy value object-et adnék át. Önmagában egy kompakt egész, amelyen lekérdezési műveleteket lehet elvégezni.

Még egy kis finomítás

Van-e annak értelme, hogy az addresses mező null-referencia? Ha nincs egy eleme sem a halmaznak, akkor az egy üres halmaz. A Set-nek ugyanúgy léteznie kell, legfejlebb üres lesz.

Valamiért utálok egy mezőnek kezdő értéket adni, inkább a konstruktorokban teszem ezt meg. Ennek ellenére a Set-hez hasonló osztályokat mindig deklarációkor inicializálom:

A primitív típusok esetén is van default érték, tipikusan 0. Itt halmaz esetén az üres halmaz lesz az (ha kell elemet bele pakolni, akkor a konstruktorban még mindig megtehetem). Még tovább: mi történik, ha egyszerre akarom “felülcsapni” az ügyfél összes címét?

Az előző gondolatmenet alapján ennek így kell lennie. Ha addresses = newAddresses-t írnék, akkor ugyanaz a probléma állna fenn, mint a getter-eknél. (Ha valaki kívül módosítja utólag a halmazt, az módosul a Customer-ön belül is.)

Collections.unmodfiableSet – még egy kis adalék

Pár hónapja leltem rá a Java SDK-ban a Collections osztály statikus unmodifiableSet (List stb.) metódusára. A metódus paramétere egy halmaz, visszatérésként egy másik halmazt kapunk vissza, amely nem módosítható. (Ha módosítanánk rajta, akkor UnsupportedOperationException-t kapunk.) Ez nagyon hasonló a mi “kézi” halmazlemásolásunkhoz, de valójában csak egy view-t kapunk vissza. Ha valaki az eredeti halmazt módosítja, az a view-ban is megváltozik.

Share
This entry was posted in Programozás and tagged , , , , , . Bookmark the permalink. Follow any comments here with the RSS feed for this post. Post a comment or leave a trackback: Trackback URL.

Comments

  • Kristof Jozsa

    Jul 9th, 2009

    Collections.unmodifiableSet() a szép megoldás. az inline inicializálással meg semmi gond nincs (azt legalább nem lehet megkerülni ha valaki fél perc alatt rittyent az osztályra egy alternatív konstruktort :))

  • A Collections.unmodifiableSet() tök jó, mert a kliens azonnal elszáll, ha megkísérli változtatni a halmazt. Viszont ha az unmodifiableSetet visszaadó kód utólag megváltoztatja a halmaz tartalmát, akkor a kliensnél az unmodifiableSet is "megváltozik" (mivel belül az eredeti halmazra hivatkozik). Mit szólsz egy ilyenhez?:

    return Util.unmodifiableSet(new HashSet<Address>(addresses));

    :)

  • Viczián István

    Dec 8th, 2009

    Szia! Nagyon tetszik a blogod, ritka jó, most találtam, és azonnal el is olvastam az összes bejegyzést!

    Ehhez a bejegyzéshez szeretnék annyit írni, hogy a címben is tömb szerepel, de valójában a Collection API osztályait használod, persze ezeken belül tényleg tömbök vannak.

    Valamint írod, hogy "(A C#-ban a string primitív típus, Java-ban az osztályt ellátták a final módosítószóval, amelynek eredményeképpen az eredeti String objektumon nem tudunk változtatást végezni, egy konkatenáció pl. mindig egy új String-et ad vissza.)"

    Ez nem teljesen így van, vagy rosszul értelmezem, amit írsz. A String tényleg final, azért hogy ne lehessen az osztályból leszármaztatni, azaz egy MyString osztályt létrehozva, ami felülírja a konkatenációt, és a polimorfizmus miatt bárhol használhatnánk String helyett. De amit te írsz, hogy a String objektum értéke nem változtatható meg (tudományosan immutable), az nem abból adódik, hogy a String osztály final, hanem abból, hogy a metódusok nem változtatják a String értékét tároló char value[] tömböt, és nem is tudnák, mert az is final. :)

    A többi post tökéletes. :)

Leave a Comment


  • RSS JTechLog – Viczián István java blogja


    Warning: A non-numeric value encountered in /chroot/home/infokuka/infokukac.com/html/wp-includes/SimplePie/Parse/Date.php on line 693

    Warning: A non-numeric value encountered in /chroot/home/infokuka/infokukac.com/html/wp-includes/SimplePie/Parse/Date.php on line 693

    Warning: A non-numeric value encountered in /chroot/home/infokuka/infokukac.com/html/wp-includes/SimplePie/Parse/Date.php on line 693

    Warning: A non-numeric value encountered in /chroot/home/infokuka/infokukac.com/html/wp-includes/SimplePie/Parse/Date.php on line 693

    Warning: A non-numeric value encountered in /chroot/home/infokuka/infokukac.com/html/wp-includes/SimplePie/Parse/Date.php on line 693

    Warning: A non-numeric value encountered in /chroot/home/infokuka/infokukac.com/html/wp-includes/SimplePie/Parse/Date.php on line 693

    Warning: A non-numeric value encountered in /chroot/home/infokuka/infokukac.com/html/wp-includes/SimplePie/Parse/Date.php on line 693

    Warning: A non-numeric value encountered in /chroot/home/infokuka/infokukac.com/html/wp-includes/SimplePie/Parse/Date.php on line 693

    Warning: A non-numeric value encountered in /chroot/home/infokuka/infokukac.com/html/wp-includes/SimplePie/Parse/Date.php on line 693

    Warning: A non-numeric value encountered in /chroot/home/infokuka/infokukac.com/html/wp-includes/SimplePie/Parse/Date.php on line 693

    Warning: A non-numeric value encountered in /chroot/home/infokuka/infokukac.com/html/wp-includes/SimplePie/Parse/Date.php on line 693

    Warning: A non-numeric value encountered in /chroot/home/infokuka/infokukac.com/html/wp-includes/SimplePie/Parse/Date.php on line 693

    Warning: A non-numeric value encountered in /chroot/home/infokuka/infokukac.com/html/wp-includes/SimplePie/Parse/Date.php on line 693

    Warning: A non-numeric value encountered in /chroot/home/infokuka/infokukac.com/html/wp-includes/SimplePie/Parse/Date.php on line 693

    Warning: A non-numeric value encountered in /chroot/home/infokuka/infokukac.com/html/wp-includes/SimplePie/Parse/Date.php on line 693

    Warning: A non-numeric value encountered in /chroot/home/infokuka/infokukac.com/html/wp-includes/SimplePie/Parse/Date.php on line 693

    Warning: A non-numeric value encountered in /chroot/home/infokuka/infokukac.com/html/wp-includes/SimplePie/Parse/Date.php on line 693

    Warning: A non-numeric value encountered in /chroot/home/infokuka/infokukac.com/html/wp-includes/SimplePie/Parse/Date.php on line 693

    Warning: A non-numeric value encountered in /chroot/home/infokuka/infokukac.com/html/wp-includes/SimplePie/Parse/Date.php on line 693

    Warning: A non-numeric value encountered in /chroot/home/infokuka/infokukac.com/html/wp-includes/SimplePie/Parse/Date.php on line 693

    Warning: A non-numeric value encountered in /chroot/home/infokuka/infokukac.com/html/wp-includes/SimplePie/Parse/Date.php on line 693

    Warning: A non-numeric value encountered in /chroot/home/infokuka/infokukac.com/html/wp-includes/SimplePie/Parse/Date.php on line 693

    Warning: A non-numeric value encountered in /chroot/home/infokuka/infokukac.com/html/wp-includes/SimplePie/Parse/Date.php on line 693

    Warning: A non-numeric value encountered in /chroot/home/infokuka/infokukac.com/html/wp-includes/SimplePie/Parse/Date.php on line 693

    Warning: A non-numeric value encountered in /chroot/home/infokuka/infokukac.com/html/wp-includes/SimplePie/Parse/Date.php on line 693
  • RSS Tajti Ákos C és Java blogja

  • RSS QualityOnTime

  • RSS Adaptive PM

  • RSS Menedzsmentor blog

  • Shelfari: Book reviews on your book blog