皆さんは,業務や研究などでソフト開発を行う際に,どんなプログラミング言語をお使いでしょうか。試しに筆者が勤務する会社の知人に聞いてみると,COBOL,FORTRAN,C,C++,C#,VisualBasic(VB),Java,ABAP*1といった答えが返ってきました。皆さんの中には,これらの言語のほかにPerlやPHPといったスクリプト言語をお使いの方もいるかもしれません。

 ここで紹介するRubyについて名前だけは聞いたことがあるという方も多いと思います(カコミ記事「Rubyの特徴」参照)。PerlやPHPと同じスクリプト言語です。ただし,Rubyはオブジェクト指向を意識して設計されているので,オブジェクト指向スクリプト言語と呼ばれることが多いようです。そのため,オブジェクト指向設計(Object Oriented Design)に基づいたプログラミングをする際にRubyは大きな効果を発揮します。

 筆者は,普段の業務でRubyを使っています。例えば,Javaを使うことが決まっている開発プロジェクトでもRubyを使うことがあります(カコミ記事「なぜ筆者はRubyを使うのか」参照)。それぐらいオブジェクト指向開発において有用な言語なのです。

 本稿では,Rubyを使ってUML*2のクラス図からプログラムを作る過程を通して,オブジェクト指向プログラミングのコツを説明したいと思います。先輩や上司から与えられたメモとクラス図からソフトを作るという作業は,システム開発の現場ではよくあることです。特に,新人の方やオブジェクト指向開発を始めたばかりという方には,Rubyによる実装作業は参考になる点が多いと思います。

クラス図の登場人物を整理する

 ある日,会社のB先輩から「業務で必要な小さなツールを作ってください」と言われて,UMLのクラス図(図1)とツールの概要を書いたメモ(図2)を渡されました。どうやら,CSV*3ファイルを読み込んでSQLのINSERT文を生成するツールのようです。

図1●作成を依頼されたツールのUMLのクラス図
[画像のクリックで拡大表示]

図2●ツールの概要が書かれたメモ

 実装言語は「Javaを推奨」と書いてありますが,今回は前述の通りRubyを使います。余裕があればデータベースと連携しろということですが,まずは作成したINSERT文だけをコンソールに表示するだけのアプリケーションから作りましょう。

 最初に,渡されたクラス図を眺めてみましょう。ざっと見ただけでは,それぞれの登場人物(クラス)が,どのように呼び出し合うのかがあまりよくわからないと思います。このような場合,最初にクラス図に登場する人物がそれぞれ何をするクラスなのかを整理するのが有効です。表1に登場人物を整理したものを示します。

表1●クラス図の登場人物(クラス)の概要を整理したもの(今回実装するクラスだけ)

 登場人物を整理したうえで,クラス図を再度眺め直してみると何か見えてきませんか? 例えば,SQLのINSERT文を組み立てるInsertSQLStatementBuilderクラスは,テーブル名や項目名を表すTableMetaDataクラスを使っていると考えられます。実際,クラス図をよく見ると関連のところに“use”と書いてあるので,何となくこの推測は合っているように思われます。同様に,TableMetaDataFactoryインタフェースとTableMetaDataクラスに引かれている関連に“create”と書いてあるので,TableMetaDataFactoryはTableMetaDataのインスタンスを生成していると予測できます。

