Konstruktor
A konstruktor néhány parancs összessége, amelyek célszerű használatával új prototípust és osztályt alakíthatunk ki. A parancsoknak meg kell valósítaniuk az öröklődést, a tagok kezelését és az osztály lezárását, azaz megvédését a további módosításoktól.
Az első lépés a leendő osztály üres leíróinak elkészítése. A konstruktor parancsai ezeket töltik fel az adattagok és a metódusok létrehozásához szükséges információkkal. Amint létrehozunk akár csak egyetlen objektumot is ezekből a leírókból, a leíróknak le kell záródniuk.
Új leírók készítése
Megírjuk az új leírók elkészítését végző segédfüggvényt a basic.oob (verzió: 0.1.1) fájlba:
oob_newDescriptors() { # result: new descriptors and pointers to descriptors
dclass="${EPOCHREALTIME/,/_}"; declare -gA "dc_${dclass}_d=()" "dc_${dclass}_m=()" # create new class number and descriptors
declare -gn dc_new_d=dc_${dclass}_d dc_new_m=dc_${dclass}_m # create pointers of descriptors
} # oob_newDescriptors()
oob_newDescriptors # create new empty class descriptors just in startA segédfüggvény létrehozza az új osztály azonosítóját, amely a létrehozás időpontját tartalmazza nanomásodperc pontossággal. Így az osztály azonosítója már nem egy sorszám, hanem az időbélyeg lesz, amely nagyon nagy valószínűséggel egyedi osztályazonosítót eredményez a Földön. A basic.oob fájl betöltésekor azonnal létrehozzuk az üres oszályleírókat, hogy ezzel ne kelljen a felhasználónak foglalkoznia.
Példányosítás
Ha kialakítottuk az osztályt, létrehozhatjuk belőle az új objektumainkat. A parancsot a basic.oob (verzió: 0.1.2) fájlba írjuk:
oob_new() { # input: name of new objects in same class # create new objects and new empty proto descriptors
local i ref
for i in ${!dc_new_d[@]}; do ref+=" self_$i=\${self}_$i"; done # collect reference
[[ -n $ref ]] && ref="local -n$ref;"; ref="local self=\${FUNCNAME%_*}; $ref" # set reference commands
for i in ${!dc_new_m[@]}; do dc_new_m[$i]="${dc_new_m[$i]/\{/\{ $ref :; }"; done # prepare methods
for i; do oob_cpOneObject $i class; done # create new objects
oob_newDescriptors # create new empty dclass descriptors after close the previous
} # oob_new()A parancs először beolvassa az adattagok neveit és összeállítja belőlük a referenciákat tartalmazó parancsokat a ref helyi változóba. Majd a változó tartalmát az elválasztó üres utasítással együtt befűzi a metódusok elejére. Ezután az argumentumokat az új objektumok neveiként értelmezve darabonként meghívja az oob_cpOneObject utasítást, hogy az osztályból, mint mintából létrehozza az új objektumokat. Mivel az oob_cpOneObject nem ellenőrzi, hogy a minta objektum-e, így ugyanúgy megszerzi az osztály azonosítóját, mint egy objektumból. A parancs végül meghívja az új osztályt létrehozó utasítást, ezzel le is zárja az osztályt. A továbbiakban az osztály azonosítóját már csak az osztályhoz tartozó objektumok tárolják. Így, ha az osztályhoz tartozó összes objektumot töröljük, (egyszerű módszerekkel) már nem férünk hozzá az osztályhoz.
Öröklődés
Az OOB megengedi a többszörös öröklődést, sőt, a minták használatával teljes struktúrákat másolhatunk egyetlen osztályba. Az esetleges névütközésket nem kezeli, tehát az újabb öröklődés felülírhatja a már meglévő adatokat. Az örökléődés technikája egyszerű: be kell másolni az ős osztály leíróit az új osztály leíróiba. A metódusok esetén a referenciákat tartalmazó utasításokat el kell távolítani. Az objektumok minden metódusába beleírt :; utasítás, mint szakaszhatár megvédi a metódus tényleges utasításait a törléstől. Az öröklődés parancsát a basic.oob (verzió: 0.1.3) fájlba írjuk:
oob_parents() { # input: pattern of objects # copy objects into class descriptors
local i dobj t; local -a oob_list
for i; do # one pattern
oob_filter $i || continue # get objects
for dobj in ${oob_list[@]}; do # one object
local -n dc_parent_d=dc_${!dobj#d}_d dc_parent_m=dc_${!dobj#d}_m # get descriptors the parent object
for t in ${!dc_parent_d[@]}; do dc_new_d[$t]="${dc_parent_d[$t]}"; done # inherit data
for t in ${!dc_parent_m[@]}; do dc_new_m[$t]="{ ${dc_parent_m[$t]#*:;}"; done # inherit methods
done # one object
done # one pattern
} # oob_parents()A parancs az argumentumokat az oob_filter számára átadandó mintaként értelmezi. Az oob_list tömb elemeihez tartozó leírókat beolvassa és hozzáadja az új osztályleírókhoz.
A develop.sh fájlt ismét kicseréljük:
#!/usr/bin/env oob ## objects do_starX=1 # do_star_planet4=1 do_star_planet4_moon1=1 do_star_planet4_moon2=1 # attributes declare -gA o_starX_info=([name]=Sun ) # methods o_starX_getName() { local self=${FUNCNAME%_*}; local -n self_info=${self}_info; :; echo "${self_info[name]}"; } o_starX_sayHello() { local self=${FUNCNAME%_*}; local -n self_info=${self}_info; :; echo "Hello $(${self}_getName)!"; } ## 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]}"; }' ) ## run main() { oob_parents o_starX oob_new o_star o_star_planet4 o_star_planet4_moon1 o_star_planet4_moon2 o_star_planet4_moon1_info=( [name]=Phobos ) oob_code > $HOME/user-objects } : set -x; main "$@"; set +x
Tagok kezelése
A létező objektumok osztályainak összemásolásával tudunk kombinált osztályokat és azokból új objektumokat létrehozni. Még meg kell valósítsuk a felesleges tagok törlését és az újak létrehozását. A következő parancsok egy-egy műveletet végeznek az adattagok vagy a metódusok megadott sorozatán. A parancsok első argumentuma kötelezően -d vagy -m, attól függően, hogy adattagokkal vagy metódusokkal végzünk műveletet.
Tagok törlése
Szükség lehet az öröklődéssel feleslegesen bekerült tagok törlésére. A parancs a basic.oob (verzió: 0.1.4) fájlban:
oob_rmTags() { # input: -d|-m name of tags # remove data or method tags from class descriptors
local i mode="$1"; shift
case $mode in # select data or methods
-d) for i; do unset dc_new_d[$i]; done;; # remove data tags into descriptor
-m) for i; do unset dc_new_m[$i]; done;; # remove method tags into descriptor
*) oob_trap "Bad option:" "$mode"; return 1;;
esac # select data or methods
} # oob_rmTags()A teszteléshez átírjuk a develop.sh fájl ## run szakaszát:
## run
main() {
oob_parents o_starX; oob_rmTags $@
oob_new o_star o_star_planet4 o_star_planet4_moon1 o_star_planet4_moon2
o_star_planet4_moon1_info=( [name]=Phobos )
oob_code > $HOME/user-objects
}
: set -x; main "$@"; set +xTagok másolása
Célszerű néha egy-egy örökölt tagot átnevezni. Mivel tagot törölni már tudunk, csak a tagok másolását kell megírnunk. Az ezt végző parancs argumentuma (az első kivételével) <új tag>=<tag> alakú. A parancs a basic.oob (verzió: 0.1.5) fájlban:
oob_cpTags() { # input: -d|-m new-tag=tag... # copy data or method tags in class descriptors
local i mode="$1"; shift
case $mode in # select data or methods
-d) for i; do dc_new_d[${i%%=*}]="${dc_new_d[${i#*=}]:-"-g"}"; done;; # copy data tags into descriptor
-m) for i; do dc_new_m[${i%%=*}]="${dc_new_m[${i#*=}]:-"{ :; }"}"; done;; # copy method tags into descriptor
*) oob_trap "Bad option:" "$mode"; return 1;;
esac # select data or methods
} # oob_cpTags()Ha a másolandó tag nem létezik, akkor is létrejön az új tag. Ha adattag, akkor mint egyszerű változó, ha metódus, akkor egy semmit sem végző függvényként. Ehhez a bash :- operátorát használjuk, amely a változó értéket helyettesíti a mögötte álló kifejezéssel, ha a változó nem létezik, vagy üres.
A teszteléshez átírjuk a develop.sh fájl ## run szakaszát:
## run
main() {
oob_parents o_starX; oob_cpTags $1 temp=$2; oob_rmTags $@; oob_cpTags $@=temp
oob_new o_star o_star_planet4 o_star_planet4_moon1 o_star_planet4_moon2
o_star_planet4_moon1_info=( [name]=Phobos )
oob_code > $HOME/user-objects
}
: set -x; main "$@"; set +xTagok létrehozása
Szükségünk van teljesen új tagok létrehozására is. Ennek argumentumai (az első kivételével) <név>='<érték>' alakúak. Adattag esetén az érték az adattag típusa. Ügyeljünk rá, hogy ez a -g karakterekkel kezdődjön! Ez biztosítja, hogy az adattag globális legyen. Metódus esetén az érték a metódus végrehajtható kódja a kapcsos zázójelekkel együtt. Itt különösen fontos, hogy a kód aposztrófok között legyen, így védjük a véletlen behelyettesítéstől. Ha a kód belseje aposztrófot tartalmaz, akkor helyette a \x27 karaktersorozatot kell használni. Írjuk meg a parancsot a basic.oob (verzió: 0.1.6) fájlba:
oob_setTags() { # input: -d|-m name of tags & value # create new data or method tags in class descriptors
local i mode="$1"; shift
case $mode in # select data or methods
-d) for i; do dc_new_d+=( [${i%%=*}]="${i#*=}" ); done;; # create data tags into descriptor
-m) for i; do dc_new_m+=( [${i%%=*}]="${i#*=}" ); done;; # create method tags into descriptor
*) oob_trap "Bad option:" "$mode"; return 1;;
esac # select data or methods
} # oob_setTags()A develop.sh fájl most már nem tartalmaz előre megírt objektumokat és osztályokat, minden objektumot és szerepet az OOB parancsaival hozunk létre:
#!/usr/bin/env oob
## run
main() {
oob_setTags -d info="-gA"
oob_setTags -m sayHello='{ echo "Hello $(${self}_getName)!"; }' getName='{ echo "${self_info[name]}"; }'
oob_new o_star o_star_planet4 o_star_planet4_moon1 o_star_planet4_moon2
o_star_planet4_moon1_info=( [name]=Phobos )
oob_code > $HOME/user-objects
}
: set -x; main "$@"; set +xLétrehoztuk a használható OOB rendszert. Itt az ideje tehát, hogy a basic.oob verziószámát felemeljük a fejlesztési szintről a használható szintre. Így a basic.oob fejléce a következő lesz:
#
# OOB internal
# basic.oob, version 1.0.0
# https://oob.xport.hu
# basic commands for Object Oriented Bash