読者です 読者をやめる 読者になる 読者になる
ようこそ。睡眠不足なプログラマのチラ裏です。

とある契約の備忘目録。契約による設計(Design by Contract)で信頼性の高いソフトウェアを構築しよう。

プログラミング オブジェクト指向 契約による設計 Code Contracts .NET C# VB.NET


「より堅牢で正確性の高いソフトウェアを作りたいぜ!」と願う.NETデベロッパーお待ちかねの、
契約による設計(DbC)をサポートするCode Contractsが.NET Framework4より利用できるようになります。


C#をベースとして契約による設計をサポートする「Spec#」を利用するという方法もありますが、
学習負担を軽減するためにと、マイクロソフトは言語を意識しなくても開発者が利用できるように、
Code Contractsとして.NET Frameworkで契約をサポートしてくれました。
これは、オブジェクト指向および、オブジェクト指向プログラミングが大好きな.NET開発者にとって、とても良い知らせです。
わたしも待ち望んでいたうちのひとりです。ありがとうマイクロソフト!!という気持ちでいっぱいです。
VisualStudio2010が4月12日(米国)にローンチされることが決まったことですし、
契約による設計の概念および、Code Contractsについて、すこし予習復習しておきたいと思います。



契約による設計はソフトウェアの安全性を高めるもっとも効果的な技法のひとつ

契約による設計(Design by Contract)とは、もともとEiffelというオブジェクト指向言語の設計者である
バートランド・メイヤー大先生が提唱し、同言語で組み込み実装されている機能、または概念である*1
契約による設計は、正しいソフトウェアおよびプログラムとは、要求されたこと以上のこともそれ以下のことも行わない。という考えのもと、
あらかじめプログラムが満たすべき仕様を、条件としてプログラムコード内に直接盛り込みます。
そうすることで、その義務と利益を明確にしてオブジェクトの仕様から曖昧さを排除し、ソフトウェアの安全性を高める技法です。
オブジェクトのインタフェースを厳密に定義して、オブジェクトとそれを使用するオブジェクトが
それぞれ守るべきことを記述することから、「契約」というメタファが当てられています。


契約による設計では、ある操作について満たされるべき条件(事前条件)、操作を行った後が満たすべき環境を表す条件(事後条件)、
クラスのインスタンスについて常に満たされるべき条件(不変表明)、という3種類の契約を用いる。
この3種類の契約によって仕様ををコードに直接記述してプログラム実行時にそれらの条件を満たしているかを常にチェックすることで、
パラメータや戻り値などの妥当性の保証について、呼び出し側と操作側とでの責任の所在を明確にすることができる。
契約を違反した場合、通常、何らかの例外が投げられる。 なので、この手法では問題を引き起こすコードをすぐに発見することができ、
それらに対して速やかに対処することができる。契約による設計は、契約違反なコード、
つまり仕様を満たさないようなコードを排除するシステマチックな戦略であり、
ソフトウェアの安全性を高めるもっとも効果的な技法のひとつであると言えます。



Code Contractsで契約の静的チェックを利用するには

Visutal Studio2010 Beta2で、契約の静的チェックを行うには、
以下より、CodeContractsをダウンロードし、インストールしてください。
Visutal Strudio2010のアドオンされて、Code Contractsが利用できるようになります。
プロジェクトのプロパティで、静的チェックを行うようチェックボックをチェックすれば、
契約について静的チェックを利用することができるようになります。



DevLabs - Code Contracts
http://msdn.microsoft.com/en-us/devlabs/dd491992.aspx


事前条件と事後条件
事前条件は、ある操作について、最低限の実行可能条件を定めたものです。
ルーチンを呼び出す側を束縛するもので、呼び出しても良い状態を定義します。
Code Contractsでは、Contract.Requiresなどを使います。
(Code Contractsでの事前条件には3つの形態があります。詳しくはMSDNライブラリを参照)


事後条件は、操作を行った後に、環境がどのようになっているべきかを定めたものです。
呼び出されるルーチン側を束縛し、制御を呼び出し側に返すときに保証しなければならない状態を定義します。
Code Contractsでは、Contract.Ensuresなどを使います。
(Code Contractsでの事後条件には2つの形態があります。詳しくはMSDNライブラリを参照)

string Hoge(string x)
{
    Contract.Requires(x != null);
    Contract.Ensures(Contract.Result<string>() != null);

    var result = x + "ほげ";
    return result;
}

例えば上記の例では、クライアントはnullではない文字列を引数に渡す義務があり、
クライアントは、サプライヤからnullではない文字列を戻り値として返してもらうという利益がある、
という契約がなされています。事後条件の契約が、メソッドの上の位置に記述されていますが、
これはコンパイラによって自動的にreturn文の直前にチェックが行われます。
(Contract.Result() 関数は関数の戻り値を参照するときに使われます。)


