tifyty

pure Java, what else ?

Tervezési minta: belső építő megváltoztathatatlan objektumokhoz

A múlt héten a Javax0 blogon jelentettem meg egy kis agymenést arról, hogy jók-e egyáltalán a tervezési minták. Ezen a héten pedig ezt cikket írtam, aminek ez kb. a (félre)fordítása.

Ez a cikk egy egyszerű (vagy nem is olyan egyszerű) tervezési mintát ír le, amelyikkel módosíthatatlan (immutable) objektumokat lehet létrehozni belső építővel.

Az építő minta az, amikor egy osztály egy másik objektumot épít fel. Az eredeti szándék, hogy elválasszuk az objektum létrehozási fázisát, és a használatát. A létrehozás egyszerű esetben csak egy new Object(), de általában ennél jóval összetettebb folyamat. Sokszor az építő az építés során dönti el, hogy milyen objektumot hozzon létre, így a folyamat végén a felépített objektum akár különböző osztályok példánya is lehet. Ez jó példája az aggódások szétválasztásának (separation of concerns).

A megváltoztathatatlan objektumok, mint a nevük is mutatja nem változtathatóak miután felépítettük őket.

Az építők, és a megváltoztathatatlan objektumok a legnagyobb természetességgel illenek össze.

Mivel az építők és az épített osztályok nagyon erősen kötődnek egymáshoz, ezért általában egy csomagba szoktuk őket rakni. De miért vannak külön osztályban? Természetesen külön osztályoknak kell lenniük, különben nem válna szét az építő és az épített. De miért nem lehet az építő osztály az épített osztályon belül? Az építő az építési folyamat során gyűjti az információt arról, hogy milyen objektumot kell építenie, és amikor minden információ megvan, akkor ezeket arra használja, hogy elkészítse az épített objektumot. Ez a “használat” általában azt jelenti, hogy az értékeket bemásolja az épített objektumba. Ha az építő az épített osztály belső osztálya, akkor az információt nem kell bemásolnia, hanem rögtön az épített objektum mezőváltozóiban tárolhatja azokat az építés során. Ha ezek a mező privátak az sem baj, hiszen az építő belül van. Az építő létrehoz egy félkész objektumot, amit nem ad át senkinek, amíg el nem készül, és amikor a build() metódust meghívjuk (általában ez szokott a neve lenni, de nevezhetnénk Jakab-nak is) akkor csak a végső simításokat kell elvégeznie.

Ezt a mintát követi például a Google Guava projektje is a megváltoztathatatlan gyűjteményeknél (immutable collections). Az építők statikus belső osztályok. Ha megnézed a kódot a ImmutableList oldalon, láthatod, hogy a Builder egy belső osztály az absztrakt osztályon belül.

De nem ez az egyetlen lehetőség. Mi lenne ha megfordítanánk az egészet, és nem az építőt raknánk az épített osztályba, hanem az épített osztályt raknánk bele az építőbe? (8. utas a halál, megvan?) Csak az építő az egyetlen, amelyiknek hozzá kell férnie az épített osztály módosító metódusaihoz, mezőihez, kezelőorvosához, gyógyszerészéhez. Ha van olyan felületünk (interfész), amelyik definiálja a lekérdező metódusokat akkor annak elégnek kell lennie mindenki más számára. És ha már eljutottunk idáig, és van egy lekérdező interfészkünk, egy építőnk, és abban az interfész implementációja, az épített objektum osztálya, akkor miért nem csinálunk egy teljes matrjoschka baba sorozatot?

Legyen interfész. Legyen az építő ebben az interfészben egy belső osztály (publikus és statikus, mert ugye más nem is lehet). Legyen az interfész implementációja az építőn belül egy privát statikus belső osztály. És lőn:

public interface Knight {
    boolean saysNi();

    class Builder {
        private Implementation implementation = new Implementation();

        public Builder setState(String say) {
            implementation.say = say;
            return this;
        }

        public Implementation build() {
            Implementation knight = implementation;
            implementation = null;
            return knight;
        }

        private static class Implementation implements Knight {
            private String say;

            public boolean saysNi() {
                return say.indexOf("ni") != -1;
            }
        }
    }
}

Lefordul? De le ám! Ami talán meglepő lehet, az az, hogy az implementation.say = say működik, hiszen a say privát változó a belső osztályon belül, de ha elolvassuk a Java szabvány 6.6.1 fejezetét, akkor világossá válik a dolog (JLS1.7, section 6.6.1 Determining Accessibility). A privát változókat a top level osztályon (interfészen) belül egyenértékűen lehet látni. Mindegy, hogy azon belül alosztály, alosztályának az alosztályában vagyunk-e. Ebben nincs a Java-ban hierarchia, ha egy fájlon belül vannak (persze lehet egy fájlban több top level osztály, de ha valaki még egyszer ezzel veszi el másfél napomat, azt …) akkor látják egymást.

Az implementációhoz nem fér hozzá más, csak az építő (eltekintve a reflection, és egyéb byte kód abuzálástól, amitől most a karácsonyra való tekintettel eltekintek), és amint egyszer elkészült, többet már nem tud rajta változtatni. Az implementáció megváltoztathatatlan és garantáltan megtartja az állapotát.

Na akkor ez most pattern, vagy antipattern? Van annyira bonyolult, hogy átlagprogramozó elbaltázza, vagy szuper jó, és biztonságos (maraton)?

Boldog karácsonyt, és jövőre folytatjuk.

3 responses to “Tervezési minta: belső építő megváltoztathatatlan objektumokhoz

  1. Balazs Fejes december 20, 2013 4:16 du.

    Az aggódások szétválasztása eddig a kedvenc fordításom

  2. Verhás István december 20, 2013 4:28 du.

    “Legyen az építő ebben az interfészben egy belső osztály (publikus és statikus, mert ugye más nem is lehet).”
    De miért is? Utána a példában sem static!

    • Peter Verhas december 20, 2013 4:34 du.

      Egy interfésznek nincsenek példányai, ezért a belső osztályai csak statikusak lehetnek. Ezért ki sem kell írnom. Ezen kívül, mert nem lenne másnak értelme, az interfészen belül minden publikus. És mivel ez is alapműködés, ezt sem kell kiírni. Sonar háklis is rá, ha kiírjuk, mert szerinte ez csak zaj. (Le is vettem a public kulcsszót, hiba volt otthagyni.)

Vélemény, hozzászólás?

Adatok megadása vagy bejelentkezés valamelyik ikonnal:

WordPress.com Logo

Hozzászólhat a WordPress.com felhasználói fiók használatával. Kilépés / Módosítás )

Twitter kép

Hozzászólhat a Twitter felhasználói fiók használatával. Kilépés / Módosítás )

Facebook kép

Hozzászólhat a Facebook felhasználói fiók használatával. Kilépés / Módosítás )

Google+ kép

Hozzászólhat a Google+ felhasználói fiók használatával. Kilépés / Módosítás )

Kapcsolódás: %s

%d blogger ezt kedveli: