ロバストネス原則でRubyコードを簡素化する
この記事で紹介するアイデアの多くは、筆者のお気に入りのRuby書籍『Confident Ruby』から学んだものです。この記事が気に入ったら、ぜひ本書を手に取って全文を読んでみてください。素晴らしい内容がぎっしり詰まった一冊です。
あなたの current_user メソッドは User オブジェクトを返します。ただし、ユーザーが存在しない場合は nil を返します。また、search メソッドは結果の Array を返しますが、結果が1件だけの場合は、その単一の結果だけを返します。一見すると合理的に思えますよね?むしろ便利にさえ感じるかもしれません。
しかし、こうした設計判断は、やがてコードを if 文の山の下に埋もれさせてしまいます。あちこちに if kind_of? が散りばめられたり、いたるところで nil チェックを強いられたりします。さらに悪いことに、新機能をリリースするたびに NoMethodError が発生し、ホットフィックスを繰り返す羽目になるかもしれません。
幸い、これを防ぐ方法があります。必要なのは、ほんの少しの配慮だけです。
ロバストネス原則とは
コンピューティングの世界には、次のような有名な原則があります。
自分が行うことについては保守的に、他人から受け入れることについては寛容にあれ。
この原則は、Ruby のメソッドにもそのまま適用できます。あなたが書くメソッドは、妥当な入力を受け入れ、一貫性のある出力を返すべきです。
特に重要なのは後半部分です。誰かがあなたのメソッドを呼び出すとき、そのメソッドが何を返すのかを正確に把握できているべきです。
戻り値について熟慮する
Rails 2.1 の ActiveRecord::Errors#on の実装を見てみましょう。
# File activerecord/lib/active_record/validations.rb, line 212
def on(attribute)
attribute = attribute.to_s
return nil unless @errors.has_key?(attribute)
errors = @errors[attribute].map(&:to_s)
errors.size == 1 ? errors.first : errors
end
このメソッドを呼び出すと、String の Array、単一の String、あるいは nil のいずれかが返される可能性があります。どの型のオブジェクトを扱っているのかを判別するのは、呼び出し側の責任です。これは悪い設計です。
-
呼び出し側は、目障りな型チェックで自らのコードを汚さなければなりません。
-
呼び出し側は、呼び出すメソッドについて多くのことを知っておく必要があります。少なくとも、メソッドが返しうるすべての型と、それぞれの型がいつ返されるのかを把握していなければなりません。
-
テストすべきエッジケースが増えます。コードが期待どおりに動作していると確信したいなら、3つのシナリオすべてを試す必要があります。
メソッドの戻り値は一貫しているべきです。普段 Array を返すなら、常に Array を返すようにしましょう。普段は User を返すが、ときどき nil を返すという場合には、Null User オブジェクトを作成し、nil の代わりにそちらを返すようにできます。
もう少し緩くすることも可能です。「Taggable モジュールを include したオブジェクトを返す」と決めるのもよいでしょう。あるいはもっと汎用的に、「id と name 属性を持つオブジェクトを返す」としても構いません。重要なのは一貫性であり、呼び出し側が何を期待できるのかを明確にすることです。
jQuery は興味深い例です。jQuery のほとんどのメソッドは、同じ種類の Array ライクなオブジェクトを返します。おかげで jQuery のメソッドは驚くほど組み合わせやすく、たった1行のコードで膨大な処理を実現できます。
ちなみに、Rails では後のバージョンでこの問題が修正されました。
# File activemodel/lib/active_model/errors.rb, line 133
def [](attribute)
get(attribute.to_sym) || set(attribute.to_sym, [])
end
現在は常に Array が返されます。Rails 側にとっても、私たち開発者にとっても、シンプルになったのです。
不整合をなくそう
次に「Array または nil」を返そうとしたときは、単に Array を返しましょう。コードベースを見渡して、kind_of? や respond_to? を使っている箇所を探してみてください。そこから呼び出されているメソッドをリファクタリングして、単一の型を返すようにできないか検討してみましょう。
そして、戻り値について確信を持てるようになると、その恩恵はプロジェクト全体へと波及し、周辺のコードがどんどんシンプルになっていくのを目の当たりにすることでしょう。
-
RubyでNクイーン問題を解く方法【バックトラッキングの実装を徹底解説】
N-Queens(Nクイーン)は、N×Nの盤面にN個のクイーンを配置する、非常に興味深いコーディングチャレンジです。 イメージは以下のようになります。 クイーンは全方向に移動できる駒です。 縦方向 横方向 斜め方向 この問題の解答(複数存在する場合もあります)では、すべてのクイーンを盤上に配置しつつ、どのクイーンも他のクイーンの攻撃範囲に入らないようにする必要があります。 この記事では、私がどのような思考プロセスで解答にたどり着いたのかを紹介します。 まずは計画を立てる この種のチャレンジに取り組むときは、まず平易な言葉で計画を書き出すことから始めるのが効果的です。 そうすることで、「何
-
【保存版】「凝集度」と「結合度」でRubyコードはここまで変わる!基本のOOP原則を実例で解説
オブジェクト指向設計において最も重要な原則のひとつが、凝集度(Cohesion)と結合度(Coupling)です。この2つの概念を理解すると、コードベース内のすべてのクラスを特定のレンズを通して評価できるようになり、クラス設計の堅牢さや改善すべき箇所が明確に見えてきます。 凝集度と結合度とは何か? 凝集度とは、クラス内にあるメソッド同士の関係性を表す概念です。各メソッドが同じインスタンス変数や引数を使い、共通の目的に向かって協力し合っているか?それとも、メソッドごとに役割がバラバラで、互いに無関係に感じられるでしょうか? 結合度とは、あるクラスが他のクラスにどれほど依存しているか、システムの他