コードコントラクトを使用する理由


26

私は最近、Microsoftのコードコントラクトのフレームワークに出会いました。

私は少しのドキュメントを読んで、「静的分析を実行できないことが多いため、なぜこれを実行したいのか」と絶えず尋ねてきました。

今、私はすでに次のような例外を守って、ある種の防御的なプログラミングスタイルを持っています。

if(var == null) { throw new NullArgumentException(); }

また、NullObject Pattern alotを使用していますが、問題はほとんどありません。ユニットテストを追加すれば、セットアップは完了です。

アサートを使用したことも、見逃したこともありません。まったく逆です。意味のない主張がたくさんあるコードは本当に嫌いです。これは、私にとってはノイズであり、本当に見たいものから気を散らすものです。コード契約、少なくともマイクロソフトのやり方はほとんど同じです-さらに悪いことです。それらはコードに多くのノイズと複雑さを追加します。とにかく、99%で例外がスローされます。そのため、それがアサート/コントラクトのどちらなのか、実際の問題なのかは気にしません。まさに、プログラムの状態が実際に破損するケースはほとんどありません。

率直に言って、コードコントラクトを使用する利点は何ですか?何かありますか?すでにユニットテストとコードを防御的に使用している場合、契約を導入することはコストの価値がなく、メンテナーがそのメソッドを更新するときに呪いをかけるコードにノイズを入れていると感じます。無駄なアサートのため。私はまだその代価を払う正当な理由を見ていません。



1
なぜ「静的解析を実行せず、しばしば実行できない」と言うのですか?私はそれがこのプロジェクトのポイントの1つだと思った(単にアサートまたは防御的な例外のスローを使用するのとは対照的に)?
Carson63000

@ Carson63000文書を注意深く読むと、静的チェッカーは多くの警告を大量に出力し、最も不必要であることがわかります(開発者はそうであると認めています)。 。
ファルコン

@Falcon:奇妙なことに、私の経験はまったく異なります。静的分析は完璧に機能するわけではありませんが、非常にうまく機能します。警告レベル4(最高)で作業している場合でも、不必要な警告はほとんど表示されません。
アルセニムルゼンコ

私はそれがどのように振る舞うかを知るためにそれを試さなければならないと思います。
ファルコン

回答:


42

静的な型付けが動的な型付けよりも優れているのはいつかと尋ねたかもしれません。終わりの見えない何年にもわたって激怒している議論。したがって、動的に型付けされた言語と静的に型付けされた言語の研究のような質問を見てください

しかし、基本的な議論は、コンパイル時に問題を発見し修正する可能性があることです。

契約対警備員

ガードや例外があっても、システムが意図したタスクを実行できない場合があるという問題に直面しています。おそらく非常に重要で失敗するのに費用がかかるインスタンスです。

契約と単体テスト

これに関する通常の議論は、テストがバグの存在を証明する一方で、タイプ(契約)が存在しないことを証明するというものです。F.ex. プログラム内のパスが無効な入力を提供する可能性がないことがわかっているタイプを使用しますが、テストでは、カバーされたパスが正しい入力を提供したことのみを伝えることができます。

コントラクトとヌルオブジェクトパターン

今、これは少なくとも同じ球場にあります。ScalaやHaskellなどの言語は、プログラムからnull参照を完全に排除するこのアプローチで大きな成功を収めています。(Scalaが正式にnullを許可している場合でも、規約ではnullを使用しないことです)

すでにこのパターンを使用してNREを排除している場合、実行時エラーの最大の原因を基本的に削除したことになります。基本的には、契約で許可されている方法です。

違いは、コントラクトにはヌルを避けるためにすべてのコードを自動的に要求するオプションがあるため、コンパイルを渡すためにより多くの場所でこのパターンを使用することを強制することです。

その上、契約により、nullを超えた対象を柔軟に設定できます。したがって、バグにNREが表示されなくなった場合は、契約を使用して、次に発生する可能性がある最も一般的な問題を絞ることができます。1つオフですか?インデックスが範囲外ですか?

しかし...

言われているすべて。コードに追加される構文上のノイズ(および構造上のノイズ)コントラクトは非常に重要であり、分析がビルド時間に与える影響を過小評価すべきではないことに同意します。したがって、システムにコントラクトを追加することにした場合、どのクラスのバグに対処しようとするかに焦点を絞って慎重に行うのが賢明でしょう。


6
それはプログラマーにとって素晴らしい最初の答えです。コミュニティにご参加いただきありがとうございます。

13

