2009-08-26 3 views
2

私は過去7年間のほとんどの間、オブジェクト指向プログラミングを行ってきました。最も有用なデザインパターンなど、私は優れた把握力を持っていると確信しています。実際には、次のコードは私が今実装している特定のインスタンスを処理し、将来通知された将来の要件を処理するのに十分な柔軟性を備えながら、1日のうちに小さなシステムを作り出すことを可能にしました:私の例外処理の戦略を批判します

public void importAndArchive(File detectedFile) throws FileNotFoundException, IOException { 
     File workingCopy = returnWorkingCopy(detectedFile); 
     List<String[]> csvData = csvData(workingCopy); 
     setHeaderFields(csvData.get(0)); 

     importData(csvData); //subclass will implement this abstract method 

     archiveWorkingCopy(workingCopy); 
    } 

私は、テンプレートメソッドの私の把握を自慢するために、上記を示さないのではなく、出発点としての私のOO設計能力の光の中で睨んされ、私は自分の能力を持っているギャップを議論します。そしてそのギャップは、例外処理に対する体系的なアプローチです。実際には、そのメソッドのシグネチャでは、いくつかの例外を再スローしているのが分かりますが、実際には、アプリケーションの別のコーナーで、まったく同じ種類のものを根絶するために行ってきました。

私がはるかに詳しく説明する前に、これは私の最初の試みで特に系統的であるため、これまでに行ったことの検証をしたいと思います。私たちのアプリケーションはたくさんのファイルをループし、それらを "処理"し、それらを "アーカイブ"します。かなり標準的な運賃。できるだけ早くプロトタイプを公開するために私が作った1つの決定は次のとおりです。アプリケーションは、(ResourceBundled)プロパティファイルのデータに基づいて初期化されます。 ResourceBundle APIのさまざまなメソッドはチェックされていない例外をスローしますが、アプリケーションの起動を妨げるため、当面は何も処理していないので、当面はスタックトレースで十分です。

私はCSV処理用のライブラリを選択しましたが、これはチェック例外をスローします。 NetBeansはその後、そもそもこれらの例外を増殖させることが非常に簡単に作ら;)次は私がするので、実際にこれらの例外を処理することを作り直してきた方法です。

private String detectImportHandler(File detectedFile) throws Exception { 
    String[] firstLine = null; 

    try { 
     /* 
     * CSVReader throws checked exceptions 
     */ 
     CSVReader csvReader = new CSVReader(new FileReader(detectedFile)); 
     firstLine = csvReader.readNext(); 
     csvReader.close(); 
    } 
    catch(Exception x) { 
     throw new Exception("CSVReader unable to process file: " + x.getMessage()); 
    } 

    try { 
     return firstLine[1]; 
    } 
    catch(Exception x) { 
     /* 
     * since we're re-throwing CSVReader's checked exceptions it seems easiest 
     * to also re-throw null-pointer and/or array-out-of-bounds errors 
     */ 
     throw new Exception(
       "First line null or did not have importHandlerType field: " + x.getMessage()); 
    } 
} 

上記の方法は、ループ内で、thuslyと呼ばれています私は、このマップと、それはより高いレベルで物事を処理するために含まれているファイル固有のメッセージを使用することができ、地図がある

try { 
     importHandlerType = detectImportHandler(detectedFile); 
    } 
    catch(Exception x) { 
     unusableFileErrors.put(detectedFile.getName(), x.getMessage()); 
     continue; 
    } 

unusableFileErrors、と私は、ファイルを反復処理行われていたときに私が把握:それは、ファイルを処理しますログの記録、システム上の他の場所へのファイルの移動など)。

とにかく私は十分に長く続けました。私は本"Robust Java"を注文しました。私はそれとSOコミュニティとの間で、私の能力のこのような面を改善することができます。私はSOに関する他の同様の質問を見てきましたが、私は実際のコードの文脈で具体的なアドバイスを要求することも有益かもしれないと考えています。

+0

あなたはリソースを 'finally'ブロックで閉じているべきです。 Javaの7年と最終的にブロックtut!ツタン! :) Shame JavaはC#の 'using'ブロックを持っていません – pjp

