4

私は前提条件とそれらをいつ使用するかについて学んでいます。前提条件は

@pre fileName must be the name of a valid file

次のコードには適合しません。

/**
Creates a new FileReader, given the name of file to read from.
@param fileName- the name of file to read from
@throw FileNotFoundException - if the named file does not exist,
is a directory rather than a regular file, or for some other reason cannot
be opened for reading.
*/
public FileReader readFile(String fileName) throws FileNotFoundException {
. . .
}//readFile

どうしてこれなの?

編集:別の例

例として、次のことが「正しい」方法で行われると想定しています。IllegalArgumentExceptionと前提条件に注意してください。動作がどのように明確に定義されているか、および前提条件が設定されている場合でもthrows宣言がどのように行われるかに注意してください。最も重要なことは、NullPointerExceptionの前提条件が含まれていないことに注意してください。もう一度、なぜそうではないのですか?

/**
* @param start the beginning of the period
* @param end the end of the period; must not precede start
* @pre start <= end
* @post The time span of the returned period is positive.
* @throws IllegalArgumentException if start is after end
* @throws NullPointerException if start or end is null
*/
public Period(Date start, Date end) f

これらの例は、余分な前提条件の使用を回避していますか?前提条件を回避しているのなら、なぜそれらを持っているのでしょうか。つまり、すべての前提条件を@throws宣言に置き換えてみませんか(それらを回避することがここで行われている場合)?

4

3 に答える 3

4

ウィキペディアでは、前提条件を次のように定義しています。

コンピュータプログラミングでは、前提条件は、コードの一部のセクションを実行する直前、または形式仕様の操作の前に常に真でなければならない条件または述語です。

前提条件に違反すると、コードのセクションの効果が未定義になり、意図した作業を実行する場合と実行しない場合があります。

あなたの例では、ファイル名が無効な場合のメソッドの効果定義されています(それはをスローする必要がありますFileNotFoundException)。

言い換えると、file有効であることが前提条件である場合、それは常に有効であり、例外を義務付ける契約の一部がスローされることは決して適用されませんでした。到達不能仕様の場合は、到達不能コードと同じようにコードの臭いです。

編集

いくつかの前提条件があり、これらの条件に対して定義された動作を提供できる場合は、そうするとよいのではないでしょうか。

もちろんですが、Hoareによって定義された前提条件ではなくなりました。正式に言えば、メソッドに事前条件preと事後条件があるということは、状態で開始され、状態で終了しpostたメソッドの実行ごとに、prestatepoststate

pre(prestate) ==> post(poststate)

含意の左側が偽である場合、これは何であるかに関係なく自明に真poststateです。つまり、メソッドは、それが何をするかに関係なくそのコントラクトを満たします。つまり、メソッドの動作は定義されていません。

さて、メソッドが例外をスローできる現代に早送りします。例外をモデル化する通常の方法は、それらを特別な戻り値として扱うことです。つまり、例外が発生したかどうかは事後条件の一部です。

しかし、例外は実際には到達不能ではありませんね。

throws句がpostcondtionの一部である場合、次のようになります。

pre(prestate) ==> (pre(prestate) and return_valid) or (not pre(prestate) and throws_ exception)

これは論理的に同等です

pre(prestate) ==> (pre(prestate) and return_valid)

つまり、throws句を記述してもかまいません。そのため、私はその仕様ケースを到達不能と呼びました。

例外は、ユーザーが契約を破った場合に何が起こるかをユーザーに通知するための前提条件の補足として機能するということです。

いいえ; throws句は契約の一部であるため、契約が破られても重要ではありません。

もちろん、前提条件に関係なく@throws句が満たされる必要があると定義することは可能ですが、それは役に立ちますか?検討:

@pre foo != null
@throws IllegalStateException if foo.active

の場合、例外をスローする必要がありfooますnullか?古典的な定義では、誰もパスしないと想定しているため、未定義nullですfoo。あなたの定義では、すべてのthrows節でそれを明示的に繰り返す必要があります。

@pre foo != null
@throws NullPointerException if foo == null
@throws IllegalStateException if foo != null && foo.active

