マジックナンバー使うな? どんどん使え!
マジックナンバー使うなって言われません? 私も昔はそう思ってたんですよ。
そうでもないなあと最近思い始めたわけでして。
よくある「マジックナンバー使うな!」
消費税の税額を求めるプログラムがあるとします。(軽減税率とかいろいろありますが、面倒なので一律 10% ってことにします)
Console.WriteLine(税抜金額 * 0.10m);
このままだとよくないってお話ですね。
-
0.10m
が何のことか、コードを書いた本人以外にはわからない -
0.10m
がコードのあちこちに散在することになる- タイプミスする人が出てくる
- 消費税率が変わった時の修正量ががが
っていう理由で 0.10m
っていうマジックナンバーじゃなくて、定数化しなさいって言われます。
const decimal 消費税率 = 0.10m;
って定数にしておいて
Console.WriteLine(税抜金額 * 消費税率);
こういう風にしなさいってことですね。
定数化なんて役に立たない
名前を与えてわかりやすくするのはいいことなんです。消費税率をタイプミスする人も減るでしょう。でも、* 消費税率
じゃなくて + 消費税率
とかタイプミスしたらどうでしょうか?
いちゃもんっぽいですけど。(笑)
このケースでは、定数化するよりは「消費税額は税抜き金額の 10% 分」という仕様に合わせて関数を書いたほうがすっきりします。
decimal 消費税額(decimal 税抜金額)
{
return 税抜金額 * 0.10m;
}
このように関数を用意しておいて、利用するわけです。
Console.WriteLine(消費税額(税抜金額));
ここで 0.10m
とマジックナンバーを使いましたけど、消費税額の計算なのは明白ですし、掛け算を利用している以上税率なのもわかります。ですので、定数化する理由はほとんどなくなるんじゃないかと。
仕所得税の所得控除額とかのややこしいのになると、定数化したほうがわかりにくくなります。
const decimal ランク1上限 = 1_625_000;
const decimal ランク1控除額 = 650_000;
const decimal ランク2上限 = 1_800_000;
const decimal ランク2控除率 = 0.40m;
// 以下省略...
if (収入金額 <= ランク1上限)
{
Console.WriteLine(ランク1控除額);
}
else if (収入金額 <= ランク2上限)
{
Console.WriteLine(収入金額 * ランク2控除率);
}
// 以下省略...
これをわかりやすいという人はいないと思います。わかりにくいだけならまだしも、タイプミスを防げません。ランク3上限
のところを ランク2上限
とタイプミスしても気が付かないでしょう。
また、定数化しておくと変更に強くなるといわれますが、このケースではめったに当てはまりません。
ランク1上限が引き下げられた場合は定数の値の修正でも対応できますが、次のケースでは定数の値の修正だけでは対応できません。
- ランクが増減した
- ランク 2 の控除額が「収入金額 * 40%」から「収入金額 * 40% + 5 万円」に
現実には定数の値の修正では足りない変更が入る確率のほうが高いでしょう。
次のように定数を使わずに関数化したほうがよっぽどいいです。
decimal 給与所得控除額(decimal 収入金額)
{
if (収入金額 <= 1_625_000) return 650_000;
if (収入金額 <= 1_800_000) return 収入金額 * 0.40m;
// 以下省略...
}
最後に
ほとんどの処理には名前があります。「消費税額」や「所得控除額」という仕様にある名前をそのまま関数にして、仕様と同じになるように実装を書きます。それをすれば、定数化はほとんど必要ないでしょう。
誤解してほしくないのは、定数化絶対ダメってわけじゃないことです。「マジックナンバーはだめだから定数かしろ」っていうルールだけをうのみにすると逆効果になるよっていうことです。(もしかしたら、このドキュメントに書いたルールもそうなのかもね)