酒匂 寛(さこう ひろし)
ソフトウェア開発コンサルタント。(有)デザイナーズデン代表取締役。要求記述やシステムモデリング,既存システムのリエンジニアリング,リファクタリング,フレームワーク設計,テスト,構成管理,文書化,プロジェクト管理などに関するコンサルティング,教育,開発,著述,翻訳等を手がける。対象ドメインはエンタープライズ・システムから,組み込み機器まで多岐に渡る。最近の関心事は信頼性の高い仕様記述手段の普及と実践。

 オブジェクト指向言語といえば,現在ではC++やJavaを思い浮かべる人が多いでしょう。こうした言語で正しいオブジェクト指向プログラミングを行うには,よく考えてクラスの設計を行う必要があります。ただ,正しい設計を行う能力は一朝一夕には身に付きません。こうした訓練に適した言語はないのでしょうか。あります。それがEiffelです。Eiffelは,正しいクラス設計をプログラマに強制します。

 Eiffelは,1985年にBertrand Meyer(バートランド・メイヤー)博士が考案したプログラミング言語です。その設計思想をひもといていくと,言語設計者がたどる一つの道筋が見えてくると同時に,ソフトウエア開発が満たすべき原則も見えてきます。

 Eiffelを有名にしたのは,その設計思想を解説した書籍「オブジェクト指向入門」(第1版はアスキー発行,第2版は2006年に翔泳社が発行予定)の功績によるものです。この本は,オブジェクト指向分野におけるロングセラーになっています。Eiffelという言語そのものは,CやJavaなどのメジャーな言語に比べるとずっと無名です。しかし,オブジェクト指向技術に関する議論では参照モデルの一つとしてよく取り上げられ,他の言語に大きな影響を与え続けています。

 例えばEiffelは,Java SE 5でようやくJavaに取り入れられた「Generics(総称)」を当初から導入していました。同様に,Javaの仕様記述を補完するJML(Java Modeling Language)やUMLのモデリング精度を高めるために導入されたOCL(Object Constraint Language)が記述する各種表明(事前条件,事後条件,不変表明)も,Eiffelは言語の機能として最初から持っています。

高品質のソフトを開発するための言語

 書籍「オブジェクト指向入門」は,品質の定義から始まっています。言語設計に臨んで,まずは「何を」解決したいのかをはっきりさせようということです。ソフトウエア開発では,問題領域の「課題」,問題解決の「仕様」,そして解決手段の「設計」を切り分けて議論することはとても大切です*1

 言語設計者はそれぞれの目的を心に秘めています。何を目的にするかによって,言語にどのような機能を取り入れるかの選択基準は変わります(これが,ソフトウエアの開発言語が唯一つに定まらない理由でもあるのですが)。Meyer博士がEiffelを設計した目的は「作られるソフトウエアの品質を向上させたい」という一言に集約できます。

 「オブジェクト指向入門」では,まず最終的に利用者にとって大切な「正確さ」「頑丈さ」「拡張性」「再利用性」「互換性」「可搬性」「効率性」といった外的品質要因を確認しています。そのうえで,こうした外的品質要因を“縁の下の力持ち”として支えるために必要な言語構造を一つひとつ検討しています。

 目的とする「高い品質」を確保するために,言語が支援しなければならない要素を検討した結果,Eiffelは表1に示す言語特性を持つよう設計されることになりました。この中には,今では常識になっているものが多数含まれていることがわかります。ただし,Eiffelの肝ともいえる各種表明(事前条件,事後条件,不変表明)が言語レベルで組み込まれているプログラミング言語は,まだほとんどありません。表明はソフトウエアの仕様記述と関連が深く,分析設計レベルでもきちんと扱うべきものです。この記事を通してその重要さに気づいていただければと思います。

表1●Eiffelが持つ言語特性。現在では常識になっている概念も多い
表1●Eiffelが持つ言語特性。現在では常識になっている概念も多い
[画像のクリックで拡大表示]

Eiffelのプログラムに触れてみる

 さて,あまり硬い話ばかりでも退屈ですね。Eiffelを触ったことがない読者がほとんどだと思いますので,お約束の「Hello, world!」プログラムを見ていきましょう。実際には「Hello, Eiffel.」というメッセージを出力することにします。

 リスト1の内容を,hello.eというファイルに記述してください(「.e」はEiffelの標準的な拡張子です)*2。これが「Hello, Eiffel.」を出力するHELLOというクラスの定義になります。

indexing
 description: "hello object"
 author: "Foo Bar"
 date: "2006/10/01"
 revision: "1.0"

class
 HELLO
 
