2009-04-02 5 views

答えて

14

コンストラクタも関数です - なぜ区別するのですか?

オブジェクトを作成すると、すべての整合性チェックが完了したことを意味します。コンストラクタ内のパラメータをチェックし、不正な値が検出されたら例外をスローするのは完全に合理的です。

このうち、デバッグが簡単になります。あなたのプログラムがコンストラクタ内で例外をスローすると、スタックトレースを見ることができ、しばしば原因をすぐに見ることができます。チェックを遅らせると、以前のイベントが現在のエラーを引き起こす原因を検出するために、より多くの調査を行う必要があります。

2

すべての不変条件が最初から満たされている完全に構成されたオブジェクトを持つ方が常に良いです。ただし、管理されていない言語でコンストラクタから例外をスローすると、メモリリークが発生する可能性があります。

1

コンストラクタをスローすると、間違った値がどこから来ているのかがスタックトレースによって示される可能性が高くなります。

0

私は一般的なケースではsharptoothに同意しますが、一部の機能が有効であり、一部が有効でない状態を持つことができるオブジェクトがあることがあります。このような状況では、これらの特定の依存関係を持つ関数にチェックを延期する方がよいでしょう。

通常は、オブジェクトが常に有効な状態にあるという理由だけで、コンストラクタで検証する必要があります。しかし、いくつかの種類のオブジェクトには機能固有のチェックが必要ですが、それも問題ありません。

+0

これは同じです。すべてのメソッドがすべてのオブジェクト状態で合法的に呼び出されるわけではないことは事実です。しかし、コンストラクタにまとめられたパラメータ値の組み合わせを特定し、他の組み合わせがあれば例外をスローすることもできます。 – sharptooth

+0

また、オブジェクトには常に有効なメソッドとそうでないメソッドがある場合、オブジェクトが大きすぎるかどうかを検討することもできます。たぶんそれは分割する必要がありますか? – sleske

2

私は常にコンストラクタで値を強制します。ユーザーがルールに従わなくても困らない場合は、私はそのことを知らずに黙って実行します。

したがって、107%の値を渡すと100%に設定されます。ドキュメントでは、それが起こるのは明らかです。

明白な論理強制がない場合に限り、私はそれらに例外を戻します。私はこれを「ドキュメントを読むにはあまりにも怠け者や愚かな人たちに、最も驚くべき校長」と呼ぶのが好きです。

+0

は興味深いと思いますが、これはいくつかの予期しない動作をするコードにつながりませんか? – Homes2001

+0

はい、しかし、ドコモを読んでいない人にのみです:-) – paxdiablo

+0

uhm ...これは、コードを使いにくく/理解することにつながると思います。多くの場合、 –

0

これは難しい質問です。この質問に関連する私の習慣はここ数年で変わりましたので、ここにいくつかの考えがあります:

  • 伝統的な方法の引数をチェックするのと同じです...私はここで区別しません...
  • もちろん、メソッドの引数をチェックすることは、渡されたパラメータが正しいことを確認するのに役立ちますが、あなたが書かなければならない多くのコードを導入します。 ...特に、他のメソッドにかなり頻繁に委譲する短いメソッドを書くときは、コードチェックの50%で終わると思います。
  • その他あなたがクラスの単体テストを書くとき、非常に頻繁に考えると、すべてのメソッドに対して有効なパラメータを渡す必要はありません...現行のテストケースで重要なパラメータだけを渡すのが理にかなっていますチェックをすることでユニットテストを書いても遅くなります...

私はこれが本当に状況に依存すると思います...私が最後にしたのは、パラメータが外部システム(ユーザ入力、データベース、Webサービスなど)から来ていることがわかったときだけ、パラメータチェックを書くことです。データが自分のシステムから来た場合、私はほとんどの時間テストを書いていません。

+0

第3のポイントについて:決して起こらないものをテストするのはなぜですか?コンストラクタのパラメータをチェックすると、値は決して間違った値になることはありません。だからあなたはそこでテストする必要はありません。間違ったパラメータでコンストラクタをテストするだけです。 –

0

私は可能な限り早く失敗しようとします。だから私は確かにコンストラクタのパラメータをチェックします。

+0

"私はいつもできるだけ早く失敗しようとする" = "int main(void){raise(6); ...}"? :-) – paxdiablo

+0

少なくともそれはコンパイルされました:) – idstam

0

理論的には、関数を呼び出す前に、呼び出し元コードは常に前提条件が満たされていることを確認する必要があります。コンストラクタも同じです。

実際には、プログラマは怠惰であり、前提条件を確認するのが最善です。アサーションはそこに便利です。

例。

// precondition b<>0 
function divide(a,b:double):double; 
begin 
    assert(b<>0); // in case of a programming error. 
    result := a/b; 
end; 

// calling code should be: 
if foo<>0 then 
    bar := divide(1,0) 
else 
    // do whatever you need to do when foo equals 0 

また、前提条件はいつでも変更できます。コンストラクタの場合、これは本当に便利ではありません。

// no preconditions.. still need to check the result 
function divide(a,b:double; out hasResult:boolean):double; 
begin 
    hasResult := b<>0; 
    if not hasResult then 
    Exit; 
    result := a/b; 
end; 

// calling code: 
bar := divide(1,0,result); 
if not result then 
    // do whatever you need to do when the division failed 
+0

これはちょうど括弧のないCだけではありません。それはパスカルです!彼はパスカルのコンパイラを持っています。あなたの人生のために実行します。 – paxdiablo

0

できるだけ早く必ず失敗してください。このプラクティスの良い例は、実行時に表示されます。 *配列に無効なインデックスを使用しようとすると、例外が発生します。 *後で実行しようとしないとすぐにキャスティングが失敗します。

悪いキャストがリファレンスに残っていて、例外を使用しようとするたびに災害コードが何であるか想像してみてください。賢明なアプローチは、できるだけ早く失敗し、悪い/無効な状態をできるだけ早く回避することです。

関連する問題