This text is more or less an intellectual exercise
or maybe even a nice source for discussion
Anyway I just wanted to be the heretic in the OOP room
Yes, that kind of person
Don't take this too seriously though
it's more about the way of thinking than the actual code.
Working examplecode is included.
INTRODUCTION
Several people have tried to implement OOP in bash by trying to imitate the syntax and constructs of other so called OOP languages, which always ends up as very complex, restricted, cumbersome, labour intensive and highly impractical.
Let's just don't do that, forget about other so called OOP languages
and go back to the roots of what OOP actually is:
Most people agree that a language is fully OOP if it conforms to (most of) the following rules.
In order of importance and if it is supported in bash:
- encapsulation -> yes
- messagepassing - >yes
- dynamic/late binding -> yes
- data abstraction -> yes
- objects -> yes
- classes/prototypes -> yes
- inheritance -> yes
- polymorphism -> no ( or maybe, not investigated yet)
The fundamentals of OOP in bash
As known, bash is immensely powerful but not a very subtile tool, so we have to accept this:
- every object in bash is a backgound process (encapsulation)
- messagepassing is by named pipes (or POSIX-ipc if implemented)
( I suppose Alan Kay would already acknowledge the validity of calling these 'objects' )
Let's get started
We can create a class/prototype file :
example1.class
name='John'
address='myway_or_the_highway 3'
DrawLine() { echo '----------------------' ;}
We have inheritance:
example2.class
. example1.class
name='Peter'
We have the code:
examplecode.sh
#!/bin/bash
. oop
# create instances
instantiate example1.class obj_one
instantiate example2.class obj_two
# send messages to the objects
msg_to obj_one 'DrawLine'
msg_to obj_one 'echo $name ; DrawLine'
msg_to obj_two 'echo $name ; DrawLine'
# The TERRIFYING but oh so powerful truth
msg_to obj_two 'name=Mark'
msg_to obj_two 'echo $name ; DrawLine'
# destroy instances
msg_to obj_one 'exit'
msg_to obj_two 'exit'
wait # until all subprocesses have ended
we have the output:
----------------------
John
----------------------
Peter
----------------------
Mark
----------------------
The TERRIFYING principle of this bash OOP implementation:
all it does is wrapping a classfile like this:
mkfifo instancename
. classfile
while : ; do eval "$(<"${instancename}")" ; done
And subsequently launching it as background process
Every bash command you send through the pipe is executed in this subshell
Yes, terrifying, but also immensely powerful and flexible.
And finally, this is the working oop file that was sourced in the above examplecode.sh
This is what makes the magic happen:
oop
msg_to() { echo "${2}" >"${1}"; wait ;}
instantiate()
{
local class="${1}" instance="${2}"
rm "${instance}" 2>/dev/null ; mkfifo "${instance}" || return 1
wait
(
trap "rm ${instance}" INT TERM EXIT
. "${class}"
while : ; do eval "$(<"${instance}")" ; done
) &
disown "$!"
}