Rubyの特徴

 Ruby(http://www.ruby-lang.org/ja/)は,まつもとゆきひろ氏が開発したオブジェクト指向スクリプト言語です。以下のような特徴があります。

(1)インタプリタ型
 コンパイルなしで書いてすぐに実行できるインタプリタ型の言語です。コンパイル型言語では,コンパイルに時間がかかることがありますが,その点,インタプリタ言語であるRubyは書いてすぐに実行できます。したがって手軽にプログラムを試すことができます。

(2)簡潔で美しい文法
 オブジェクト指向に基づいた一貫した文法を持っています。また,非常に簡潔かつ美しく記述できます。簡潔で美しく,一貫性のとれた記述ができますので,可読性やメンテナンス性が非常に高くなっています。

(3)Perlを引き継いだ強力なテキスト処理機能
 テキスト処理,ファイル操作,システム管理系の処理に非常に効果を発揮します。これらの機能が充実しているのは,RubyがPerlを参考にしているからだそうです。


なぜ筆者はRubyを使うのか

 実際に,筆者はRubyを業務の補助ツールとして以下のように使っています。

(1)仕事上で必要となる小さなツールを作る
(2)UMLで記述したモデルが実際に動作するかを検証する

 (1)は,テキスト処理やファイル処理に強いRubyに適した使用方法です。例えば,テスト用のRDB(リレーショナル・データベース)で利用するテスト・データの作成*Aや,コードの自動生成で使用します。テスト・データの作成では,都道府県,市町村,数値をランダムに並べて住所データを作成したり,6桁の数値と決まっている顧客IDを重複なしで生成したりします。一方のコード生成では,RDBのテーブルから自動的にJavaクラスを生成するのに使用します。このコード生成はテーブル数が多い場合,非常に開発が楽になります。

 (2)の使用方法はUMLを学び始めたときに非常に効果的でした。クラス同士の静的な関係をUMLのクラス図で記述しても,どのような順序でオブジェクトが呼び出されて動くかはわかりにくいものです。クラス図をRubyで簡単に実装してみることでクラス同士の相互作用が理解できるようになります。

 「Javaなどの本番で使用する言語で最初から実装すればいいじゃないか」という意見が聞こえてきそうですね。また,「UMLにはオブジェクト同士の動的な関係を記述するためにシーケンス図やアクティビティ図があるのだから,それを使えばいいじゃないか」という意見もあると思います。その通りです。筆者自身,UMLを覚えたてのころは,ていねいにシーケンス図をツールで書いていました。

 しかし,実際に書いたことがある方ならおわかりのように,シーケンス図やアクティビティ図は書くのは非常に面倒です。なんとか書き終えても,実行して結果がわかるわけではないので動作イメージがつかみにくく,もやもやとした気分になることが多いのです。「これだったら最初からコードを書いておけばよかった」と後悔することもありました。

 結局,業務ではJavaを使用することが多いので,シーケンス図を書くのをあきらめてJavaでモデルの動作確認をするようになりました。ところが,Javaで書くとこれまたかなり面倒くさいのです。Javaはかっちりとした言語なので,メソッドのシグネチャ,つまり戻り値の型と引数の型の順序をきちんと定める必要があります。そのうえ,例外が起こったときの処理についても決めておく必要があります。

 オブジェクト指向で設計する場合,「責務の配分」を強く意識します。責務の配分とは,誰(=クラス)がどのような仕事をする(=メソッド)かを振り分けることです。しかし,責務の配分を決めるのはそう簡単ではありません。何度か試行錯誤を繰り返した後に,ようやく決まることが多かったりします。つまり,あるメソッドをあるクラスに振り分けてみて,しっくりこないので別のクラスに振り分け直すといったことが頻繁に起こります。そのため,なるべく設計時には責務の配分については試行錯誤をしておきたいというのが本音で,責務の配分以外のこと(シグネチャや例外処理)はあまり気にしていられないのです。

 そこでRubyが登場します。Rubyは,引数の型がありませんのでメソッドの引数の型や戻り値の型は記述する必要がありません。また,Rubyでも例外処理はきちんと書けますが,Javaのように例外処理の記述が強制されるわけではありません。加えて,Rubyは非常に簡潔に記述できますので,責務の振り分けにかかわる試行錯誤も手軽に行うことができます。きれいな設計ができるようになるには,試行錯誤の過程は非常に大事なのです。もう一つ,UMLのクラス図を書くときは,ある程度実装を見越したうえで書く必要があります。Rubyで実装する経験を積めば,実装を見越したクラス図を書けるようになるはずです。責務の振り分けを意識して実装したRubyコードを参考にして,メソッドのシグネチャや例外処理を考慮したうえでJavaで実装し直せば,より安全性や信頼性の高いソースコードが出来るでしょう。