Vous n'avez pas besoin de beaucoup de code bash pour implémenter des classes ou des objets dans bash.
Dis, 100 lignes.
Bash a des tableaux associatifs qui peuvent être utilisés pour implémenter un système Object simple avec héritage, méthodes et propriétés.
Donc, vous pourriez définir une classe comme celle-ci:
class Queue N=10 add=q_add remove=q_remove
La création d'une instance de cette file d'attente pourrait se faire comme suit:
class Q:Queue N=100
ou
inst Q:Queue N=100
Comme les classes sont implémentées avec un tableau, class et inst sont vraiment synonymes - un peu comme en javascript.
L'ajout d'éléments dans cette file d'attente pourrait se faire comme suit:
$Q add 1 2 aaa bbb "a string"
Supprimer des éléments dans une variable X pourrait se faire comme ceci:
$Q remove X
Et la structure de déchargement d'un objet pourrait être faite comme ceci:
$Q dump
Ce qui retournerait quelque chose comme ceci:
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=
}
Les classes sont créées en utilisant une fonction de classe comme ceci:
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"
}
REMARQUE: lors de la définition d'une nouvelle classe ou instance, vous pouvez remplacer toute valeur ou fonction de membre.
Les tableaux associatifs Bash ont une bizarrerie qui rend ce travail parfaitement ordonné: $ Q [0]} est identique à $ Q. Cela signifie que nous pouvons utiliser le nom du tableau pour appeler une fonction d'envoi de méthode:
dispatch(){
local _this=$1 _method=$2 _fn
shift 2
_fn="$_this[$_method]" # reference to method name
${!_fn} $_this "$@"
}
Un inconvénient est que je ne peux pas utiliser [0] pour les données, donc mes files d'attente (dans ce cas) commencent à index = 1. Sinon, j'aurais pu utiliser des indices associatifs tels que "q + 0".
Pour obtenir et définir des membres, vous pouvez faire quelque chose comme ceci:
# 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
}
Et pour vider une structure d'objet, j'ai fait ceci:
NOTE: Ceci n'est pas nécessaire pour la programmation orientée objet dans bash, mais il est agréable de voir comment les objets sont créés.
# 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
}
Ma conception POO n'a pas pris en compte les objets à l'intérieur des objets, à l'exception de la classe héritée. Vous pouvez les créer séparément ou créer un constructeur spécial tel que class (). * obj_dump * devrait être modifié pour détecter les classes internes afin de les imprimer de manière récursive.
Oh! et je définis manuellement une classe ROOT pour simplifier la fonction de la classe :
declare -gA ROOT=( \
[this]=ROOT \
[0]="dispatch ROOT" \
[dump]=obj_dump \
[set]="ROOT_set" \
[get]="ROOT_get" \
)
Avec quelques fonctions de file d’attente, j'ai défini des classes comme celle-ci:
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
Créé des instances de file d'attente et les a fait fonctionner:
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