Sie benötigen nicht viel Bash-Code, um Klassen oder Objekte in Bash zu implementieren.
Sagen wir 100 Zeilen.
Bash verfügt über assoziative Arrays, mit denen ein einfaches Objektsystem mit Vererbung, Methoden und Eigenschaften implementiert werden kann.
Sie könnten also eine Klasse wie diese definieren:
class Queue N=10 add=q_add remove=q_remove
Das Erstellen einer Instanz dieser Warteschlange kann folgendermaßen erfolgen:
class Q:Queue N=100
oder
inst Q:Queue N=100
Da Klassen mit einem Array implementiert werden, sind class und inst wirklich Synonyme - ähnlich wie in Javascript.
Das Hinzufügen von Elementen zu dieser Warteschlange könnte folgendermaßen erfolgen:
$Q add 1 2 aaa bbb "a string"
Das Entfernen von Elementen in eine Variable X kann folgendermaßen erfolgen:
$Q remove X
Die Dump-Struktur eines Objekts könnte folgendermaßen aussehen:
$Q dump
Welches würde so etwas zurückgeben:
Q {
parent=Queue {
parent=ROOT {
this=ROOT
0=dispatch ROOT
}
class=Queue
N=10
add=q_add
remove=q_remove
0=dispatch Queue
}
class=Q
N=4
add=q_add
remove=q_remove
0=dispatch Q
1=
2=ccc ddd
3=
4=
}
Klassen werden mit einer Klassenfunktion wie der folgenden erstellt:
class(){
local _name="$1:" # append a : to handle case of class with no parent
printf "$FUNCNAME: %s\n" $_name
local _this _parent _p _key _val _members
_this=${_name%%:*} # get class name
_parent=${_name#*:} # get parent class name
_parent=${_parent/:/} # remove handy :
declare -g -A $_this # make class storage
[[ -n $_parent ]] && { # copy parent class members into this class
eval _members=\"\${!$_parent[*]}\" # get indices of members
for _key in $_members; do # inherit members from parent
eval _val=\"\${$_parent[$_key]}\" # get parent value
eval $_this[$_key]=\"$_val\" # set this member
done
}
shift 1
# overwrite with specific values for this object
ROOT_set $_this "$@" "0=dispatch $_this" "parent=${_parent:-ROOT}" "class=$_this"
}
ANMERKUNG: Wenn Sie eine neue Klasse oder Instanz definieren, können Sie jeden Mitgliedswert oder jede Funktion überschreiben.
Assoziative Bash-Arrays haben eine Besonderheit, die diese Funktion sehr übersichtlich macht: $ Q [0]} ist identisch mit $ Q. Dies bedeutet, dass wir den Array-Namen verwenden können, um eine Methode-Dispatch-Funktion aufzurufen:
dispatch(){
local _this=$1 _method=$2 _fn
shift 2
_fn="$_this[$_method]" # reference to method name
${!_fn} $_this "$@"
}
Ein Nachteil ist, dass ich [0] nicht für Daten verwenden kann, sodass meine Warteschlangen (in diesem Fall) bei index = 1 beginnen. Alternativ hätte ich assoziative Indizes wie "q + 0" verwenden können.
Um Mitglieder zu bekommen und zu setzen , könnten Sie Folgendes tun:
# basic set and get for key-value members
ROOT_set(){ # $QOBJ set key=value
local _this=$1 _exp _key _val
shift
for _exp in "$@"; do
_key=${_exp%%=*}
_val="${_exp#*=}"
eval $_this[$_key]=\"$_val\"
done
}
ROOT_get(){ # $QOBJ get var=key
local _this=$1 _exp _var _key
shift
for _exp in "$@"; do
_var=${_exp%%=*}
_key=${_exp#*=}
eval $_var=\"\${$_this[$_key]}\"
done
}
Und Dump eine Objektstruktur, habe ich dies:
HINWEIS: Dies ist für OOP in bash nicht erforderlich, aber es ist schön zu sehen, wie Objekte erstellt werden.
# dump any object
obj_dump(){ # obj_dump <object/class name>
local _this=$1 _j _val _key; local -i _tab=${2:-(${#_this}+2)} # add 2 for " {"
_tab+=2 # hanging indent from {
printf "%s {\n" $_this
eval "_key=\"\${!$_this[*]}\""
for _j in $_key; do # print all members
eval "_val=\"\${$_this[\$_j]}\""
case $_j in
# special treatment for parent
parent) printf "%*s%s=" $_tab "" $_j; ${!_val} dump $(( _tab+${#_j}+${#_val}+2 ));;
*) printf "%*s%s=%s\n" $_tab "" $_j "$_val";;
esac
done
(( _tab-=2 ))
printf "%*s}\n" $_tab ""
return 0
}
In meinem OOP-Design wurden keine Objekte innerhalb von Objekten berücksichtigt - mit Ausnahme der geerbten Klasse. Sie können sie separat erstellen oder einen speziellen Konstruktor wie class () erstellen. * obj_dump * müsste geändert werden, um interne Klassen zu erkennen und diese rekursiv zu drucken.
Oh! und ich definiere manuell eine ROOT-Klasse, um die Klassenfunktion zu vereinfachen :
declare -gA ROOT=( \
[this]=ROOT \
[0]="dispatch ROOT" \
[dump]=obj_dump \
[set]="ROOT_set" \
[get]="ROOT_get" \
)
Mit ein paar Queue-Funktionen habe ich einige Klassen wie folgt definiert:
class Queue \
in=0 out=0 N=10 \
dump=obj_dump \
add=q_add \
empty=q_empty \
full=q_full \
peek=q_peek \
remove=q_remove
class RoughQueue:Queue \
N=100 \
shove=q_shove \
head_drop=q_head_drop
Einige Warteschlangeninstanzen erstellt und funktionsfähig gemacht:
class Q:Queue N=1000
$Q add aaa bbb "ccc ddd"
$Q peek X
$Q remove X
printf "X=%s\n" "$X"
$Q remove X
printf "X=%s\n" "$X"
$Q remove X
printf "X=%s\n" "$X"
class R:RoughQueue N=3
$R shove aa bb cc dd ee ff gg hh ii jj
$R dump