🔖

try catchの使い方メモ

2024/09/22に公開

Summary

  • 闇雲に広範囲をtry catchで囲わない
  • 関連性の高い処理は1つのtry catchで
  • コケても良い処理とか影響範囲とか

コードレビューでtry catchの使い方についてフィードバックを受けたことを思い出したので自分の復習がてらまとめです。

Not Good: 広すぎるtry catch

私がやっていたのはこれ。闇雲に広いスコープをtry catchで囲っていたつもりはないのですが、もっと細かくしたほうが良いとフィードバックを受けました。

広すぎると何が良くないかというと、「catchブロック内でエラーの内容に応じてどの部分の処理でコケたかを判別し、対処する」という当たり前のことの難易度を悪戯に高くしてしまうことです。単純に考慮事項が増えますからね。

ではどのくらいまで細かくするべきか?私がフィードバックを受けたときは、可能な限り細かくしてみてほしいとまで言われました。実際にどれくらいまで細かくするかはそのアプリケーションや実装内容次第だと思うので一概にこうしたら良いという基準は見つけにくいように思います。しかしながら、下手にスコープを広げないというのは意識しておいて良いかと思います。


try {

  // 処理A
  // 処理B
  // 処理C
  // ...

} catch (error) {
  // 処理Aで起きた例外の場合の処理

  // 処理Bで起きた例外の場合の処理

  // 処理Cで起きた例外の場合の処理

  // 処理A〜Cの範囲で、共通して発生しうる例外があるとその分だけ更に判定と対処が複雑になる

}

Good: 関連性の高い処理を同じtry catchでまとめる

言わずもがなですが関連性の高い処理については同じブロックで囲っていたほうがコードとして可読性も高まり、改修もしやすいです。

try {
  // 処理A
  // 処理B:処理Aの結果を利用するなど、処理Aに関連したもの
} catch (error) {
  // 共通のエラー処理
}

try {
  // 処理C:処理A、処理Bの後続の処理ではあるが、ある程度関連性が低い
} catch (error) {
  // 共通のエラー処理
}

Consider: コケても良い処理とか影響範囲とか

スコープをどうするかという設計よりの考えとは別にして、try catchの性質から考えなければいけないことがあります。それは、try catchで囲った処理で例外が発生した時にtryブロックのその後の処理は呼び出されることが無いということです。try catchを使う際は「tryブロックで囲った範囲はどこで中断されるか分からない」ので、他の機能や処理への影響を最小限に抑えるように良いですね。

特にアプリケーション外へ働きかける処理(データの永続化や外部サービスのAPI)が複数絡んできてそれら全体で整合性を取らないといけない、つまるところトランザクションのようなことを考えないといけないときはしっかり考えておかないといけないかと思います。

try {
  // 処理A
  // 処理B: ここで例外が発生した場合、処理Cは呼び出されない
  // 処理C
} catch (error) {
  // エラー処理
}

Discussion