クラス不変表明(不変条件)
クラス不変表明とは、クラスについてどんな操作を施しても、常に満たされているべき条件を定めたものです。
ただし、この常にとはいついかなる時もという意味ではなく、オブジェクトが完了している状態でという意味である。
つまり、関数が呼び出されて、ルーチンワークが目的の事後条件(結果)へ向かっている間は満たされなくても構わない。


Code Contractsにおいては、ObjectInvariant(invariantは不変という意味)メソッドという仕組みを使います。
クラス不変表明を表すメソッドを表すには、ContractInvariantMethod 属性でマークします。
また、「ObjectInvariant」という名前にして、直接呼び出すことができないようにするために
アクセシビリティを protected にするのが一般的なプラクティスとなります。

[ContractInvariantMethod]
protected void ObjectInvariant()
{
    Contract.Invariant(_strategy != null);
}

このクラス不変表明を表現している特別なメソッドは、クラスそのもの「正しさ」を契約として表現していて、
オブジェクトのすべてのメソッドの最後にこの契約がチェックされ、オブジェクトに契約違反がないか確認されます。
ここで大事なのはこれを全てのメソッドつまり、関係するサブクラスのメソッドも含めて契約が適用されるということ。
クラスの深い意味的な特徴を捉えて整合性を強制することになるので、設計は慎重にするとよい。
慣れないうちは、厳格であったり複雑なな契約を強要することは避けた方がいいだろう。



インターフェイス契約
Code Contractsでは、インターフェイスに対して契約を表明することができる。


インターフェイスに関連付けられた個別のクラスを属性として指定することで利用することができる。
例えば以下のように宣言する。

    [ContractClass(typeof(IFooContract))]
    interface IFoo
    {
        int Foo(int x);
    }

    [ContractClassFor(typeof(IFoo))]
    sealed class IFooContract : IFoo
    {

        int IFoo.Foo(int x)
        {
            Contract.Requires(x > 0);
            Contract.Ensures(Contract.Result<int>() > 0);
            throw new NotImplementedException();
        }
    }

    public abstract class AbstractFooImplSample : IFoo
    {
        public virtual int Foo(int x)
        {
            return x;
        }
    }

ContractClassForAttributeによって、特定のインターフェイス(ここではIFoo)に対応する
インターフェイス契約を宣言するクラスをマークします。
ContractClassAttributeによって、そのインターフェイス契約をインターフェイスに対して適用します。
これで、インターフェイスに対する契約を関連付けすることが出来ます。


契約による設計は性悪説的思想である

契約による設計は「性悪説」に基づいた思想です。


契約による設計を行わない場合、「使う側がルールを守って使ってくれるはず」、
あるいは「実装を見て配慮して使ってくれるだろ常識的に考えて」という「性善説」によって成り立っているのに対し、
契約による設計では「人間なんてミスする生き物だし、提供側が考えてやんなきゃだめでしょ」
「使う側なんてどうせ何も考えてるわけないじゃん」という「性悪説」を前提とした考え方です。
何かすこしネガティブな考え方のような感じですが、頑丈なソフトウェアを作るという目標に対しては、
とてもポジティブな考え方であると言えます。


これはフール・プルーフという工業製品や電化製品などで、
利用者が誤って不適切な操作を行なっても危険を生じない、あるいは正常な動作を妨害されないように、
設計の段階で安全対策を施しておくという考え方に似ています。
例えば、正しい向きにしか入らない電池ボックスや、ドアを閉めなければ加熱できない電子レンジ、
底面が床についているときは加熱しないアイロンなど、人が犯しうるミスを予め想定して設計することで、
利用者に対して優しい製品となっています。性悪説を前提とすることで、良い結果が生まれています。
「人間はミスするもの」、「人間の注意力はあてにならない」という前提を持つことが、
システムの安全設計の基本として重要な基本概念であると言えます。


契約による設計は、防御的プログラミングとはまったく逆のアプローチ

契約による設計のアプローチは個々の関数について理論的な基盤をもつことで、
堅牢でかつモジュール化されたシンプルなシステム開発をするためのガイドラインを提供します。
これは面白いことに、ある意味では防御的プログラミングとは全く逆のアプローチです。
防御的プログラミングでは、引数がnullではないとか、パラメータの値が有効範囲内であることだとか、
できるだけ多くのチェックを行うことによって各モジュールを保護するという方針で進められる。
しかし、この防御的プログラミングという方法ではオブジェクト間の義務と利益。その責任の所在が曖昧になりやすい。
これは、ソフトウェアの複雑さを招く副作用があることを示している。


それに対して、契約による設計は、誤りやすい一貫性の条件を明確に指定して
それを守る責任をルーチンの呼び出し元(クライアント)と、ルーチンの実装(サプライヤ)に明示的に割り当てることで、
オブジェクト間の義務と利益にかかわる複雑性について、明確にすることができる。
つまりこれは、契約を用いることでインターフェイス仕様が強力で且つ内容の深いものになることを意味します。
そして、事前条件で契約されている事項については、すでに保証されていることなので、
防御的プログラミングを用いて再度ルーチン内でチェックをしてはいけない(する意味がない)ことも意味しています。