feature -- Access

 message : STRING is "Hello, Eiffel."

 greeting is
  do
   io.put_string(Current.message)
  end
end
リスト1●「Hello, Eiffel.」というメッセージを出力するEiffelのプログラム(hello.e)

 このソースコードは三つのセクションから構成されています。indexing,class,そしてfeatureです。indexingは,このクラスの様々な情報をタグ付けして提供する部分です。実行には関係ありませんが,ツールによる情報抽出を想定しており,言語仕様の一部になっています。なお「-- Access」のように「--」で始まる部分は,そこから行の終わりまでがコメントとして扱われます。

 次のclass句で,クラスの名前「HELLO」を指定しています。ここでは総称パラメータ,継承なども指定できます。

 クラスの中心となるのが,次のfeature(特性)です。ここに属性やルーチンを定義します。まず「greeting is」で始まる部分を見てください。doとendにはさまれた「io.put_string(Current.message)」の部分が,実際の手続きに相当します。

 ioは,標準入出力を扱うSTD_FILESクラスのインスタンスです。上位のクラスANYで定義され,これを継承しています。このソースコードではHELLOに上位クラスを指定していませんが,指定がない場合にはANYというクラスを指定したものと解釈されます。

 put_stringはioオブジェクトの特性で,その名前の通り文字列を受け取って出力します。引数としてCurrent.messageを与えています。Eiffelでは,Currentは「特性を実行しつつあるオブジェクトそのもの」を表します。JavaやC++でいうところのthisに相当します。なので,Current.messageは,現在のHELLOインスタンスのmessage特性を参照して「Hello, Eiffel.」という値を返します。

 さて,勘のいい方なら「ところで実行はどこから始まるの?」という疑問を持ったのではないでしょうか。プログラムを実行するためには,誰かがHELLOクラスのインスタンスを作り,greeting特性を呼び出してくれなければなりません。

 皆さんおなじみのJavaでは「static public voidmain( )」のエントリから実行が始まります。一方Eiffelでは,実行の始まりはAce(Assembly of Classes in Eiffel)という設定ファイルで指定します*3リスト2に,このプログラムに必要なAceファイルの例を示します。「システムの実行をROOT_CLASSのmake特性から開始する」ことを指定しています。

System
 name: hello_test
 location: ~ プロジェクトの場所~
 ace file: ~ Ace ファイルのパス名~
Root class
 ROOT_CLASS (root_cluster): make
リスト2●サンプル・プログラムの実行に必要なAce(Assembly of Classes in Eiffel)という設定ファイルの例

 ということは,プログラムの実行のためにはROOT_CLASSも必要だということです。ROOT_CLASSはリスト3に示す内容になります(indexingは省略)。

class
 ROOT_CLASS
create
 make
feature -- Initialization
 make is
  -- Creation procedure.
 local
  hello : HELLO
 do
  create hello
  hello.greeting
 end
end -- class ROOT_CLASS
リスト3●サンプル・プログラムの実行に必要なROOT_CLASSクラス(root_class.e)

 ROOT_CLASSクラスには,HELLOクラスにはなかったcreateというセクションが存在しています。ここにコンストラクタ(生成手続き)の名前を指定します。他の言語とは異なり,Eiffelでは複数の生成手続きを用意することが可能で,オブジェクト生成の際にそれらを明示的に指定できます。このことからEiffelにおける生成手続きは,デザインパターンでいうところのファクトリ・メソッドを体現しているといえるでしょう。生成手続きの本体は,普通の特性としてfeatureの中に定義します。ここではmakeという生成手続きを定義しています。システムが起動すると,Aceファイルで指定した通りにmakeルーチンが呼ばれます。

 makeの中身をもう少し見てみましょう。localとdoの間には,特性ルーチンの中で使うローカル変数を宣言します。ここでは,HELLO型のhelloという名前の変数を宣言しています。ルーチンの実行が始まった時点では,helloにはまだ何のオブジェクトも関連付けられていません。「create hello」という命令が,helloにHELLOクラスから新しいオブジェクトを作って関連付けます(オブジェクトの生成に失敗すると例外が起こります)。オブジェクトが無事に生成されると,次に「hello.greeting」というコードが実行されます。これはHELLOクラスに定義されているgreeting特性の呼び出しですから,この時点でオブジェクトがメッセージを出力します(図1)。

図1●EiffelStudio 5.7でサンプル・プログラムを実行した結果。コマンドプロンプトにメッセージが表示される
図1●EiffelStudio 5.7でサンプル・プログラムを実行した結果。コマンドプロンプトにメッセージが表示される