今回は、C++ では実現しにくい面があるけれども、 考え方としては非常に役立つと思われる開発手法を紹介する。
「契約による設計」 (Design by Contract、以下、DBC と略記) とは、 もともと Eiffel*1 というオブジェクト指向プログラミング言語の設計者である Bertrand Meyer氏が提唱し、同言語に実装した概念であるらしい。 同氏が設立した ISE という会社の Web サイトを見ると、なぜか、"Design by Contract" に、 TM が付いていたりする。まあ、それはともかく。
DBC では、ある操作を実行するのに必要な条件や、 操作を実行する前後で変わってはならない条件などを記述し、 プログラムの実行時にその条件をチェックすることができる。 こうすることによって、パラメータや戻値などの値の妥当性の保証について、 呼び出し側と操作側とで、責任の所在を明確にすることができるのである。
条件が満たされなかった場合は、通常、何らかの例外が投げられる。 したがって、問題を引き起こすようなコードはすぐに発見することができ、 問題のフィックスも速やかに行うことができるというわけである。 何か間違ったことをやったときにすぐに分かるというのは、 単体テストを頻繁に行う "Test first" にも通じる戦略である。
契約には、次の3種類がある。
操作について、最低限の実行可能条件を定めたもの。たとえば、実数の平方根を 返すという関数に対しては、double sqrt( double num )というのが事前条件になる。関数呼び出しの際に、 この条件が満たされていなかった場合、通常は何らかの例外が投げられる。 C++ でこの機能を実装するには、関数の入り口のところで、num >= 0assertを行うか、 あるいは明示にif文で条件をチェックして、 成立していなかったら例外を投げることになるだろう。
操作を行った後に、環境がどのようになっているべきかを定めたもの。たとえば、 つねに偶数を返すという関数については、int even( int num )resultを戻値とすると、というのが事後条件となる。C++ でこの機能を実装するのは難しい。 コメントの形で事後条件を記述しておき、何らかのプリプロセッサを通して、 ソースコードを変換するような仕組みが必要になるだろう。result % 2 == 0
クラスについて、どんな操作を施しても、常に満たされているべき条件を定めたもの。 たとえば、平衡木の一種であるB木では、ルートから任意のキーにアクセスするパス (=枝) の長さは同一である、という条件が不変表明になるだろう。
これらの条件は、クラスのインターフェイス (C++ でいうと、 純粋仮想関数だけからなる、抽象基底クラス) に対して定義された場合には、 それを継承する実装クラスにも自動的に適用されるべきものである。 このような仕組みは C++ では用意されていないので、 完全に実現するのは不可能である。 しかし、コンパイルの前にソースコード変換をしたり、 プリプロセッサを駆使したりして、それらしいことができるツールも いくつか存在するようである。(たとえば、 GNU Nana)
ところで、Amazon で、"Design by Contract" で検索をかけたところ、
というのが見つかった。まだ先のことであるが、出版されるのが楽しみな本である。Design By Contract Bertrand Meyer (著) この本の出版予定日 2001/10/15。
初出: 2001年3月25日