契約による設計における下請け契約(subcontractiong)の概念を理解する。

契約による設計においける下請け契約の概念は、オブジェクト指向の継承関係(is-aの関係)と似ています。
あるオブジェクトの仮想関数における契約が、継承先で再定義されるとき、
その仮想関数における契約について下請けに出されると考えらる。
つまり、再定義された仮想関数が少なくともオリジナルの契約を満たさなければならない
という点に置いて、再定義された関数は契約についてもセマンティクスを保持した変換である必要があります。
また、再定義された関数がオリジナルの契約を満たすだけではなく、それ以上のことを行ってもよいことを意味します。
たとえば、オリジナルの契約で否定していた条件を許容したり、オリジナルの契約よりも
より優れた結果を返す事後条件を契約とするなどです。
つまり、契約による設計における下請け契約の考え方では、
サブクラスによって再定義された事前条件は、より広くを受け入れてオリジナルの契約よりも厳しくあらず、
事後条件は、より多くのことを行い、オリジナルの契約よりも強化されなければなりません。


オブジェクト指向言語Eiffelでは、契約による設計と継承を統合するための規則が言語レベルで適用されていて、
require elseおよびensure thenという構文において、事前条件は弱められ、
事後条件は強化されることを保証するようにできています。
しかし、.NET Framework4で提供されるCode Contractsでは、この概念はどうも保証されないようです。
つまり、Code Contractsにおいては、事前条件をオリジナルの契約よりも強くすることが可能だし、
事後条件を弱めることも可能なようになっているようです。
これは、ある意味では自由度の高さを表わしているが、厳密な契約による設計の概念とはズレていると言えるでしょう。
Code Contractsの利用者は、契約による設計について、
誤りをおかす可能性が含まれていることを、あらかじめ理解しておく必要があります。


契約による設計とテスト駆動開発(TDD)

契約による設計では、問題を引き起こすコードを早期発見して即座に対処することができる。
そういう意味では、ユニットテストを頻繁に行うテスト駆動開発に相通じる戦略である
と考える人も多いかもしれない。が、その趣旨や性質はテスト駆動開発のそれとは異なる。
テスト駆動開発はテストやそのオブジェクトの表明を保証をするための技法ではなく、開発方法あるいは設計技法です。
それに対して、契約による設計は仕様そのものであり、ソフトウェアの安全性を高めようという技法である。
つまり、そのアプローチと求めている結果がまったく異なる技法なのです。
テスト駆動開発を行う場合、契約による設計を意識して併用して行うことで、
よりクライアント側からの使われ方を意識したインターフェイス。オブジェクトの責務やリレーションを意識したインターフェイス
が期待できます。また、契約を盛り込むことで、テスト駆動を行う上でのTODO事項や、考慮漏れへの気付き。
あるいは、準備すべきテストコードの考えがまとめやすくなる。テストの資産価値を評価しやくなる。などの効能が期待できる。



設計者が契約による設計をためらう場合、そしてその対処法
契約による設計は堅牢なコードを保って信頼できるソフトウェアを作る上でとても強力な技法だが、
場合によっては適用が難しい場合があり、それをためらってしまうこともあるだろう。
マーチン・ファウラ大先生がblikiの「実例による仕様書」内で語っているように、
すべての仕様について契約としてコードに盛り込むというのは、現実問題として難しい場合もある。
クラスの責務によっては、複雑になったり表現が難しくなることも少なくない。
条件式の計算内容がヘビーだったり、本来のコードよりも契約の計算の方がトリッキーなんてことも珍しくないでしょう。


「すべての仕様について契約されている状態」が理想なのはもちろんではあるが、
「すべてについて契約が保証されているべきであり、必ず守られなければならない」ものだとは思わない。
単純なヒューマンエラーの防止やTDD支援的な用途として、契約を利用するというのもアリだと思う。
契約の条件が複雑であったり、契約をコードとして記述するにはとても割に合わないというような場合は、
オブジェクトの責務や設計に誤りがある可能性もあるので、再度設計について考えることを検討する。
検討してみたが、オブジェクトの設計に変更がないような場合もある。その場合は、
契約条件の詳細をコメントとして記述しつつ、防御的プログラミングのアプローチを適用する。
そのあたりはケースバイケースで対処するものだと思う。


実例による仕様書
http://capsctrl.que.jp/kdmsnr/wiki/bliki/?SpecificationByExample


最後に
ここ最近は契約にハマっていました。C#およびVB.NETで契約しまくって遊んでいました。*2
契約はオブジェクト指向について、さらなる深みを与えてくれる重要な概念だと思っています。 
Code Contractsは.NETで開発するソフトウェアの品質向上には絶対に欠かせない要素になると感じています。
契約楽しい!契約最高!です。みなさんも契約による設計を学んで、ぜひ有効活用しましょう。

*1:また、登録商標でもあるらしい

*2:現在のところF#ではCode Contractsは利用できないようです。