+0

あなたの最初のメソッドに対するちょっとしたコメント - 実用的な方法のように見えるのは、ファイル読み込み、CSVへの変換、CSVの保存です。責任原則*は、メソッドだけでなくクラスにも適用できます。 –

+0

Nick、それは実用的な方法ではなく、すべての適格なファイルがどのように処理されるかの本質です。あるレベルでは、そうでなければ一見無関係なステップをアルゴリズムでカプセル化するメソッドが必要です。 –

答えて

15

いくつかの発言:

  1. 例外eを包む、あなただけe.getMessage()を使用します。代わりに、私は新しい例外(メッセージ、e)を使用することをお勧めします。これにより例外の原因が設定されるため、スタックトレース全体に元の例外のスタックトレースも含まれます。これは、例外の原因をデバッグするのに非常に便利です。

  2. 例外をスローするのではなく、より明示的な例外をスローすることをおすすめします。 FileNotFoundException。 Java APIで定義されている例外を理にかなって使用してください。

  3. 例外をキャッチする代わりに、どの例外をキャッチするかを明示することをお勧めします。

  4. (オプションの2の置き換え)一部の開発者は、例外の種類ごとに例外クラスを定義するのではなく、例外の種類を示すエラーキーを含む一般的な例外をスローすることをお勧めします。新しいDetailedException(GeneralBusinessErrors.PARSING_ERROR)。これにより、ビジネスエラーの言語依存リソースファイルを簡単に割り当てることができます。

  5. 例外にデバッグデータを追加すると便利な場合があります。たとえば、ファイルが見つからなかった場合は、開こうとしたファイルなどの情報になります。これは、問題を再現できず、デバッガを使用してデバッグすることができないメンテナンスに非常に役立ちます。

  6. キャッチされていないチェックされていない例外がシステムに記録されるようにすると便利です。一般に、スローされた(キャッチされた、またはキャッチされなかった)例外をログに記録すると便利です。昔ながらのExceptionをキャッチして投げ

1

一般的に私は2つの場所のいずれかで例外を処理:私が知っているユーザずに例外を処理できる場合

  1. 、私は可能なエラーに近いそれを扱います。
  2. 出力に何らかの出力が必要な場合(ロギング、ダイアログをポップアップしてstdoutにメッセージを書き込む)、私はそれをある集中ポイントに渡します。その時点で、キャッチされて適切な出力メカニズムに配信されます。

これは私にとってはうまくいくようです。

2

小メモ:元のメッセージを追加するのではなく、実際の原因を連鎖させることをお勧めします。そうしないと、スタックトレースが失われ、デバッグが必要以上に苦しくなります。

それ以外では、detectImportHandler()内のtry/catchブロックには大きな価値はありません。メソッドはすでにExceptionをスローしているので、CSVReaderでスローされたものをリラップする必要はなく、NullPointerExceptionはtry/catchの呼び出しだけで捕捉されます。例外がreadNext()またはCSVReaderコンストラクタのいずれかによってスローされることができれば、より価値がある

何を、潜在的に、FileReader(および/またはCSVReader)を閉じfinally句だろう。

+0

+1不足している「最終的に」を見つけ出すために+1 – pjp

+0

ええこれは間違いなく私がこのすばらしい情報をふるい落とし、私の戦略を工夫する間にできることです。 –

2

は悪いです - あなたはあなたがそれらをしたいかどうか、全てランタイム例外を(おそらくない)を取得します。

私は彼らが来ているメソッドの文脈で意味をなさないチェック例外をスローしようとします。例えば、私がloadCustomer(long id)を持っていたのであれば、私はSQLExceptionではなくObjectLoadExceptionを投げます。

はい、私の好みは例外をチェックされています - メソッドのコンシューマは、例外が発生したときに何をすべきかを明示的に決定しなければなりません。想像されたコールスタックコードイホ。

6

私は私が私のAPIによって公開したかった例外に実装固有の例外を変換したい場合、通常、私はキャッチ再スローなアプローチを採用します。

