Rails: コードをシンプルにする2種類の委譲(翻訳)

概要 原著者の許諾を得て翻訳・公開いたします。 英語記事: Delegate to simplify your code - Andy Croll 原文公開日: 2018/08/05 著者: Andy Croll 日本語タイトルは内容に即したものにしました。 Rails: コードをシンプルにする2種類の委譲(翻訳) オブジェクト指向プログラミングは、オブジェクト間のメッセージ受け渡しと考えることができます。 関連オブジェクトでは多くの場合、元のオブジェクトのメソッドであるかのように関連オブジェクトのpublicメソッドとしてアクセスできます。 これを実現する主な方法は次の2つです。 Ruby標準ライブラリのForwardableの機能を用いる(ドキュメント) Active Support core extensionのdelegateメソッドを用いる(ドキュメント) 変更前 関連オブジェクトを直接呼び出す新しいメソッドを書く。 # 素のRuby class Workspace attr_reader :user def initialize(user) @user = user end def user_email @user.email end end # Railsの場合 class Workspace < ApplicationRecord belongs_to :user def user_email user.email end end 変更後 Forwardableの機能かdelegateメソッドを用いる。 # 素のRuby class Workspace extend Forwardable attr_reader :user def initialize(user) @user = user end def_delegator :@user, :email, :user_email end # Railsの場合 class Workspace < ApplicationRecord belongs_to :user delegate :email, to: :user, allow_nil: true, prefix: true # allow_nil: trueにするとuserがnilの場合にエラーにならない # prefix: trueにするとメソッド名がuser_emailになる end そうすべき理由 RubyやRailsでメッセージを「パススルー」する場合、いずれかのスタイルで新しいメソッドを作成するのが望ましい方法です。これによりコードで達成しようとしていることが明確に表現できます。「単に関連オブジェクトのメソッドを呼び出す」ことと「新しい機能を実際に実装する」ことの違いを見た目ではっきりと区別できるようになり、便利です。 Railsの場合のコードでは、構文が洗練され、柔軟性も機能も向上するのが普通です。私が特に気に入っているのは、prefixオプションとallow_nilオプションです。Railsをお使いの方は、Railsがdelegation向けに提供している拡張機能をこの方法で利用できます。 そうしない理由があるとすれば これらの機能を使ってworkspace.user.emailがworkspace.user_emailに改善されるぐらいでは大したメリットは感じられません。しかしメソッド名をworkspace.owner_emailなどに変更する場合は、意図が明確になるというメリットもあるでしょう。 RubyのDelegatorライブラリ(ドキュメント)など、委譲の手法は他にもありますが、しかしこれらはいくつかのメソッドを渡すよりも、機能ごとクラス全体をラップする場合に使う方が便利です。 関連記事 Rails: ActiveRecordのスコープで`present?`を使うとパフォーマンスが落ちることがある(翻訳) Rails: pluckでメモリを大幅に節約する(翻訳)