ifとunlessってどう使い分けてますか?
「個人的な好みの問題だろ」とか「unlessはキモい」とか「if notの立場はどうなるのか」とか「規約でうちでは禁止してます」とか色いろあると思います。
その辺は置いておくとして、例えばこんなケースだったらどっちでも良さそうに見えますよね。
def create_foo(name)
return unless name.present? # 名前が入ってなかったらreturn
Foo.create name: name
end
def create_foo(name)
return if name.blank? # 名前が空だったらreturn
Foo.create name: name
end
では次のケースではどうでしょうか?
def create_foo(name)
return unless name.present?
return unless name.length.in? 2..64
Foo.create name: name
end
def create_foo(name)
return if name.blank?
return if name.length < 2 || name.ength > 64
Foo.create name: name
end
fooのcreate条件はどちらが分かりやすいでしょうか?
僕は断然上だと思います。
上の書き方ならこんな風に流れるように読めます。
def create_foo(name)
return unless name.present? # 名前が入っており、
return unless name.length.in? 2..64 # 2文字以上64文字以内なら
Foo.create name: name # Fooを作成する
end
常に正常系の条件を
return if 異常系の条件
正常処理
上のようにガード節にif 異常系の条件
とすると「どういう時にダメなのか?」は分かるけれど、「どんな時に正常なのか?」は条件を反転させる必要があるので分かりづらいです。
「異常じゃなければ正常だろう」で問題が解決するケースは稀です。
return unless 正常系の条件
正常処理
逆に、上のようにガード節にunlessを使うと「どんな時に正常なのか?」が分かりやすくなります。
「どういう時にダメなのか?」は分かりづらくなるように見えますが、「正常じゃなかったらダメ」という事は分かります。
なので「正常じゃないってどんな状態?」まで追う必要がなければこれで十分です。
正常じゃない条件を作るのは難しい
name.present?
が正常系の条件ならname.blank?
が正常じゃない条件、つまり補集合の条件になることを理解するのはそんなに難しく無いと思います。
ですがnum.in? [1, 2, 3]
の補集合の条件はなんでしょうか?
num < 1 || num > 3
ですか?
nilは?
1.5は?
わざわざ難しい条件を見つけ出すよりunless 正常系の条件
を使った方が分かりやすくミスも減るのではないでしょうか。
ブールを返すメソッドは肯定形が良い
リーダブルコード 3.6
それから、名前を否定形にするのは避けたほうがいい。例えば
bool disable_ssl = false;
ではなく、肯定形にしたほうが声に出して読みやすい(それに短くて済む)
bool use_ssl = true;
コードコンプリート 11.2.4
肯定的なブール変数名を使用する
notFound, notdone, notSuccessfulといった否定的な名前は、否定された時の意味がわかりにくい
というわけでifもunlessも渡す条件は正常系を基本として、特にガード節はunlessを使うと読みやすいなーと感じています。