はじめに
言語の進歩は日々目覚ましいものです。
昔の常識は今の非常識かもしれないというところで例外処理論争が起きたので起稿しました。
オプショナルチェーン(?.)とは
「オプショナルチェーン(?.)」1は、JavaScript ES2020 で追加された、名前を知らないとググれない2超便利演算子です。
入れ子になったオブジェクトのプロパティにアクセスする時に、null や undefined が含まれているかもしれないオブジェクトが含まれている時に重宝する演算子です。
コードを見た方が早いと思うので、オプショナルチェーンを使った場合と使わない場合のコードを以下に書きます。
- オプショナルチェーンあり
const someValue = someConfig?.subConfig?.subsubConfig?.someValue;
- オプショナルチェーンなし
let someValue = null;
const subConfig = someCofig.subConfig;
if(subConfig) {
if(subConfig.subsubConfig) {
someValue = subConfig.subsubConfig.someValue
}
}
オプショナルチェーンを使ったコードの方が見通しがだいぶすっきりしますね!
Javaにはオプショナルチェーンないんだけど
われわれ職業エンジニアは、案件ごとで使う言語が変わります。
javascript を書いた後で、java を書くと「堅牢」というよりは「手数が多い」なぁと感じたりするかもしれません。javaにはオプショナルチェーンは存在しないので、上記の例であれば、通常は if文を使って書くことになります。
int someValue = 0;
SubConfig subConfig = someCofig.getSubConfig();
if(subConfig != null) {
SubsubConfig subsubConfig = subConfig.getSubsubConfig();
if(subsubConfig != null) {
someValue = subsubConfig.getSomeValue();
}
}
そうだ、例外処理を使おう
// オプショナルチェーンのように書ける
int someValue = 0;
try {
someValue = someCofig.getSubConfig().getSubsubConfig().getSomeValue();
} catch(Exception e) {
// default
}
try - catch で書けば、オプショナルチェーンのように書けて見通しがだいぶすっきりしますね!
外部設計で 『subsubConfig に設定されている someValueを取得する、取得できなかったらデフォルト値で処理を行う』 と定義されているのであれば、subConfig や subsubConfig が null だった時の異常系処理は未定義ですし、設計意図にも近いコードとなるでしょう。
また、外部設計として未定義の条件分岐をわざわざ書いて、それに対するカバレッジを担保するというのは工数を圧迫し品質の低下を招くリスクになるかもしれませんよね?
ところで例外処理を気軽に使っていいの?
例外処理とはなんぞやをWikiから引用しておきます。
(出典 : Wiki : 例外処理)
例外処理(れいがいしょり、英語: exception handling)とは、IT業界で用いられる専門用語で、システムの設計で想定されておらず、ユーザー操作によって解決できない問題に対処するための処理である。例外処理の結果として問題が解決されないとシステム障害になる。システム停止やデータ破損の原因になり、ユーザーに損害を与える可能性があるため、システム開発で例外処理は重要視されている[1][2][3]。
システムの設計で想定されておらず、継続不能や継続すると問題になる様な状態としては、次のようなものが挙げられる。
・ハードウェアの故障
・オペレーティングシステム等、システムの設定ミス
・ライブラリの欠損
・ユーザーの入力間違い
・数値入力を要求している場合での、英単語の入力
・存在しないデータベースのテーブル/カラムやファイルの指定
・必要な他システムとの疎通が取れない
・許されない演算(0での除算や実数演算で解が虚数になる演算など)
・割り当てられていない記憶領域へのアクセス
・不正な値が与えられたポインタで参照する、或いは機械語レベルで不正な値が与えられたインデックスレジスタ等を用いてメモリ参照することとなった場合
・ページフォールト
・プログラミング言語において、何も参照していないハンドルやポインター(Nullポインタ)を参照して操作しようとした場合(例としてJavaにおけるNullPointerExceptionなど)。
シニアエンジニアの方からすると例外処理をきちんと理解して作れという話になると思いますが、多くの人は何のポリシーも持たずにコンパイラに怒られる程度の理由で例外処理(try - catch)を使っているようにも思います。
関数を呼び出す側が、投げつけられたよく分からない例外を責任を持って処理せよ という設計思想が、そもそも近代の大規模開発では難しいのではないでしょうか?
プロジェクト単位でSESガチャで寄せ集められた人間全てが、発生しうる例外を全て理解して適切に処理できるでしょうか?
プロジェクトの例外処理のルールを100ページにも及ぶドキュメントで明文化すれば、それは守られるのでしょうか?
戦闘力が未知数の未経験エンジニアの受け入れもしないといけないかもしれませんし、自称、経験3年や10年のJavaエンジニアであれば全ての例外を把握して適切に処理できるのでしょうか?
わたしは技術的には可能かもしれませんが現実的には不可能と思っています。
おわりに
プロジェクト内のコードに複数の思想が入り乱れると可読性が著しく下がるのは理解していますが、例外処理については、思想に一貫性を保ててるケースは少ないと感じています。
例外処理の崇高な思想に反する記事となりましたが、1プログラマ個人ではなく、プロジェクトに視野を広げて、その思想が現実に即したものなのか、メリットやデメリットはなんなのか?などは考えてもよいのではないかと思いました。
言葉は時代とともに変わります。思想も時代とともに変わるものではないでしょうか?3
おまけ1
営業A短編シリーズ
おまけ2
2030年にIT人材が最大79万人不足するらしいので、学習用の資料をgitで無料公開してます(不定期更新)。
よろしければどうぞ。
-
GO言語にはそもそも try - catch がない。参考:「例外」がないからGo言語はイケてないとかって言ってるヤツが本当にイケてない件 ↩