【備忘録】2者間の条件分岐だからといって安易にif文を使用しない
はじめに
2者間の条件分岐を記述する際にはif~elseを使用することが多いと思います。
しかしながら、if~elseを使用することで可読性や拡張性が低下するケースがあります。
本記事ではif~elseを使用することで発生する問題点を紹介します。
なお、今回記述するコードはRuby on Railsのケースで記述しています。
例えば、山田さんと中島さんと伊藤さんの3人の年齢を比較してその中で一番年齢が上の人を取得する場合はmax_byメソッドを使用して次のように書けます。
yamada = User.create!(name: "山田", age: 20)
nakajima = User.create!(name: "中島", age: 40)
ito = User.create!(name: "伊藤", age: 30)
[yamada, nakajima, ito].max_by{|user| user.age} # nakajimaが返却される
max_byメソッドについては次のリンクを参照してください。
それでは山田さんと中島さんの2人の年齢を比較して、年齢が取得する人の名前を取得する場合はどうでしょうか?
上の誘導があれば次のように書けます。
max_age_user = [yamada, nakajima].max_by{|user| user.age} # nakajimaが返却される
max_age_user.name # 中島が返却される
ところが、実際には次のように書かれるケースがあります。
if yamada.age > nakajima.age
max_age_user = yamada
else
max_age_user = nakajima
end
max_age_user.name # 中島が返却される
おそらく2者間の条件分岐であれば、if~elseを使用することが多いと思います。
しかしながら個人的には可能な限りif~elseを使用したくないと思っています。
if~elseを使用する問題点
理由①:可読性が低い
条件分岐を使用すると行数が増えるため、
コードの読み手に読解を強いる箇所が増えることになり、認知負荷が高くなります。
理由②:拡張性が悪い
条件分岐を使用すると、条件が増えるたびにコードを修正する必要があります。
if~elseを使用しているとelsifを追加する対応などをした結果、
コードが複雑になるケースがあります。
2つの理由について実際に遭遇したケースを見ていきます。
条件が追加された場合
別の開発者が条件追加に伴うコード修正をした場合に、
純粋に条件分岐のみを修正する可能性があります。
if~elseを使用した次のコードを考えます。
if hoge.content.present? && fuga.content.present?
"ホゲ・フガ"
elsif hoge.content.present? && !fuga.content.present?
"ホゲ"
elsif !hoge.content.present? && fuga.content.present?
"フガ"
end
hoge,fugaの2つのオブジェクトがある場合に、
それぞれのオブジェクトのcontentが存在する場合には該当する文字列を返却するという処理を行っています。
この時点ですでに可読性が低いですが、扱うオブジェクトが増えた場合にこのように書かれるケースがあります。
if hoge.content.present? && fuga.content.present? && piyo.content.present?
"ホゲ・フガ・ピヨ"
elsif hoge.content.present? && fuga.content.present?
"ホゲ・フガ"
elsif hoge.content.present? && piyo.content.present?
"ホゲ・ピヨ"
elsif fuga.content.present? && piyo.content.present?
"フガ・ピヨ"
elsif hoge.content.present?
"ホゲ"
elsif fuga.content.present?
"フガ"
elsif piyo.content.present?
"ピヨ"
end
このようにオブジェクトが増えると、条件分岐の数が増えていきます。
今回の場合は次のような対応をはじめから考えておくべきでした。
tmp = []
tmp << "ホゲ" if hoge.content.present?
tmp << "フガ" if fuga.content.present?
tmp.join("・")
ネストした場合
次のように条件分岐がネストするケースも考えられます。
hoge = Hoge.find_by(id: params[:hoge_id])
fuga = Fuga.find_by(id: params[:fuga_id])
if hoge.present? && fuga.present?
if hoge.created_at > fuga.created_at
name = hoge.name
date = hoge.created_at.strftime("%Y年%m月%d日")
else
name = fuga.name
date = fuga.created_at.strftime("%Y年%m月%d日")
end
elsif hoge.present?
name = hoge.name
date = hoge.created_at.strftime("%Y年%m月%d日")
elsif fuga.present?
name = fuga.name
date = fuga.created_at.strftime("%Y年%m月%d日")
end
このように書けば条件分岐をネストせずに書けます。
hoge = Hoge.find_by(id: params[:hoge_id])
fuga = Fuga.find_by(id: params[:fuga_id])
content = [hoge, fuga].compact.max_by(&:created_at)
return unless content
name = content.name
date = content.created_at.strftime("%Y年%m月%d日")
まとめ
2者間の条件を比較する場合はif~elseを使用することが多いと思います。
しかしながら可読性や拡張性を踏まえると配列などを活用して
早い段階で条件分岐を排除することが望ましいと考えています。
配列やオブジェクトにすると、条件がわかりにくくなるケースもありますので、
一概にこのような修正が良いとは言えませんが、
安易に使用するのではなく、
条件分岐を排除する方法を考えることをおすすめします。
Discussion