bash에서 클래스 또는 객체를 구현하기 위해 많은 bash 코드가 필요하지 않습니다.
100 줄이라고하자.
Bash에는 상속, 메서드 및 속성을 사용하여 간단한 Object 시스템을 구현하는 데 사용할 수있는 연관 배열이 있습니다.
따라서 다음과 같은 클래스를 정의 할 수 있습니다.
class Queue N=10 add=q_add remove=q_remove
이 큐의 인스턴스 작성은 다음과 같이 수행 될 수 있습니다.
class Q:Queue N=100
또는
inst Q:Queue N=100
클래스는 배열로 구현되므로 class 와 inst 는 실제로 동의어입니다.
이 대기열에 항목을 추가하는 방법은 다음과 같습니다.
$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]을 사용할 수 없으므로 큐 (이 경우)가 index = 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
}
그리고 객체 구조 를 덤프 하기 위해 이것을 만들었습니다.
참고 : 이것은 bash의 OOP에 필요하지 않지만 객체가 어떻게 만들어 지는지를 보는 것이 좋습니다.
# 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