web-dev-qa-db-ja.com

メソッドはパラメーターを検証する必要がありますか?

平方根法sqrtを設計するとします。渡されたパラメーターが負の数でないことを検証しますか、それとも呼び出し元に任せて、渡されたパラメーターが有効であることを確認しますか?メソッド/ APIがサードパーティの消費向けであるか、それが作業中の特定のアプリケーションでのみ使用される場合、あなたの答えはどのように異なりますか

メソッドはパラメーターを検証する必要があると私は考えていましたが、Pragmatic ProgrammerのDesign by Contractセクション(第4章)では、適切なデータ(111ページと115ページ)を渡すのは呼び出し側の責任であり、メソッドでアサーションを使用することを提案しています同じことを確認します。他の人がこれについてどう思うか知りたいです。

44
Amit Wadhwa

一般的に、私は次のようにAPIを設計します。
1。メソッドを適切に文書化し、呼び出し元に適切な/有効なデータを渡すように勧めます。
2。とにかくパラメータを検証してください! -前提条件が満たされていない場合に例外をスローします。

パラメータの検証は、ほとんどの公開APIで必要だと思います。非公開メソッドのパラメーター検証はそれほど重要ではありません-検証を公開の「エントリポイント」で一度だけ実行することが望ましいことがよくありますが、潜在的なパフォーマンスヒットに耐えられる場合は、どこでもパラメーターを検証するのが好きです。コードのメンテナンスとリファクタリングが少し簡単になります。

34
Matthew King

常にパラメーターを検証する場合は、不要な可能性がある追加の作業を行っています。

呼び出しの前に入力が既に検証されており、呼び出しのデータを再検証している状況を考えてみてください。 OK 1つの追加の検証チェックで十分ですが、そのロジックをアプリケーションのすべての関数に拡張します。以前に検証されていても(現在は複数回)、すべての関数呼び出しでデータが検証されます。

[〜#〜] one [〜#〜]ポイントでデータを検証する必要があります。これは、データがプログラム(またはサブシステム(サブシステムの定義が柔軟になる可能性があるため、ある程度の余裕を与える))に入る場所です。プログラミングエラーがなければ、コードは機能するはずです(不良コードをチェックするためのアサーションは、パラメーターの検証とは異なります)。

パラメータを検証したい場合は、2つのバージョンの関数を使用してください。検証するものと検証しないもの。 std :: vectorを見てください(at [)が検証するのに、operator []は検証しません)。

したがって、sqrt()関数を設計する必要がある場合、not入力を検証します。これは、大多数の状況ではデータはとにかく良好であり、少数の状況ではユーザーが間違っている可能性があるためです。クイックチェックを実行します(ユーザー入力と同様に、間違っている可能性があり、使用前に検証する必要があります)。それが間違っている他の唯一の時は、プログラマーのエラーのためです(そしてあなたのユニットテストはこれらを捕らえるべきです).

26
Martin York

複雑なソフトウェアでどの程度の冗長性/堅牢性を実装する必要がありますか? 関連する質問をします。

私の答えは、外部の世界とやり取りする関数(パブリックAPI、UIメソッド、ファイルリーダーなど)は入力を検証し、エラーを可能な限り丁寧かつ明確にユーザーに通知する必要があるということです。少なくとも、エラーメッセージ/リターンコードは、不正な入力が発生した場所と、違反した制約のタイプを示す必要があります。より具体的であるほど良い。

一方、内部で生成されたデータ、または外部に面する関数のいずれかによって既に処理されたデータのみを処理するプライベート関数は、正常に動作するために必要な前提条件のいずれかにアサーションを持っている必要があります。プログラマがこれらに違反するコードを書いた場合、プログラムは失敗し、一生懸命失敗するはずです。このタイプのバグは、テストの初期段階を過ぎることはありません。

ここでの動機は、ユーザーにとって可能な限り素晴らしいことであると同時に、悪い入力を処理する方法に関する決定をできるだけ高いレベルに制限することです。低レベルの関数を作成するたびに、プログラムレベルのエラー処理戦略の次の部分を理解する必要はありません。

したがって:ユーザー入力を検証し、プログラマー入力をアサートします

17
AShelly

型システムがある場合は、それを使用してください。

アサーションは、悪を早期に発見するのに役立ちます。

nullでない制約は合理的なものです。

sqrt(-1)は、一部のプログラミング言語ではエラーになりません。複素数をサポートするSmalltalkはiを返すだけです。

10
Tim Williscroft

それは実装理論ではなく、方法の適用に依存すると思います。

つまり、高速の数学ライブラリを構築している場合、たとえそれがだれでも使用する可能性があるとしても、少なくとも「リリース」モードで構築されている場合は、実行時チェックをしたくありません。基準。モード間で動作を一貫させる必要があるため、アサーションを使用して「デバッグ」モードでチェックを実装する場合があります。もちろん、そのような動作を非常によく文書化したいので、ライブラリのユーザーは(たとえ3か月後でも)、何をチェックするかtheyが実行する必要があることを知っています。

ここで、ネットワーク通信ライブラリを構築している場合は、できる限り多くのセキュリティを追加する必要があります。1)ライブラリはほとんどユーザー入力を供給されるため、危険2)生のパフォーマンスは主にネットワークI/Oによって制限されるため、ほとんどの場合、CPUの操作によるものではないため、検証を追加しても気付かれません。