「静的解析を実行しないことが多い」という主張がどこから来たのかはわかりません。アサーションの最初の部分は明らかに間違っています。2つ目は、「多くの場合」の意味によって異なります。私はむしろ、しばしば静的分析を実行し、めったに失敗しないと言いたいです。通常のビジネスアプリケーションでは、neverに近づくことめったにありません

ここに、最初の利点があります。

利点1:静的分析

通常のアサーションと引数のチェックには欠点があります:コードが実行されるまで延期されます。一方、コードコントラクトは、コーディング段階で、またはアプリケーションのコンパイル中に、はるかに早いレベルで現れます。エラーを早期に発見すればするほど、修正費用は安くなります。

利点2:常に最新のドキュメントのようなもの

コード契約は、常にドキュメントの一種を提供します最新のします。メソッドのXMLコメントは場合はSetProductPrice(int newPrice)それが伝えるnewPrice優れていることや、ゼロに等しくなければならない、あなたはドキュメントが最新であることを願っていますが、あなたも誰かがなるようにする方法を変更することを発見するかもしれnewPrice = 0投げるArgumentOutOfRangeExceptionが、対応するドキュメントを変更することはありません。コードコントラクトとコード自体の相関関係を考えると、非同期のドキュメントの問題はありません。

コードコントラクトによって提供されるドキュメントの種類も貴重であり、多くの場合、XMLコメントでは許容値を十分に説明できません。何回思った?nullstring.Emptyまたは\r\nメソッドの認可値であり、XMLのコメントは、その上静かでした!

結論として、コードコントラクトがない場合、多くのコードは次のようになります。

一部の値は受け入れますが、他の値は受け入れませんが、ドキュメントがある場合は、推測または読む必要があります。実際には、ドキュメントを読んでいない:それは時代遅れです。すべての値をループするだけで、例外をスローする値が表示されます。また、返される可能性のある値の範囲を推測する必要があります。これは、過去数年間に私に加えられた何百もの変更を考えると、それらについてもう少しお話ししたとしても、真実ではない場合があるためです。

コードコントラクトでは、次のようになります。

title引数には、0..500の長さのnull以外の文字列を指定できます。続く整数は正の値で、文字列が空の場合にのみゼロになる場合があります。最後に、IDefinitionnullではないオブジェクトを返します。

利点3:インターフェースの契約

3番目の利点は、コードコントラクトがインターフェイスを強化することです。次のようなものがあるとしましょう:

public interface ICommittable
{
    public ICollection<AtomicChange> PendingChanges { get; }

    public void CommitChanges();

    ...
}

アサートと例外のみを使用して、空でないCommitChangesときにのみ呼び出すことができることをどのように保証しPendingChangesますか?それPendingChangesが決してないことをどのように保証しますかnullますか?

利点4:メソッドの結果を実施する

最後に、4番目の利点はContract.Ensure、結果が得られることです。整数を返すメソッドを記述するときに、値が決してゼロ以下にならないことを確認したい場合はどうすればよいですか?5年後を含め、多くの開発者からの多くの変更に苦しんだ後?メソッドに複数のリターンポイントがあるとすぐに、Assertsはそのためのメンテナンスの悪夢になります。


コードコントラクトは、コードの正確さを意味するだけでなく、より厳密なコード記述方法としても考慮してください。同様に、動的言語のみを使用した人は、言語レベルで型を強制する理由を尋ねる一方で、必要に応じてアサーションで同じことを行うことができます。ただし、静的型付けは使いやすく、多数のアサーションと比較してエラーが発生しにくく、自己文書化できます。

動的型付けと静的型付けの違いは、通常のプログラミングとコントラクトによるプログラミングの違いに非常に近いです。


3

私はこれが質問に少し遅れていることを知っていますが、言及することに耐えるコード契約にはもう1つの利点があります

契約はメソッド外でチェックされます

メソッド内にガード条件がある場合、それはチェックが行われ、エラーが報告される場所です。次に、スタックトレースを調べて、実際のエラーの原因を特定する必要があります。

コードコントラクトはメソッド内にありますが、何かがそのメソッドを呼び出そうとすると、メソッドの外側で前提条件がチェックされます。コントラクトはメソッドの一部になり、呼び出すことができるかどうかを決定します。つまり、実際の問題の原因により近いエラーが報告されます。

多くの場所で呼び出されるコントラクト保護されたメソッドがある場合、これは本当の利点になります。


2

本当に2つのこと:

  1. ツーリングサポート、VSは契約データをインテリジェンスとは別に作成できるため、オートコンプリートヘルプの一部となります。
  2. サブクラスがメソッドをオーバーライドすると、コントラクトがすべてのチェック(LSPに従う場合に有効なはずです)が失われ、コントラクトは子クラスに自動的に従います。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.