合理的なプログラマーがそのメソッドに渡さないことがわかっnullている場合、仕様でそのケースを指定するように強制する必要があるのはなぜですか?発信者にとって役に立たない行動を説明するには、どのような利点がありますか?(呼び出し元がfooがnullかどうかを知りたい場合は、メソッドを呼び出してNullPointerExceptionをキャッチするのではなく、自分で確認できます!)

于 2011-04-19T19:42:19.657 に答える
3

わかりました、これが私が見つけたものです:

バックグラウンド

Bertrand Meyer の著書Object Oriented Software Constructionで説明されているように、次の原則に基づいています 。

「非冗長性の原則 いかなる状況においても、ルーチンの本体がルーチンの前提条件をテストすることはありません。」- バートランド・メイヤー

「前提条件の可用性ルール ルーチンの前提条件に表示されるすべての機能は、ルーチンが利用可能なすべてのクライアントで利用可能でなければなりません。」- バートランド・メイヤー

、これらの 2 つのポイントは、この質問に答えます。

  1. 前提条件が有用であるためには、クライアント (メソッドのユーザー) がそれらをテストできる必要があります。
  2. システムが複雑になるため、サーバーは前提条件をテストしないでください。ただし、システムのデバッグ時にこのテストを行うためにアサーションがオンになっています。

いつ、なぜ、どのように前提条件を使用するかについての詳細:

「契約による設計の中心は、ルーチンの適切な機能を危険にさらす可能性のある一貫性条件について、この条件の施行を契約の2つのパートナーのうちの1つだけに割り当てる必要があるという考えであり、非冗長性の原則として表現されています。 • 顧客に責任を割り当てる場合、その条件はルーチンの前提条件の一部として表示されます • または、サプライヤーを指名する場合、その条件は条件付き命令に表示されますif condition then ...、または同等の制御構造をルーチンの本体に記述します。

前者は要求が厳しく、後者は寛容と言えます」 - バートランド・マイヤー

したがって、前提条件は、クライアントが責任を負うと判断された場合にのみ存在する必要があります。サーバーは前提条件をテストすべきではないため、動作は未定義になります (ウィキペディアにも記載されています)。

回答

  • 最初のポイントは最初の例に答えます。
  • 2 番目の例については、おそらく正しい方法で行われていません。これは、最初の@throws宣言が、メソッドが (アサーション以外で) 前提条件をテストしたことを暗示しているためです。これは 2 番目の点に違反しています。

ヌルポインタに関しては; これは、null ポインターの責任がサーバーに割り当てられていることを示しています。つまり、「厳しい態度」ではなく、「寛容な態度」を使用することです。これで全然OKです。要求の厳しい態度を実装することを選択した場合は、throws 宣言を削除し (ただし、より重要なのは、それをテストしないことです)、前提条件宣言 (およびおそらくアサーション) を追加します。

于 2011-04-20T19:39:36.677 に答える
2

コントラクトによる設計 (私自身はまだ使用していません) と pre/post 条件は、メソッドから出入りする特定の条件を保証することを目的としていると思います。特に、コンパイラ (この場合、理論的には Java には組み込みがないため) は、契約条件を検証できる必要があります。ファイルの前提条件の場合、ファイルが外部リソースであり、クラスが移動し、同じファイルが存在しない可能性があるため、これを行うことはできません。コンパイラ (またはプリプロセッサ) はどのようにそのような契約を保証できますか?

一方、コメントにのみ使用している場合は、少なくとも他の開発者に期待どおりの結果が表示されますが、ファイルが終了しない場合に例外が発生することも期待する必要があります。

1件でも検証できないので、契約による意匠という形式的な意味での方法は「合わない」と思います。つまり、ある環境では有効なファイル名を指定できますが、プログラムの外部の別の環境では有効でない場合があります。

一方、日付の例では、事前条件と事後条件は、メソッド自体が制御できない外部環境設定の影響を受けないため、呼び出し元のコンテキストで検証できます。

于 2011-04-19T21:49:16.183 に答える