たとえば、例のインポートメソッドがFileではなくURLであるとします。このURLがFileを参照した場合は、FileNotFoundExceptionをAPIに公開したくない(これは実装の詳細です)が、これをキャッチして(たとえばImportExceptionとして)再送信しますが、依然として基底のFileNotFoundExceptionにチェーンしていますダビデ)。

また、特定の場合(たとえば、SpringのDataAccessException)でも、通常はチェックされていない例外(NullPointerExceptionなど)をキャッチして再スローしません。

3

私はすでに@Kolibriに議決権を与えていますので、最初に読むことができますが、いくつか追加する必要があります。

例外処理は、黒い芸術のようなものであり、予想される方法は言語によって異なります。 Javaでは、メソッドの署名を、メソッドと呼び出し元の間の契約として見てください。

発信者が契約の終了(たとえば、 "detectedFile"をnullとして渡す)を解除した場合、未確認の例外をスローしたいとします。あなたのAPIは彼らに契約を与えており、明示的に壊れています。彼らの問題。 (無視するとFileNotFoundExceptionは現在チェックされていますが、これはJavaで持っているビーフのようなものです)

呼び出し元が適切なデータを渡して、要求を完了できない場合は、たとえば、ディスクエラー(IOExceptionなど)が原因でファイルを読み取れない場合は、契約が終了したことになり、チェック例外をスローすることになります。チェックされた例外は、呼び出し元にメソッド呼び出し時に間違っている可能性のあるすべてのものを警告します。

これは、ユーザーが例外を処理するためにどのような粒度を与えたいかによって異なります。例外を投げることは、通常は望ましくないことです。なぜなら、他の人のように、起こったことを細かく扱うことができないからです。呼び出しには、契約が破られたかどうか、またはメソッドが完了したかどうかがすぐにわかりません。例外には、チェック例外と未チェック例外の両方が含まれているためです。さらに、あなたの呼び出し側がコード内でFileNotFoundExceptionとIOExceptionを別々に扱いたいと思うかもしれません。 Exceptionを投げただけでは、これを簡単に行う能力が失われます。独自の例外を定義することで、この概念が拡張され、メソッドの呼び出し側がメソッドの例外をより詳細に処理できるようになります。ここで重要なことは、10の異なる例外を投げ、呼び出し側がその違いを気にしない場合、単に「catch(Exception e)」を実行して、それらをすべて1回で捕捉できることです。ただし、「例外をスローする」だけの場合、ユーザーは異なる例外を別の方法で処理するか、すべてをキャッチするかの選択肢がなくなります。

は、私も非常に「スロー新しいXXXException(メッセージ、E)」を使用してについてのコメントに同意する代わりに、「新しい例外を投げる(メッセージ+ e.getMessage())」。最初のメソッドを使用する場合は、内部スタックトレースを元のエラーに戻してください。 2番目の方法を使用すると、スタックトレースは「新しい例外をスローする」行までしかトレースされず、その例外を引き起こす原因を突き止めるためにさらにデバッグする必要があります。これは過去に私を噛んだ。

最後に、Exception.printStackTrace()が役立つ場合は、キャッチで使用することを恐れないでください。ユーザーはスタックトレースを見たくないので、できる限り例外を処理する必要がありますが、実際には予期しない例外で、スタックトレースを使用すると将来的に大幅にデバッグするのに役立ちます。

うわー、それはエッセイのようでした。私は今停止する。がんばろう!:-D

=====================

補遺:あなたは間違いなく...なければならない、私はちょうどPJPさんのコメント@キャッチし、それが繰り返しクマ"catch"ステートメントの後に "finally"ブロックで ".close()"メソッドを呼び出すことができます。そうでない場合、例外が発生した場合、割り当てられたリソースをただちに閉じることなくメソッドが返されます。&が開かれました。

+0

エッセイ - ishの質問に答えるに値するです。 –

3

私は問題にまだ取り上げられていないもう一つの次元があると思います。つまり、例外処理はコーディング手法だけではなく、エラー状態とその処理方法についてです。 Javaのチェック例外は混在しています。なぜなら、あなたが望んでいないときにエラー状態を考えるようになるからです。開発者の中には、コンパイラーを邪魔したくないものを悪用するよりも悪いことが起こることがあります。 (チェックされていない例外には反対の問題があります。そのため、ネットワーク接続が切断されるなどの非常に一般的なシナリオでも、エラー処理を忘れてしまいます)。

