89-関数の「サイズ」を小さくする

プログラマが知るべき97のこと」の88個目のエピソードは、関数の「サイズ」に関する話です。このエピソードで述べられている関数の「サイズ」とは、コードの量という意味ではなく、そのコードが表現している数学関数のサイズという意味です。関数というものは入力と出力の組み合わせと言えます。内部状態を変えないと仮定した場合、その関数は全入力パターンに対し、どれか1つの出力パターンが対応する写像です。したがって、入力出力パターンが多ければ多いほど、その関数は複雑と言えます。
次に示す加算関数をみてみましょう。

int add(int x, int y) { return x + y; }

この関数には整数値の2つの組み合わせが入力で、出力は整数値の何れかになります。ロジック自体はシンプルなのですが、関数のサイズとしては小さくありません。少なくとも全パターンを網羅してテストすることがほぼ不可能なサイズです。
次の関数は文字列の長さを返す関数です。

int len(String text) { return text.length(); }

Javaの文字列は可変長であり、各文字はどんな組み合わせも許すので、ほぼ無限のサイズを文字列は持つ事ができます。したがって、網羅的なテストは出来ないと考えるべきでしょう。
このように関数には「サイズ」があります。関数の「サイズ」を小さくする事で、関数は遙かに扱いやすくなり、テストしやすくなるでしょう。結果として、テストの信頼性も向上します。関数のサイズを小さくするには、入出力パターンをコンパクトにする事です。例えば、整数(int)を入力とする場合でも、マイナス値はあり得ないという事であればマイナス値の入力を禁止する事です。Javaでは、入力値の制約はドキュメンテーションでは可能ですが、実装としては実現できません。できるのは、関数の入り口で入力値の妥当性チェックを行う事でしょう。
また、業務知識に長けているのであれば、本当に必要なパターンは数種しかないことに気付くかもしれません。入力値が幾つかに制限されるのであれば、列挙型(Enum)を使う事で、関数の「サイズ」は小さくなるでしょう。定義された入力値パターンしかないため、テストが簡単に書けるようになります。
このように関数の「サイズ」を小さくしていくことで、関数がよりシンプルになり、テスタビリティが高くなります。しかし、あまりやりすぎると、実装コストがメリットを大きく上回ってしまいます。また、業務系システムなどでは発生しにくいですが、組み込み系のシステムなどではドメインオブジェクトを作りすぎて、無視できない負荷になることも考えられます。
この手の処方箋は、それぞれ有効な手段ですが、適切な運用を心がける必要があります。

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

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