tifyty

pure Java, what else ?

Havi archívumok: május 2013

Interfész implementálás, level ninja

Aki ezt a blogot olvassa az mind tudja, hogy mi Java-ban az interface implementálás (vagy unatkozó nagymama). Azt is körbejártuk, hogy hogyan lehet többszörös öröklődést csinálni, ami nem csak interfészektől örököl többszörösen, hanem valóban mixin jellegű többszörös öröklődést valósít meg.

Azzal sincs már gondunk, hogy vajon a Java megengedi-e, hogy két interfész ugyanazt a metódust definiálja, és mi mind a kettőt implementáljuk az osztályunkban egy metódusban. Arról is esett már talán szó, hogy a Java azért sem haragszik, ha két olyan interfészt is megpróbálunk implementálni, amelyekben inkompatibilis metódusok szerepelnek: csak éppen nem fog menni.

Ez ugye az az eset, amikor

interface1:

     Integer a();

interface2:

     String a();

class:

class Z implements interface1, interface2 {...

A fordítónak itt sem azzal van a baja, hogy nem implementálhatjuk mind a két interface-t, csak azzal, hogy nem sikerül. Vagy az egyiket implementáljuk, vagy a másikat, a visszatérési érték Java nyelvben nem része a metódus aláírásnak.

És akkor most jön a kérdés hétvégére:

Hogyan fordulhat olyan elő, hogy egy osztályunk implementálni akar két interface-t, a két interface-ben nincsenek egymást “ütő” metódusok, a Java compiler mégsem engedi meg, hogy mind a két interface-t implementáljuk.

Hogy még egyszerűbb legyen a dolog: I1 és I2 interface-k egyike sem definiál egyetlen metódust sem, sem direk módon, sem pedig öröklődésen keresztül. A C osztály pedig csak ennyi:

public class C implements I1, I2{

}

És mégsem fordul le. Mi lehet?

Mese az ügyféligényről és a numerikus módszerekről

Történt valamikor a rendszerváltás után, amikor a jó állami vállalatok sorban mentek tönkre meg privatizálódtak, és úgy általában átalakultak, hogy egy ismerősöm elvesztette a munkáját. Mihez kezdjen ötven fölött egy alkalmazott fizikus, aki addig egy nagyvállalat óvó szárnyai alatt fejlesztette a hardvert és a hozzátartozó szoftvert? Keresett valami hasonlót. Több kitérő után eljutott egy olyan céghez, amelyik feltehetően szintén valamely korábbi szocialista nagyvállalat romjain kivirágzott rózsa volt, és akik külföldi piacra geológia szoftvert fejlesztettek. Lehet, hogy geodéziai, ami persze nem mindegy, hiszen Descartes sem azt mondta, hogy “coito ergo sum”, pedig mondhatta volna. A mi kis történetünk szempontjából viszont mindegy.

Főhősünk kapott egy feladatot, hogy optimalizáljon egy numerikus számítási kódrészletet, amelyik akkori állapotában kb. 24 órát futott egy szokásos PC-n, és azt szerették volna, hogy 8 órán belül lefusson. Ha ugyanis több, mint 8 óra, akkor a felhasználó elindítja, és aznap már nincs készen. Ha viszont 8 órán belül van, akkor egy nap az előző futás eredménye alapján akár rögtön elindíthat egy második számítást. Ha hat óra, vagy öt: akkor sem jobb. Négy órára leszorítani pedig a 24-ről nem tűnt reálisnak.

Valószínűleg nem nagyon számítottak rá, hogy az “öreg” fizikus megoldja a dolgot, hiszen eddig sem a programozás volt a fő területe, bár alkotott programokat, de clean code és társai olyan messze voltak tőle, mint a finn mezei nyúltól az Alpha-Centauri. De a programok működtek, és ha nem csapatban kell dolgozni, és nem túl méretes a kód, akkor az optimalizálás komolyabb szakértelem híján try and fail módszerrel is olvashatatlan, karbantarthatatlan, de működő és valamelyest gyorsabb kódot eredményezhet. Programot akár gyárthatunk úgy is, hogy teszteljük a futási eredményeket, és addig rakunk össze random karaktereket, amíg egyszer csak olyan fájlt kapunk, ami lefordul, és azt is csinálja amit szeretnénk. Ha Shakespeare szonátát lehetett így előállítani, programot miért ne lehetne. Csak sok virtuális majom kell.

Szóval a fizikus leült egy asztalhoz, és nekiállt tanulni. Megtanulta, hogy mit csinál a cég, mit csinál a szoftver, mi mögötte a fizika, és addig számolt, amíg rájött, hogy ezt a numerikus számolást, ami a program aktuális változatában 24 órát futott meg lehet oldani zárt képlettel. Ugyan kibabrált durva nagy az a képlet, de a számítás futási ideje még így is inkább az ezredmásodperc környékére esett, semmint a 86,400 másodperc közelébe. Boldogan vitte be a főnökéhez, aki a következőket mondta:

  • Ez nagyon jó, csak az eredmény nekünk két hét alatt kellett volna, mostanra meg eltelt három hónap.
  • Az ifjú titánok közben leszorították az eredeti algoritmus futási idejét hét és fél órára különböző programozási trükkökkel, és a nemzetközi vásárra ki is ment ez a release.
  • A következő release fél év múlva lesz.
  • Ha a számítási idő ilyen nagyon rövid, akkor a vevők nem fogják elhinni, hogy ez egy komoly számítás, és hamarosan nem lesznek hajlandók prémium árat fizetni a modulért.
  • Persze a munka nem volt haszontalan, mert majd bekerül a következő release-be, és az ifjú titánok írnak köré egy kis kódot, ami vár, meg forgatja a homokórát, hogy hihető legyen, hogy sokáig számol. Viszont ha a konkurencia kijön egy gyorsabb változattal, pillanat alatt leveszünk a várakozási időből, és megint mi vagyunk a nyerők.

Természetesen nem szó szerint idéztem, hiszen nem voltam ott, és közel húsz éve történt a dolog, inkább tessék úgy felfogni, mint egy mesét. De azért a mese tanulságos!

Hogy történt, hogy rá sem néztek az új kollégára három hónapig? (Ez már nem nagy szoci vállalat volt, hanem kis magyar Kft.) A kultúra folytonos.

Az “ügyfél” által adott feladat leírása, megfogalmazása nem fedte az igazi ügyféligényt. És ez általában mindig így van. Általában. Mindig.

Ha fizikai modellekkel dolgozunk, akkor jobb ha a csapatban kezdetektől van valaki aki matematikus, fizikus. De általánosan is igaz: ha XXX modellel dolgozunk, jobb ha a csapatban van a kezdetektől XXX-hez értő szakember. Ne gondoljuk, hogy majd mi értünk hozzá. Mi a clean code-hoz értünk.

És akkor most levezetésnek itt egy programozási feladat, ha valaki még emlékszik a középiskolás fizikára, és a numerikus módszerekre. Semmi köze a fenti meséhez (á, dehogy), hacsak ki nem derül, hogy hazudok.

Adott egy inga, a hossza L, földi környezetben, ahol konstans a nehézségi gyorsulás, vagyis L elég kicsi ahhoz, hogy a gyorsulás ne függjön attól, hogy mennyire tér ki az inga: nem kisebb a nehézségi gyorsulás akkor sem, amikor kitér az inga, és ezért távolabb van éppen a föld tömegvonzási központjától.

Az inga végén a tömeg pontszerű. Nincs légellenállás, és az inga nulla tömegű, teljesen rugalmatlan szálon lóg. Nem kell figyelembe vennünk semmilyen torziós erőt.

Meglökjük az ingát a nyugalmi helyzetében, olyan sebességgel elindítva, hogy az inga túljut a vízszintes helyzeten, de nem éri el a körpálya tetejét, hanem egy ponton elhagyja a körpályát, és parabola pályán repül, majd egy ponton újra eléri a körpályát.

Írjunk programot, amelyik kiszámolja, hogy ha ismerjük, hogy hol hagyja el az inga a körpályát, akkor hol fog visszatérni!

Bónuszkérdés: Miért éppen a finn nyulakat emlegettem? Mi a helyzet a nem őshonos, behurcolt ausztrál nyulakkal? (A juhokkal, meg a lámákkal már foglalkoztunk eleget.)

Nyitott programok

Az open source alapvetően azt jelenti, hogy a programok forráskódja elérhető a felhasználó számára. Hogy mire használhatja, az persze más kérdés: van, hogy belenyúlhat (hopp… ugrott a garancia, ami általában nem is volt). Van, hogy a módosított szoftvert el is adhatja, de van, hogy csak megtekintheti. Ebből a szempontból igen gazdag a licenc kínálat; válogathatunk.

A nyíltság azonban nem csak ezt jelenti, hanem sok minden mást is. Ha a program nem magában nyújtja a szolgáltatásait, akkor olyan felületeket használ, amelyek elérhetőek más programok számára és szabványosak. Azért ma már elég nehéz elképzelni olyan programot, ami magában elketyeg: ha mást nem, legalább az operációs rendszert figyelembe kell venni. Bár léteznek embedded rendszerek, és az is igaz, hogy csak az, hogy a program fut egy operációs rendszer alatt még nem teszi igazán nyílttá, még akkor sem, ha a forrás maga elérhető.

És az is a nyíltsághoz tartozik, hogy nem csak használ ilyen felületeket, hanem ha értelmes a dolog, akkor nyújt is. Így a felhasználók nem csak egy GUI-n keresztül manuálisan érhetik el a funkciókat, hanem más rendszerekkel összeintegrálva komplex rendszereket alkothatnak konfigurációkkal, és apró kiegészítő szoftverekkel (általában ezt értjük rendszerintegráció alatt). És ez nagyon fontos, annyira, hogy amikor még nem tervezték ilyenre a programokat akkor komoly eszközöket fejlesztetettek arra 1994 környékén (Jabberwocky Toolkit), hogy a humán felületeket meg lehessen programból hajtani. Szerencsére akkor még nem nagyon voltak grafikus felületek, és egy 25×80 karakteres VT100 terminál felület programmal való értelmezése, és meghajtása azért egy kicsit egyszerűbb, mint egy Windows felület. Különösen alkalmasak voltak erre az IBM 3270 terminálok, amelyek nem karakterenként kommunikáltak a szerverrel, hanem egy egész képernyőt lehetett/kellett kitölteni, és utána elküldeni a szervernek. Ez persze nem jelenti azt, hogy a mainframe 3270 terminált használó alkalmazások nyitottak lettek volna abban az értelemben, ahogy a nyitottságot ma értelmezzük.

És ha már ilyen messzire elkalandoztam, nem tudom megállni, hogy el ne meséljek egy másik ide kapcsolódó történetet. A nyíltság egyik élenjárója a DEC volt (akkor még használatban volt ez a név, később már csak Digital volt a hivatalos említés, amikor a kék helyett bejött a piros logó). Magyarországon is lement egy TV hirdetés, amelyik azt hirdette, hogy a nyitott szoftverek jobbak, mint a zárt mainframe-es rendszerek. A TV spotban egy ejtőernyő volt látható, amelyik szépen kinyílik, és a szlogen (nembiztos, hogy pontosan idézek): better open. Másnap betelefonált a Digital-ba valaki, hogy szeretne ejtőernyőt vásárolni.

Na akkor most `stash` és `checkout origin master`. Végül is master sempre certa est.

Csak ezért jobb-e egy programnak ha nyitott, mert több lehetőséget ad a felhasználóknak, hogy saját környezetükbe integrálva használhassák? Mostanában tanultam meg, hogy nem.

Pontos részletek meghatározása nélkül az történt, hogy egy program egy részét ki kellett nyitni, és elérhetővé tenni, a mi jó kis implementációnk helyett a felhasználók által készítendő különböző alkalmazások számára. Ez azt jelentette, hogy ezentúl nem csak az történhetett hogy JVM-en belül meghívtunk valami metódust, létrehoztunk egy/néhány osztályt, hívtuk őket, visszahívtak, paraméterek jöttek, mentek, míg végül összeállt valami. Sajnos az egész kódra ez utóbbi volt a jellemző a fejlesztés folyamán: addig masszírozta a csapat, amíg összeállt valami.

Amikor azonban ki kellett nyitni a funkciót, az történt, mint amikor egy kelést felnyitnak: a belül fortyogó sárga genny kijön és elkezdődik egy tisztulási folyamat. Pontosabban meg kellett határozni, hogy pontosan hol vannak, hol legyenek a funkcionalitási határok: ki csinál mit, mi kerül az egyik oldalra, és mi a másikra. Ezzel (utólag már) nem meglepő módon a hívó kód is tisztább lett, és egy év után látszott a JIRA statisztikákból is, hogy kevesebb az ezen területet érintő hibajegy.

Természetesen kitisztulhat egy ilyen modul úgy is, hogy nem nyitjuk ki, de általában, mint a gennyes sebnél: ez nem szokott bekövetkezni. Nem refaktorálunk, csak amikor már nagyon fáj. Vagy amikor valamilyen más ok miatt kell. Szükséges az úgynevezett “demanding need”. Ha ez nincs, akkor a gennyes kód betokozódik, és ott marad, amíg az egész program a krematóriumban nem végzi.