コードコントラクトと入力検証

受信データの検証ルールは、コードの契約と間違われることがよくあります(実際、その逆)。 これら2つの概念の違いは何か、どのような場合に適用できるかを見てみましょう。



コード契約:前提条件



コードで契約、つまり前提条件、事後条件、および不変条件を使用する目的は何ですか? この考え方は、 フェイルファーストの原則と密接に関連しています。予期しない動作に気付くほど、修正が早くなります。



ほとんどの場合、エラーから自動的に回復しようとするよりも、アプリケーションをクラッシュさせる方が効率的です。 このエラーが正確に何であり、それがアプリケーションにどのように影響するかを確実に知ることはできません。 エラーが発生した後もソフトウェアを継続すると、データベース内のデータの状態に一貫性がなくなる可能性があります。



それでは、契約の前提条件とはどのようなもので、着信データの検証とどのように違うのでしょうか? ここで重要な点は、前提条件の違反が常にクライアントコードのバグを示すということです 。 一方、不正な受信データはシステムエラーではありません。



これが主な違いであり、残りはすべてその結果です。 それらをさらに詳しく見てみましょう。



契約の前提条件と受信データの検証の違い



契約の前提条件は、すべてが順調であることを確認するために、コード内に配置された保護画面として表すことができます。 一方、受信データの検証は、外部からの「防御」のために配置された画面です。



画像








赤い信号は、ユーザーまたは他のアプリケーションからの不正な対話(または無効なデータ)を示します。 緑色の信号は、許容可能な相互作用を示します。 開発者としての目標は、無効なメロンがシステムに侵入できないようにすることです。



システム内に赤い信号が表示される場合は、着信データをフィルタリングしていないか、独自のコードが誤ったデータを生成していることを意味します。



いずれにせよ、これはシステムのバグであり、可能な限り迅速にローカライズおよび修正する必要があります。 契約はこれに役立ちます。 これらを使用すると、アプリケーション上の無効なデータの配布を停止し、バグの原因をすばやく特定できます。



画像








アプリケーションに適切な契約の前提条件(さらに良いことに、事後条件と不変条件)がある場合、無効な対話はローカライズされ、発生するとすぐに配布が停止するため、アプリケーションのデバッグが非常に容易になります。



受信データの検証は、外部からの不正なデータの侵入からアプリケーションを保護することを目的としたメカニズムです。 このメカニズムはそれが働くデータに関する仮定を全くしません 。 これは、データが無効になる可能性があることを意味し、これ自体は有効な状況です。



実際、ユーザーが整数フィールドに「10」を入力した場合、アプリケーションをクラッシュさせたくありません。 代わりに、ユーザーにエラーを丁寧に指摘します。



一方、 契約の前提条件は、システム内のデータが有効な状態にあることを前提としています 。 そうでない場合は、アプリケーションにエラーが入り込んでいます。



契約の前提条件:ベストプラクティス



契約の前提条件として考えられるものを見てみましょう。 契約は、サービスクラスによって提供される公開オファーです。 クライアントが特定のルール(前提条件)に従う場合、サービスは事後条件に記述された結果を保証します。



これは、契約の各前提条件が従わなければならない次の特性につながります。





これらのポイントをさらに詳しく見てみましょう。 最初の方法は非常に簡単です。つまり、開発者がクラスを使用する前に前提条件に慣れるために、すべての前提条件を何らかの方法で記述する必要があります。 C#では、メソッドの署名に直接コントラクトを埋め込むことはまだ許可されていないため、現時点での最善の方法は、メソッドの最初にコントラクトを記述することです。



2番目のポイントは、サービスクラスが契約を満たすためにクライアントクラスの開発者に複雑なチェックの実行を強制するべきではないことを意味します。 検証アルゴリズムが複雑な場合は、顧客が前提条件を検証するために使用できる別の方法に移動する必要があります。



public int Distribute(int amount) { Contract.Requires(CanDistribute(amount)); return DistributeCore(amount); }
      
      





次のポイントはこれから続きます。非パブリックメソッドまたはフィールドに前提条件を依存させないでください。そうしないと、クライアントはクラスと対話する前に適切なチェックを行うことができません。



最後の段落では、テスト結果の再現性について説明しています。 クラスの前提条件が外部環境(たとえば、ディスク上のファイルの存在)に依存している場合、クライアントは100%の場合にメソッドの正常な実行を達成できません。



 public string ReadFile(string filePath) { Contract.Requires(File.Exists(filePath)); // Rest of the method }
      
      





この例では、ReadFileメソッドの呼び出しからコントラクト検証の開始までの間にファイルが削除されたり、アクセスできなくなったりする可能性があり、クライアントはこれを回避するために何もできません。 このようなチェックは、連絡の前提条件ではありません。なぜなら、 クライアントコードのバグを示すものではないため、前提条件として導入することはできません。



おわりに



連絡先の前提条件と受信データの検証には、同様の目的がありますが、目的は異なります。 受信データの検証により、システムが外界から保護されていることを確認できますが、契約の前提条件はシステム内のコードを保護し、システム内のコードを迅速にローカライズして排除できるようにします。



元の記事へのリンク: C#コードコントラクトと入力検証



All Articles