fc2ブログ

本を読む

読書やコンピュータなどに関するメモ

Bash on Railsを作る(8) bashでオブジェクト指向

 bashの内蔵コマンドだけでいかにRuby on Railsっぽいことをやるかというパロディ企画です。1000speakers:2デビューを狙ってましたが、発表者枠が分速で埋まったようなので、のんびりとブログで解説します。

 今回は、予告どおり、bashでオブジェクト指向(もどき)を実現するしくみについて、使う側から解説します。bashでOO、略してbashOO(ばしょー)というところでしょうか。O/Rマッパー(もどき)など、Bash on Railsの多くの部分で利用しています。

前提:あくまでもbash

 「bashでオブジェクト指向言語を作る」のではなく、「bashの解釈系のままで、オブジェクト指向っぽい書き方をできるようにする」のを目指しました。bashOOの呼び出しは、すべてbashのコマンドとして実行されています。

基本はObjectクラス

 一般的な仕様どおり、bashOOでも、Objectクラスが最も基本的なクラスです。

 インスタンスを作るのは、クラスメソッドnewです。

Object.new objectA

 これで、インスタンスobjectAが作られます。

インスタンスメソッドを呼ぶ

 作られたインスタンスのメソッドを実行してみます。ここではclassメソッドを呼んでみます。

objectA.class

 クラスメソッドclassが呼ばれ、インスタンスが属するクラスの名前である「Object」が標準出力に出力されます。

注意:オブジェクトになるのは名前

 ここで注意したいのは、オブジェクトになるのは、変数の内容ではなくて、変数の名前だということです。たとえば、以下のように、ほかの変数に代入してメソッドを実行しようとすると、エラーになります。

objectB=objectA
objectB.class

継承クラスを定義:まずインスタンスメソッドを定義

 言語を作るわけではないので、いまのところObjectクラスには機能はほとんどありません。そこで、Objectクラスを継承して新しいクラスを作ってみましょう。ここでは、Humanクラスを作ってみます。

 まず、インスタンスメソッドhelloを定義します。インスタンスメソッドは、「(クラス名)::instance.(メソッド名)」という名前の関数として定義します。

function Human::instance.hello() {
    echo 'Hello!'
}

 引数のあるメソッドも定義できます。

function Human::instance.speak() {
    local self=$1
    local message=$2
    echo "$message"
}

 第1引数がselfなんて、PerlかPythonみたいですね。いや、それを狙ったんですが。

クラスメソッドを定義

 なんとなく気づいた方もいるかもしれませんが、クラスメソッドは、「(クラス名)::class.(メソッド名)」という名前の関数として定義します。

function Human::class.amounts() {
    local class=$1
    echo "$class lives: 6,400million"
}

 クラスメソッドの第1引数はclassです。Perlのnewみたいですね。いや、それを狙ったんですが。

継承クラスを定義:実行

 あとは、実際に継承を実行します。

Object.extend Human

 クラスメソッドを試してみましょう。

Human.amounts

 標準出力に「Human lives: 6,400million」と出力されます。

 インスタンスを作ってみます。

Human.new ore

 インスタンスメソッドを呼び出してみます。

ore.hello
ore.speak 'Hello!'

 どちらも、標準出力に「Hello!」と出力されます。

さらに継承:コンストラクタとインスタンス変数

 もちろん、Humanから継承クラスを作ることもできます。ここでは、Bikerクラスを作ってみます。

 ついでに、コンストラクタも解説します。コンストラクタは、そのクラスのinitializeというインスタンスメソッドです。Rubyっぽいですね。いや、それを狙ったんですが。

function Biker::instance.initialize() {
    local self=$1
    local name=$2
    eval "${self}_name='${name}'"
}

 インスタンス変数は、「(インスタンス名)_(メンバー名)」という変数とします。代入のためにevalを使っているのはご愛嬌ということで。

 インスタンス変数を参照するメソッドも作ってみましょう。

function Biker::instance.name() {
    local self=$1
    local name_var="${self}_name"
    echo "${!name_var}"
}

 「${!hoge}」は、変数の間接参照です。詳しくはman bashをご覧ください。

メソッドの継承

 親クラスのメソッドは上書きできます。

function Biker::instance.hello() {
    echo "Hey!"
}

 では継承クラスを作ります。

Human.extend Biker

 インスタンスを作ってみましょう。

Biker.new wyatt 'Captain America'

 新しく定義したインスタンス変数を呼んでみます。

wyatt.name
wyatt.hello

 それぞれ「Captain America」「Hey!」と出力されます。

 親クラスのメソッドは、そのまま使えます。

wyatt.speak 'Easy come, easy go.'

まとめ

 bashの解釈系(シンタックス)の中で、オブジェクト指向っぽい書き方(セマンティックス)を実現してみました。

 bashOOも、bashOO自身で実装しています。そのへんのブートストラップの仕組みは、そのうち。

コメント

コメントの投稿

管理者にだけ表示を許可する

トラックバック

https://emasaka.blog.fc2.com/tb.php/355-d23f709f

 | HOME | 

Categories

Recent Entries

Recent Comments

Recent Trackbacks

Appendix

emasaka

emasaka

フリーター。
連絡先はこのへん

Monthly