マジックナンバーとは何か、なぜ避けるべきなのか
コードレビューでマジックナンバーを指摘され、マジックナンバーとは何か?なぜ駄目なのか?を調べたのでメモ。
概要
マジックナンバーとは、直接ソースコードに記述された数値で、その意味が不明なものを言う。
コードの可読性を下げ、修正や変更の際に問題が起きやすくなる。
これを避けるために、数値は定数化して、定数名の形で値を参照すべき。
問題のコード
class HomeController < ApplicationController def index @reads = Read.includes([task: :user],[task: :book]).order(read_on: :desc).limit(50) end end
マジックナンバーとは何か
ハードコーディングをした際に、実際に記述された値のことを、マジックナンバーと言います。
- ハードコーディング
ソースコードの中に固定値をそのまま記述してしまうことを、ハードコーディングと言います。
以下のように、~.limit(50)
と数値を直接記述している状態です。
@reads = Read.includes([task: :user],[task: :book]).order(read_on: :desc).limit(50)
つまり上記の 50
がマジックナンバーです。
なぜ避けるべきなのか
なぜ避けるべきなのか、以下のマジックナンバーの例から説明します。
税込み価格の計算
tax_included_price = price * 1.08
1.08
がマジックナンバーです。
1. コードの可読性が下がる
良いコードとは「他人(そして後の自分)が読んで意味のわかるコード」です。
つまり、「誰もがひと目で何の数字かわかること」が必要です。
1.08
ではその数値が何を表すのかが明確ではありません。
1.08
って言ったら消費税じゃん…と思ってしまいますが、
よく考えると、消費税率は時代によって変動するものです。また、日本の消費税率を知らない人がコードを見たらさっぱりわからないですね。
2. 修正や変更時にミスの温床になる
もし税率が変わったら、
コード内の 1.08
の数字をすべて探し出し、ひとつひとつ修正しなければいけません。
探し漏れ、修正漏れが起きることは予想に容易いです。
定数化で解消する
マジックナンバーを解消するには定数化することです。 問題のコードを実際に直してみましょう。
class HomeController < ApplicationController MAX_READS = 50 def index @reads = Read.includes([task: :user],[task: :book]).order(read_on: :desc).limit(MAX_READS) end end
これで、他の人が見ても reads
を取得する最大値だなとすぐにわかりますし、
取得件数を変更するときには MAX_READS
の値を変更するだけで済みますね!
おまけ
定数の命名時の注意点
良いコードを書くためバイブル「リーダブルコード」では、
限界値を含めるときは min
や max
を使うことをおすすめしています。
「未満(限界値を含まない)」なのか「以下(限界値を含む)」なのか明確にするためです。
上記では、limit(50)
=「最大50件まで」と指定しているので、定数に MAX_
を使っています。