例外的に3つのクラスがあります。これらは、Javaの3つのタイプ(例外のチェック、実行時例外、エラー)に対応しています。

エラーは、アプリケーションで非常に高いレベルでのみ処理する必要があります。一般に、エラーは回復不能であるとみなす必要があります。 OutOFMemoryErrorは実際にはいくらか回復可能ですが、オブジェクトの初期化など、考えられないコードのシーケンスで間違っていて、実際には(アプリケーションサーバーなどの)コードを分離します。

ランタイム例外は、決定的なものであり、同じ入力が与えられた場合に常に間違っていなければなりません。たとえば、nullを許可しないメソッド(NullPointerException)にnull参照を渡すか、Integer.parseIntを実行すると、指定されたStringは結果が毎回同じになります。

チェックされた例外は、やはり間違っているかどうかを事前に知ることができないものです。 FileNotFoundException、例えば、Fileオブジェクトを作成したときにファイルが存在する可能性があります。チェックすることもできますが、何秒後に何かが削除される可能性があります。

これらのカテゴリを念頭に置いて例外を設計して対応する場合(すべてのAPI設計者がそれらに従うわけではないことを認識している場合 - たとえば、XML例外が特定のXML入力に対して確定的であってもチェックされます。チェック例外をすべて避けるため)、必要なエラー処理の種類を明確に考える方法があります。ネットワーク接続がダウンしたときに何が必要なのかは、JVMのメモリが不足している場合とは大きく異なります(プロジェクトに応じて異なります)。整数ですが、ユーザーの入力から1つを取得しません。

これらの用語を考えたら、どのようなタイプのエラー処理と堅牢性から、例外処理について何をすればよいのでしょうか。

4

すでにいくつかの非常に良い回答がありますが、私はさらに考えてみたいです。

すべての場合に適合するエラー処理戦略は1つではありません。

一般に、意識的に2つの根本的に反対の選択肢を選択する必要があります。選択肢は、ビルドしているものと、現在作業しているアプリケーションのレイヤーによって異なります。2つの選択肢は、

  1. フェイルファストです。すぐにエラーが発生し、あなたが持っているすべての情報を集め、それについて何かできる人に到達するようにします。あなたの呼び出しコード、例外を投げたり、ヘルプデスクに電話をかけたりしてください。データ破損が発生します。エラーをマスクするか、それに関する十分な情報を取得できないと、何がうまくいかなかったかを知ることが難しくなります。

  2. 防弾性があります。迷惑は災害になるので、関係なく運んでください。もちろん、障害に関する情報を取得して何らかの形で報告する必要がありますが、この場合は、どのように回復し、継続するかを把握する必要があります。プログラマは、彼らが最初に採用している必要があります第二の戦略を採用しているので、

多くのバグが導入されています。状況からどのように回復すべきかが明白でない場合は、おそらく例外を投げて、誰かがあなたに電話してもらうことが賢明な選択肢になるはずです。あなたは自分のものよりも何をすべきかを知ることができますが、それが何であるかを決定する情報。

アドバイスのもう1つの有用な点は、スローする例外がAPIレベルで確実に機能するようにすることです。 APIがhtml形式でデータをレンダリングするように設計されている場合、単に誰かが監査証跡を有効にすることを決めたため、データベース例外がスローされることはありません。このようなケースは、多くの場合、連鎖された例外(throw new ApiException(causeException);など)を使用して最もよく処理されます。

最後に、チェックされていない例外とチェックされている例外を取りたいと思います。私は、プログラマが間違いを犯したという状況に対して未チェックの例外を予約するのが好きです。たとえば、オブジェクト参照が必要なメソッドにnullを渡します。プログラマが完全であってもエラーが発生する可能性がある場合(たとえば、プログラマがその存在をチェックしてからファイルが削除された場合はFileNotFound)、チェックされた例外が通常適切です。基本的に、APIを作成するプログラマーは、「すべての入力が正しくても、この問題は引き続き発生する可能性があり、対処しなければならない」と言っています。

関連する問題