8
jv42

すべての関数は入力を検証する必要があります、APIやパブリックインターフェイスの一部ではない内部関数も含みます。

プログラマーは人間であり、大規模なコードベースが進化するにつれ、関連する制約を同期させることに不慣れであることが知られています。最終的には、「関数の呼び出し」の前に発生する「入力のチェック」部分will消えるか、他の場所に移動するか、不完全であり、関数willが誤った入力で呼び出されます。これが発生した場合、上位2つの目標は次のとおりです。

  • できるだけ早く問題を検出します(コンパイル時が最良のオプションです)
  • 問題が修正されるまで何も壊さないでください

多くの場合、言語機能または型システムを使用して、検証されたプロパティに関するコンパイル時の情報を伝達できます。これは非常に高速(実行時のペナルティなし)であり、コンパイル時にエラーを検出します。たとえば、私の検証のほとんどはこのカテゴリに属しています。

使用している言語が実行中の処理のコンパイル時検証をサポートしていない場合(これは、現代の言語では非常にまれです)、ランタイムアサーションを追加します。

コードの失敗が、簡単に検出されて無害なバグ以外に悪影響を及ぼさない可能性があり、そのコードが非常に頻繁に呼び出されることが予想される場合のみそして、いずれにしても、検証は関数コードの自然な部分ではありません、検証を省略できます。 sqrtはおそらくここにあります。

2
Victor Nicollet

関数型プリプロセッサーを提供するC/C++およびその他の言語の場合、デバッグ/テスト用にビルドする場合にのみ入力パラメーターを検証し、非検証リリースビルドを生成できます。

Visual C++ MFCライブラリは、良い例の1つです。

以下は、MFCパブリックサンプルのサンプルコードです。

void CServerNode::CalcBounding(CDC* pDC, CPoint& ptStart, CSize& sizeMax)
{
    ASSERT(sizeMax.cx >= 0 && sizeMax.cy >= 0);
    ASSERT(ptStart.x >= 0 && ptStart.y >= 0);

    CSize sizeNode;
    CalcNodeSize(pDC, sizeNode);

    ptStart.y += sizeNode.cy + CY_SEPARATOR;
    if (ptStart.y > sizeMax.cy)
        sizeMax.cy = ptStart.y;

    if (ptStart.x + sizeNode.cx > sizeMax.cx)
        sizeMax.cx = ptStart.x + sizeNode.cx;
    ptStart.x += CX_INDENT;
    // add in the kids
    if (!m_bHideChildren)
    {
        POSITION pos = m_listChild.GetHeadPosition();
        while (pos != NULL)
        {
            CServerNode* pNode = (CServerNode*)m_listChild.GetNext(pos);
            pNode->CalcBounding(pDC, ptStart, sizeMax);
        }
    }
    ptStart.x -= CX_INDENT;
}
2
9dan

データベースからのデータ、HTTPからのデータ、POST、ネットワークソケットからのデータのいずれであっても、常に受信データのソースの近くに検証を配置します。

この理由は次のとおりです。

  • コードの削減
  • 不要な操作の排除
  • より単純なコード(通常)

ただし、ほとんどのプログラミングルールまたはベストプラクティスには常に例外があります。これらの例外を認識するための鍵は、一連のルールを盲目的に遵守するのではなく、各状況を慎重に検討して検討することです。

1
dietbuddha

すべての関数はその入力パラメーターの有効性を検証する必要があると思います。コンパイル時に検証できる場合(静的型付き言語を使用している場合は、型システムの助けを借りて)良いです。

アサーションを使用して入力パラメーターが有効であることを確認するという考え方は、私には少し奇妙に思えます-本質的に、同じチェックを2回(呼び出し元の関数で1回、メソッドで2回)記述する必要があることを意味しますそれ自体、アサーションの形で。これは、入力パラメーターの要件が変更された場合、呼び出し先だけでなく、呼び出し関数のすべての場所でチェックを変更する必要があることも意味します。

では、メソッド自体のパラメーターを検証し、不整合が見つかったときに例外を発生させる(または適切な処理を行う)だけではないのでしょうか。

1
user21125

私は常にシンプルなスキームを使用しています:UIとシンプルなコードでメソッドを呼び出します(UIパラメーターを検証します)->メソッド(UIパラメーターを検証しません)->追加の関数(何も検証しません)

0
cnd