Пишу, по большей части, про историю, свою жизнь и немного про программирование.

Бу: ООП в bash

ООП к «башу» приделать и до меня пытались, ничего в этом смысле нового, но мне ни одна реализация не нравится, из того, что я видел, всё примитивное и синтаксис кривой, всё время хотелось свою сделать. Вчера вечером заморочился, думал на выходных что-то дописывать буду, не хотел никуда выкладывать, но чуть случайно не стёр основной файл и решил, что пора выложить на «Гитхаб» от вреда подальше.

Долго мучался с названием, решил, что пусть пока называется «Бу» (b-oo.sh), выложил туда же пример на котором вчера тестировал:

#!/bin/bash
# Evgeny.Stepanischev Jan 2013 /

. b-oo.sh

@Class Base
    @Dim cnt

    @Dim
    __construct() {
        This[cnt]=0
    }

    @Dim
    IncCnt() {
        let 'This[cnt]++'
    }

    @Dim
    GetCnt() {
        [ "$This[cnt]" -gt 1 ] && s=s
        echo $Self said $This[cnt] time$s
    }
@End

@Class Dog Base
    @Dim
    say() {
        $This.IncCnt
        echo 'Bow-wow!'
    }
@End

@Class Car Base
    @Dim
    say() {
        $This.IncCnt
        echo 'Beep'
    }
@End

@Class Proxy
    @Dim obj

    @Dim Static
    getInstance() {
        $1.New Self[obj]
        @Ret $2 $Self[obj]
    }
@End


Proxy.getInstance Car car # создание через «фабрику», через статический метод
Proxy.getInstance Dog dog # создание через «фабрику», через статический метод

$car.say
$car.say
$car.GetCnt # 2, вызывается из родительского класса 

$dog.say
$dog.GetCnt # 1, вызывается из родительского класса

Dog.New anothedog # создание через конструктор

$anothedog.say
$anothedog.say

$anothedog.GetCnt # 2, вызывается из родительского класса

Я не стал заморачиваться с модификаторами доступа (хотя может ещё и сделаю), из реализованного: создание объектов, наследование (есть множественное), статические и динамические методы и свойства, конструктор, есть переменная This (текущий объект), Self (текущий класс) и Parent (чтобы вызывать родительские методы).

Всё делается при помощи вызовов, начинающихся с собачки. Вряд ли синтаксис кого-то поставит в тупик, но есть несколько важных замечаний.

Во-первых, «@Dim», определящий свойства должен находиться на той же строке, что и имя свойства, а в случае метода имя метода должно быть записано на следующей строке (смотрите пример).

Во-вторых, в шеле всё уныло с передачей значения в переменную, обычные способы чаще всего создают так называемый «сабшел», поэтому передать значение в переменную следующим образом не получится:

# !неправильно
obj=$(Dog.New)
obj=`Dog.New`

Чтобы выполнить Dog.New интерпретатор создаёт отдельный процесс, где всё и выполняется, после выхода из этого процесса всё моё колдунство исчезает и от переданного значения толку нет (если у вас не «баш 4», тогда есть способы).

Поэтому конструктор у меня принимает первым параметром имя переменной, куда нужно положить экземпляр класса, а облегчения жизни программисту сделана специальная команда «@Ret», посмотрите как она используется в методе getInstance.

Естественно, в глобальном пространстве создаётся куча всякой магии, но у всего этого есть свои префиксы.

2 комментария
malfer.mu 2013

Господи, боже мой.
А что нельзя сделать на баше?

Евгений Степанищев (bolknote.ru) 2013

Комментарий для http://malfer.mu:

Легко и приятно программировать без напряга нельзя :) Язык с миллионом особенностей :)