62-プリミティブ型よりドメイン固有の型を

プログラマが知るべき97のこと」の62個目のエピソードは、ドメイン型に関する話です。このエピソードでは1999年の火星探査機「マーズ・クライメイト・オービター」の事故を例にあげ、プリミティブ型と暗黙で利用している単位に関する問題を提起しています。この事故の原因は宇宙船のプログラムにありました。推力を計算する演算処理が宇宙船と地上の両方のプログラムに含まれていたようです。どちらのプログラムの演算にも不具合は恐らくなかったのでしょう。しかし、それぞれのプログラムを書いたプログラマの文化圏が異なり、自然に使う計算の単位が異なったため、数百億円が燃え尽きたのです。
Javaで次のようなコードがあったとします。

float speed = 10f;

この単純なコードは「浮動小数点数speedを定義して値を10」と定義しているコードです。このコード自体の意味はどんな国の人がコードを読もうと不変です。Javaプログラミング言語に採用しているという事さえ共通に認識していれば、万国共通です。しかし、speedの単位という情報が欠落しているのです。この情報はプログラムの仕様書に記述されるべき情報であるかもしれませんが、そこに強制力はありません。
例えば、次のようにコメントを残したとしても、うっかり見落としてしまう可能性はまだ残ります。

// speed in kots
float speed = 10f;

一方、次のように型を定義した上で、speedを定義したとします。

class SpeedInKots {
  final float value;
  SpeedInKots(float value) {
    this.value = value;
  }
}
SpeedInKots speed = new SpeedInKots(10f);

こう記述することで、speedが『ノット』という単位であることが明確になります。ソースコードで最も重要な要素である「読みやすさ」が格段に向上し、他のプログラマが読む時に誤解を受ける事が少なくなるのです。
また、このようなドメイン固有の型を利用する事で、単なるfloatという数値に意味を持たせることができます。テストという観点で考えれば、速度にどんな値が入るかを考慮する必要がありますが、マイナス値はこの場合に妥当でしょうか?どんな乗り物のノットを考慮するでしょうか?一般的な船舶を対象としたアプリケーションを開発しているのであれば、とりえる値は0〜100までで十分でしょう*1。すると次のように制限することができます。

class SpeedInKots {
  final float value;
  SpeedInKots(float value) {
    if (value < 0f || 100f < value) throw new IllegalArgumentException("Out of range.");
    this.value = value;
  }
}

ソフトウェアで株式の注文数が異常な値の時に注文を確定させた事についてソフトウェアの不具合かどうかが問題となった事件もありました。単なるプリミティブ型でなく適切な範囲と単位を持ったドメイン型を用いることでより高品質なソフトウェアとなるのです。
コンピュータの演算能力が十分な現代であれば、ドメイン型を使う事によるパフォーマンス上のデメリットはほとんどないでしょう。全ての状況でドメイン型を使うべきかどうかは実装コストと品質(安全性)のトレードオフとなるかと思います。プログラマとしては、プリミティブ型ではなくドメイン固有の型を利用するという選択肢を自然とできるようにはなっておくべきです。間違っても注文数にマイナスを入力すると在庫が増えるようなソフトウェアを出荷してはなりません。

プログラマが知るべき97のこと

プログラマが知るべき97のこと

*1:一般的な船舶の速度は22ノット程度だそうです