Bạn không cần nhiều mã bash để triển khai các lớp hoặc đối tượng trong bash.
Nói, 100 dòng.
Bash có các mảng kết hợp có thể được sử dụng để thực hiện một hệ thống Object đơn giản với sự kế thừa, phương thức và thuộc tính.
Vì vậy, bạn có thể định nghĩa một lớp như thế này:
class Queue N=10 add=q_add remove=q_remove
Tạo một thể hiện của Hàng đợi này có thể được thực hiện như thế này:
class Q:Queue N=100
hoặc là
inst Q:Queue N=100
Vì các lớp được triển khai với một mảng, lớp và inst thực sự là từ đồng nghĩa - giống như trong javascript.
Thêm các mục vào hàng đợi này có thể được thực hiện như thế này:
$Q add 1 2 aaa bbb "a string"
Việc xóa các mục vào một biến X có thể được thực hiện như sau:
$Q remove X
Và cấu trúc bán phá giá của một đối tượng có thể được thực hiện như thế này:
$Q dump
Mà sẽ trả lại một cái gì đó như thế này:
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=
}
Các lớp được tạo bằng hàm lớp như thế này:
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"
}
LƯU Ý: Khi xác định một lớp hoặc thể hiện mới, bạn có thể ghi đè bất kỳ giá trị hoặc hàm thành viên nào.
Các mảng kết hợp Bash có một cách giải quyết giúp công việc này gọn gàng: $ Q [0]} giống hệt với $ Q. Điều này có nghĩa là chúng ta có thể sử dụng tên mảng để gọi hàm gửi phương thức:
dispatch(){
local _this=$1 _method=$2 _fn
shift 2
_fn="$_this[$_method]" # reference to method name
${!_fn} $_this "$@"
}
Mặt trái là tôi không thể sử dụng [0] cho dữ liệu để hàng đợi của tôi (trong trường hợp này) bắt đầu từ index = 1. Ngoài ra, tôi có thể đã sử dụng các chỉ số kết hợp như "q + 0".
Để có được và thiết lập thành viên, bạn có thể làm một cái gì đó như thế này:
# 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
}
Và để kết xuất một cấu trúc đối tượng, tôi đã thực hiện điều này:
LƯU Ý: Điều này không bắt buộc đối với OOP trong bash, nhưng thật tuyệt khi thấy các đối tượng được tạo ra như thế nào.
# 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
}
Thiết kế OOP của tôi đã không xem xét các đối tượng trong các đối tượng - ngoại trừ lớp kế thừa. Bạn có thể tạo chúng một cách riêng biệt hoặc tạo một hàm tạo đặc biệt như class (). * obj_dump * sẽ cần được sửa đổi để phát hiện các lớp bên trong để in đệ quy chúng.
Oh! và tôi tự định nghĩa một lớp ROOT để đơn giản hóa chức năng lớp :
declare -gA ROOT=( \
[this]=ROOT \
[0]="dispatch ROOT" \
[dump]=obj_dump \
[set]="ROOT_set" \
[get]="ROOT_get" \
)
Với một vài hàm hàng đợi, tôi đã định nghĩa một số lớp như thế này:
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
Tạo một số trường hợp Hàng đợi và làm cho chúng hoạt động:
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