日々常々

ふつうのプログラマがあたりまえにしたいこと。

「淡路島の電車の運行状況を聞いた話」をシステム開発に置き換えてみる

4/13のAM5:33にM6.0らしい地震がありました。各地で大きな被害が無いことを祈りつつ。

フジテレビのアナウンサーさんが淡路島の電車の状況を聞いたと言う話

だいたい見てると「電車無いのを知らずに聞いてしまった」のを叩く向きに思えます。実際のところ、聞いたこと自体はNGなのでしょうが、これをシステム開発の話に置き換えると見えるものがある気がしました。

以降はJavaの語彙で書きますが、これって NullPointerException っぽいなと。

コードっぽい何かで書いてみる

こんな感じ。

運行状況 = 淡路島.get路線().get運行状況();

get路線() が何を返すのか知らないけど、これが null だったりするとぬるぽです。今回のテレビでされたとされる受け答えを素直に書くとこんな感じ?

try {
  運行状況 = 淡路島.get路線().get運行状況();
  // 電車の運行状況に応じた何かしらの処理
} catch (Exception e) {
  // よくわからない状況なので柔軟に対応する
  // ここで「電車ありません」な返答をしたり
}

別解としては null チェック。

路線 = 淡路島.get路線();
if (路線 != null) {
  運行状況 = 路線.get運行状況();
  // 電車の運行状況に応じた何かしらの処理
}

EffectiveJavaに「例外的状況以外で例外使うんじゃねー」な話がある通り、上記2つでの選択ならば後者(nullチェック)ですが、null自体嫌いなのでどっちも無しだと言う個人的な意見はさておき……とりあえず路線が取得出来ない以外の例外もあると思うので前者で進めますね。


さて、今回の話では「淡路島に電車が無いのを知らずに聞いた」が問題っぽく見え、それを回避しなきゃいけなくなります。つまり「電車が無いのは知ってろよ」と。アレな開発をしているといとも安易に「特殊状況下での条件分岐」を追加します。そのまま書いちゃうとこうですか。

void 電車状況確認(場所) {
  if (場所 == 淡路島) {
    // 淡路島での特別な処理
  } else {
    try {
      運行状況 = 場所.get路線().get運行状況();
      // 電車の運行状況に応じた何かしらの処理
    } catch (Exception e) {
      // よくわからない状況なので柔軟に対応する
    }
  }
}

こんなことすると「いや待て、徳島にも電車は無いぞ」とかなるわけですね。

void 電車状況確認(場所) {
  if (場所 == 淡路島) {
    // 淡路島での特別な処理
  } else if (場所 == 徳島) {
    // 徳島での特別な処理
  } else {
    try {
      運行状況 = 場所.get路線().get運行状況();
      // 電車の運行状況に応じた何かしらの処理
    } catch (Exception e) {
      // よくわからない状況なので柔軟に対応する
    }
  }
}

ようし、嫌になってきた。でも実際に見られがちなコードでもあります。

テレビでの対応はもう「知っとけよ」となるのかもしれませんが、システム的にこれをやるのはよろしく無いと言うかダメです。少なくとも上に書いたような電車状況確認メソッド*1に書くべきではありません。
ならば電車状況確認メソッドを呼び出す際に条件分岐すれば良い?

void 災害状況確認(場所) {
  if (場所 != 淡路島 and 場所 != 徳島) {
    電車状況確認(場所)
  }
}

これもよろしくない。……と言うか「条件分岐を行う」なんてやってる時点で、いつまでたっても抜け出せません。

で、どうするか

元のシンプルなのに戻りましょう。

運行状況 = 淡路島.get路線().get運行状況();

やりたいことはこれで、これに対してコーナーケースがあった場合、それを押し込めるべき場所に押し込め、ここでは何喰わぬままこのまま行けるようにしておくのが良いと思うのです。メソッドはシンプルに保ち、明解に。
そのためには 淡路島 の get路線() に null ではなく 路線のNullObject を返させる。路線のNullObject.get運行状況() では「電車が無い」の運行状況を返し、その運行状況に応じた処理のところに電車が無い場合の振る舞いを追加する……とやれば、もし現状の実装なんてものがあったとしても、「場所を見て分岐する特殊ロジック」とやらを電車状況確認メソッドに追加するよりもマシな対応は出来そうな気はします。
もっとも get路線().get運行状況() のような列車事故はデルメルの法則がとか、Groovy で ?. 演算子使えば良いじゃんとか、運行状況に応じて振る舞いが変わるなら運行状況に振る舞いもたせるんじゃとか、路線って一つでいいのかとか、地域の粒度がとか、まぁこの辺は「何かしらの処理」の内容が見えないままだと明後日の方向を向くので何とも言えないのですが。

まとめ

アレなシステム開発っぽいのは「NullPointerExceptionが起こったら呼び出し元が悪い。ちゃんとnullチェックしろ。」と言ってるように見えた点で、処理するところに「それを知っておけ」と問題が起こってから言うようなところ。nullチェックなんぞのノイズは、入れなくて済むなら入れない方がいいと思ってる。対称性の問題もあるけど、単純に焦点がボケるから。
実際の話と開発とは問題解決の方向が違います。でも無理矢理持ち込んでくる人って居るんですよ。「ちょっと条件分岐追加するだけだろ?」って言うの。それで経験の浅い人とか押しに弱い人とかはそれに従って書いてしまう。さらに既存がそんな感じで書かれていると後押しされてしまう。ここに書いた程度でも嫌になってるのに、それがどんどこと積み重なっていって、あっという間に保守不可能なコードに成長していきます。
まとめを見て「ぬるぽっぽいな」と過ったので書いてみたら、結局「ifとnullが嫌い」っていつも言ってる事を書いただけだった。

*1:これもどうよと思うのだが……