Вам не потрібно багато bash-коду для реалізації класів або об'єктів у bash.
Скажіть, 100 рядків.
Bash має асоціативні масиви, які можна використовувати для реалізації простої системи об'єктів з успадкуванням, методами та властивостями.
Отже, ви могли б визначити такий клас:
class Queue N=10 add=q_add remove=q_remove
Створення примірника цієї черги можна зробити так:
class Q:Queue N=100
або
inst Q:Queue N=100
Оскільки класи реалізовані з масивом, клас та інст справді є синонімами - подібними до javascript.
Додавання елементів у цю чергу можна зробити так:
$Q add 1 2 aaa bbb "a string"
Видалення елементів у змінну X може бути зроблено так:
$Q remove X
І демпінгову структуру об'єкта можна зробити так:
$Q dump
Що б повернуло щось подібне:
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=
}
Класи створюються за допомогою функції класу на зразок цієї:
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"
}
ПРИМІТКА. Під час визначення нового класу чи екземпляра ви можете переосмислити значення або функції будь-якого члена.
Асоціативні масиви Bash мають вигадку, завдяки якій ця робота є акуратною: $ Q [0]} ідентичний $ Q. Це означає, що ми можемо використовувати ім'я масиву для виклику функції відправки методу:
dispatch(){
local _this=$1 _method=$2 _fn
shift 2
_fn="$_this[$_method]" # reference to method name
${!_fn} $_this "$@"
}
Нижньою стороною є те, що я не можу використовувати [0] для даних, тому мої черги (в даному випадку) починаються з індексу = 1. Крім того, я міг би використовувати асоціативні індекси типу "q + 0".
Щоб отримати та встановити членів, ви можете зробити щось подібне:
# 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
}
І щоб скинути структуру об'єкта, я зробив це:
ПРИМІТКА. Це не потрібно для OOP в bash, але приємно бачити, як створюються об'єкти.
# 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
}
Моя конструкція OOP не враховувала об'єкти всередині об'єктів - за винятком спадкового класу. Ви можете створити їх окремо або зробити спеціальний конструктор на зразок class (). * obj_dump * потрібно буде модифікувати для виявлення внутрішніх класів для їх рекурсивного друку.
Ой! і я вручну визначаю клас ROOT для спрощення функції класу :
declare -gA ROOT=( \
[this]=ROOT \
[0]="dispatch ROOT" \
[dump]=obj_dump \
[set]="ROOT_set" \
[get]="ROOT_get" \
)
За допомогою декількох функцій черги я визначив такі класи, як цей:
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
Створив кілька екземплярів черги та змусив їх працювати:
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