Alapok
Egyedi objektumok
Vágjunk is bele! Készítsünk a saját könyvtárunkba egy futtatható fájlt, például develop.sh néven. A kód minden változtatása után lefuttathatjuk ezt a szktiptet, hogy teszteljük a működést. Ennek a szkriptnek az első sora a shebang, amely az előbb létrehozott oob szimbolikus linket hívja meg:
#!/usr/bin/env oob
## run
main() { echo "Hello World!"; }
: set -x; main "$@"; set +xA szkript utolsó sorába beépítettünk egy hibakeresési lehetőséget: a kettőspont eltávolításával a szkript a főprogram (main) minden utasítását kiírja a képernyőre.
Alakítsuk át a develop.sh fájlt objektum szemléletűvé! A feladat a Föld üdvözlése.
#!/usr/bin/env oob ## objects # attributes declare -gA o_planet_info=( [name]="Earth" ) # methods o_planet_getName() { echo "${o_planet_info[name]}"; } o_planet_sayHello() { echo "Hello $(o_planet_getName)!"; } ## run main() { o_planet_sayHello; } : set -x; main "$@"; set +x
Az objektumban (o_planet) létrehoztunk egy adattagot (info) és két metódust (getName, sayHello), amelyek egymást hívják. Az adattagot hívhatjuk attribútumnak is, a metódust meg tagfüggvénynek. Az adattagok és a tagfüggvények az objektum tagjai. Az objektum lehetett volna egyszerűbb vagy bonyolultabb, a példa szerinti objektum elegendően összetett ahhoz, hogy az alapvető objektumkezelést tanulmányozzuk, mégis elég áttekinthető.
Az objektum nevében az o_ kifejezés jelzi, hogy objektumról van szó. Az adattagot változók (a példában asszociatív tömb), a metódusokat függvények valósítják meg. Az egyes részekben a camelCase írásmódot használjuk. A megengedett karakterek kizárólag az angol ábécé és a számjegyek. A tájékozódást segíti, ha az objektumok és az adattagok nevei főnevekből állnak (o_planet3, info), míg a metódusok neveinek első szava ige, a többi a metódus által végzett művelet tárgya (getName, sayHello). Az objektum és a tagok összetartozását az jelzi, hogy az objektum és a tag nevét egy aláhúzásjellel összekötjük.
Ha most kiadjuk a $HOME/develop.sh parancsot, a program angolul üdvözölni fogja a Földet.
Tagcsoportok, szerepek, függő objektumok
Az objektumon belül a szorosabban összetartozó tagokat csoportosíthatjuk és ennek a csoportnak nevet is adhatunk. Például egy personalData csoportba tartozhat a személy neve, születési adatai, igazolványszáma, stb. Így a név adattag teljes neve o_person_personalData_name lehet.
Egy személy munkahelyi adatait tartalmazhatja a workData csoport. Nyugdíjba vonuláskor törölhetjük a workData csoportot az összes adatával együtt és létrehozhatjuk a retiredData csoportot. A dolgozói és a nyugdíjas státuszt hívhatjuk a személy szerepeinek is, így a workData és a retiredData csoportokat is tekinthetjük a személy szerepeinek.
Csoportosíthatuk az objektumokat is. Például egy gép alkatrészeinek megfelelő objektumoknak adhatjuk az o_machine_parts_<cikkszám> nevet is. Nem nehéz belátni, hogy maga a gép is egy objektum. Ennek létezésétől függenek az alkatrészeket reprezentáló objektumok. Az ilyen típusú objektumok a függő objektumok.
Ha egy tag teljes nevében több, mint három aláhúzásjel van, nem tudjuk eldönteni, hogy a tag egy tagcsoportba, egy szerephez vagy egy függő objektumhoz tartozik. Technikailag nincs is jelentősége annak, hogy milyen jellegű kapcsolatról van szó. Ezért az elnevezés azon részeit, amelyek az objektum és a tag között vannak, önkényesen szerepeknek hívjuk. Szerepgazda az az objektum vagy szerep, amelytől egy vagy több másik szerep függ. (Szerepgazda a személy vagy a gép, szerep a személyi vagy munkahelyi adatok, illetve az alkatrész.) Egyedül az objektumnak nincs szerepgazdája. De az objektumot is tekinthetjük úgy, mint amelynek a szerepgazdája a modellezendő világ. A világot reprezentáló szerepgazdát gyökérobjektumnak (angolul root object) hívjuk. Ebben a szemléletben minden egyéb objektum szerep. Ezért az objektumok nevében a kezdő o karaktert tekinthetjük a gyökérobjektum nevének. Minden szerepet tekinthetünk függő objektumnak is, így az objektum és a szerep a legtöbb esetben egymás szinonímája.
Például, ha bolygórendszereket tartunk nyilván, akkor a csillag, a bolygó és a hold is objektum. Az o_star_planet_moon egy holdat reprezentáló objektum, amely a bolygó szerepe és így a bolygó a hold szerepgazdája. Egyúttal a bolygó a csillag szerepe, ezért a csillag a bolygó szerepgazdája. A csillag a gyökérobjektum szerepe, tehát a szerepgazdája a gyökérobjektum..
Ábrázoljuk a Nap - Mars - Mars (a 4. bolygó) holdjai (Phobos és Deimos) rendszert a develop.sh fájlban és most a Mars egyik holdját üdvözöljük:
#!/usr/bin/env oob ## objects # attributes declare -gA o_star_info=( [name]=Sun ) declare -gA o_star_planet4_info=( [name]=Mars ) declare -gA o_star_planet4_moon1_info=( [name]=Phobos ) declare -gA o_star_planet4_moon2_info=( [name]=Deimos ) # methods o_star_getName() { echo "${o_star_info[name]}"; } o_star_sayHello() { echo "Hello $(o_star_getName)!"; } o_star_planet4_getName() { echo "${o_star_planet4_info[name]}"; } o_star_planet4_sayHello() { echo "Hello $(o_star_planet4_getName)!"; } o_star_planet4_moon1_getName() { echo "${o_star_planet4_moon1_info[name]}"; } o_star_planet4_moon1_sayHello() { echo "Hello $(o_star_planet4_moon1_getName)!"; } o_star_planet4_moon2_getName() { echo "${o_star_planet4_moon2_info[name]}"; } o_star_planet4_moon2_sayHello() { echo "Hello $(o_star_planet4_moon2_getName)!"; } ## run main() { o_star_planet4_moon1_sayHello; } : set -x; main "$@"; set +x
Metódusok egységesítése
Látható, hogy az összes getName metódus nagyon hasonlít egymásra, mint ahogy az összes sayHello metódus is hasonlít egymásra. Nem csoda, hiszen ugyanazokat a feladatokat végzik el a különböző objektumokban. A kódot úgy is megírhatjuk, hogy az összes getName kódja azonos legyen és azonos legyen az összes sayHello kódja is.
Ehhez először bevezetjük a self változó fogalmát. Ez a változó mindig az aktuális objektum nevét tartalmazza. Ennek kiszámítása egyszerű: a bash FUNCNAME nevű tömbje tartalmazza az egymást hívó függvények sorát visszafelé, így az első tagja az aktuális függvény, azaz maga a metódus. Ebből a %_* operátorral leválasztjuk a tagot, így marad az objektum neve, amit eltárolunk a self változóba. Írjuk át a develop.sh fájlban a metódusokat úgy, hogy a self változót használják:
# methods
o_star_getName() { local self=${FUNCNAME%_*}; local -n self_info=${self}_info; echo "${self_info[name]}"; }
o_star_sayHello() { local self=${FUNCNAME%_*}; echo "Hello $(${self}_getName)!"; }
o_star_planet4_getName() { local self=${FUNCNAME%_*}; local -n self_info=${self}_info; echo "${self_info[name]}"; }
o_star_planet4_sayHello() { local self=${FUNCNAME%_*}; echo "Hello $(${self}_getName)!"; }
o_star_planet4_moon1_getName() { local self=${FUNCNAME%_*}; local -n self_info=${self}_info; echo "${self_info[name]}"; }
o_star_planet4_moon1_sayHello() { local self=${FUNCNAME%_*}; echo "Hello $(${self}_getName)!"; }
o_star_planet4_moon2_getName() { local self=${FUNCNAME%_*}; local -n self_info=${self}_info; echo "${self_info[name]}"; }
o_star_planet4_moon2_sayHello() { local self=${FUNCNAME%_*}; echo "Hello $(${self}_getName)!"; }Az adattagokat most nem a saját nevükön érjük el, hanem formalizált neveken, amelyek a self karaktersorozattal kezdődnek. Így már az összes getName metódus kódja ugyanaz, ahogy az összes sayHello metódusé is. A következő lépésben egységesítjük a metódusok elejét, azaz egy objektum összes metódusa ugyanazzal a három utasítással fog kezdődni. Az első utasítás beállítja a self változót, a második az adattagok formalizált nevét. Ezeket az utasításokat együtt referencia utasításoknak hívjuk. A harmadik (:;) utasítás semmit sem csinál, a célja csupán a rererencia utasítások és az érdemi munkát végző utasítások elválasztása.
# methods
o_star_getName() { local self=${FUNCNAME%_*}; local -n self_info=${self}_info; :; echo "${self_info[name]}"; }
o_star_sayHello() { local self=${FUNCNAME%_*}; local -n self_info=${self}_info; :; echo "Hello $(${self}_getName)!"; }
o_star_planet4_getName() { local self=${FUNCNAME%_*}; local -n self_info=${self}_info; :; echo "${self_info[name]}"; }
o_star_planet4_sayHello() { local self=${FUNCNAME%_*}; local -n self_info=${self}_info; :; echo "Hello $(${self}_getName)!"; }
o_star_planet4_moon1_getName() { local self=${FUNCNAME%_*}; local -n self_info=${self}_info; :; echo "${self_info[name]}"; }
o_star_planet4_moon1_sayHello() { local self=${FUNCNAME%_*}; local -n self_info=${self}_info; :; echo "Hello $(${self}_getName)!"; }
o_star_planet4_moon2_getName() { local self=${FUNCNAME%_*}; local -n self_info=${self}_info; :; echo "${self_info[name]}"; }
o_star_planet4_moon2_sayHello() { local self=${FUNCNAME%_*}; local -n self_info=${self}_info; :; echo "Hello $(${self}_getName)!"; }Ezek a módosítások nem befolyásolják a kimenetet, a $HOME/develop.sh szkript futtatása az egységesítés után ugyanúgy a Hello Phobos! üzenetet eredményezi, mint az egységesítés előtt.
Osztályok
Azoknak az objektumoknak a halmazát, amelyek csupán az adattagok tartalmában térnek el, osztálynak hívjuk. Az egy osztályba tartozó objektumok közös jellemzőt tárolókba gyűjthetjük. A tárolók célszerűen asszociatív tömbök. Egy asszociatív tömb tartalmazhatja az adattagok nevét és típusát, egy másik a metódusok nevét és kódját. Ezeket a tömböket leíróknak hívjuk. Így az osztály nemcsak egy elvont fogalom, hanem a két leíróval reprezentáljuk is azt a programban. Mivel lehet több osztály is, ezért azokat számokkal különböztetjük meg egymástól. Az eddig megírt objektumokhoz készítsük el az osztályokat a ## run szakasz elé:
## classes
declare -gA dc_1_d=( [info]="-gA" )
declare -gA dc_1_m=(
[sayHello]='{ local self=${FUNCNAME%_*}; local -n self_info=${self}_info; :; echo "Hello $(${self}_getName)"; }'
[getName]='{ local self=${FUNCNAME%_*}; local -n self_info=${self}_info; :; echo "${self_info[name]}"; }'
)Látható, hogy a tárolt információkból előállítható az osztályhoz tartozó objektum. Az adattagok leíróinak neve a dc_ 3 db. karakterrel kezdődik (Descriptor, Class) és az _d 2 db. karakterrel (Data) végződik, a kettő között az osztály sorszáma áll. A metódusok leíróinak nevében az utolsó d karakter helyett az m karakter (Methods) áll.
Jelezzük az ## objects szakaszban azt, hogy az objektumok a most létrehozott osztályhoz tartoznak:
## objects
do_star=1 do_star_planet4=1 do_star_planet4_moon1=1 do_star_planet4_moon2=1Itt az objektum neve elé fűzött d karakter jelzi, hogy ez a változó nem az objektum vagy annak tagja, hanem az objektum leírója és az értéke annak az osztálynak a száma, amelyhez tartozik az objektum.
Miután létrehoztunk minden objektumot és osztályt, amire a fejlesztés ellenőrzéséhez szükségünk lesz, elkezdhetjük az OOB utasításainak megírását.