銀行員からのRailsエンジニア

銀行員からのRailsエンジニア

銀行員から転身したサービス作りが大好きなRailsエンジニアのブログです。個人で開発したサービスをいくつか運営しており、今も新しいサービスを開発しています。転職して日々感じていること、個人開発サービス運営のことなどを等身大で書いていきます。

モジュールの抽出【リファクタリング Rubyエディションまとめ2】

こんにちは!

最近 リファクタリング Rubyエディション を読み始めました。本書は、復刻版が最近発売された名著でリファクタリングの様々なパターンをRubyのサンプルコードと共に紹介しています。

紹介されているリファクタリングのパターンの数は膨大なので、いいなと思ったパターンを1つずつブログにまとめています。

今回はその第2弾で「モジュールの抽出」についてまとめます。

第1弾の「委譲の隠蔽」のまとめはこちらです。
委譲の隠蔽【リファクタリング Rubyエディションまとめ1】 - 銀行員からのRailsエンジニア

f:id:ysk_pro:20200502084917p:plain

リファクタリング前のコード

早速サンプルコードを見ていきましょう。
こちらはリファクタリング前のコードです。

ItemクラスとUserクラスで、インスタンスの保存の前にデフォルトの名前を設定しています。

class Item < ApplicationRecord
  #・・・
  before_save :set_default_name

  def set_default_name
    self.name = default_name
  end
  #・・・
end

class User < ApplicationRecord
  #・・・
  before_save :set_default_name

  def set_default_name
    self.name = default_name
  end
  #・・・
end

このコードの問題点は、言うまでもなくコードが重複している点です。

ただ、set_default_name というメソッドは、Itemクラス・Userクラスそれぞれのインスタンスを更新しているため、別のクラスに共通のメソッドを作ってそのクラスのインスタンスに処理を委譲するというやり方ではうまくいきません。

今回のような、通化したいコードがそれぞれのクラスでしか意味を持たないケースで「モジュールの抽出」は有効です。

モジュールの抽出を使ったリファクタリング

モジュールの抽出を使ったリファクタリング後のコードはこのようになります。
Itemクラス、Userクラスでは新しく作った SetDefaultNameモジュールをincludeしているだけでとてもシンプルですよね。

module SetDefaultName
  def self.included(klass) # ①
    klass.class_eval do # ②
      before_save :set_default_name
    end
  end

  def set_default_name
    self.name = default_name
  end
end

class Item < ApplicationRecord
  #・・・
  include SetDefaultName
  #・・・
end

class User < ApplicationRecord
  #・・・
  include SetDefaultName
  #・・・
end

includedメソッド は、モジュール(今回だとSetDefaultName)が include された時に、include をしたクラス(今回だと Item, User)を引数に実行されるメソッドです。

class_evalメソッド は、ブロックに記載したメソッドをレシーバ(このコードだと klass)内に定義してくれるメソッドです。

つまり self.included メソッドでは、SetDefaultName モジュールが include されるタイミングで、include したクラスに before_save :set_default_name を定義しているのです。

「モジュールの抽出」という名前は、単に共通の処理をモジュールにして抽出していることを表しており、シンプルで覚えやすいですね。

おわりに

ここまでお読みいただきありがとうございます。

includedメソッドを使ってモジュールが include されたタイミングでメソッドを定義する方法は、知っておかないとこの方法を使っているコードを読むのが難しいので、覚えておいて損はないと思います。

このブログのサンプルコードは、全く同じではないもののリファクタリング Rubyエディションのサンプルコードを参考にさせていただきました。本書の中では、さらに丁寧に解説されていてとても分かりやすいので、ご興味ある方は是非ご覧ください。

次回はまた違うパターンをまとめていきます!

(追記)
第3弾として「継承から委譲へ」についてまとめました。
是非合わせてご覧ください。
ysk-pro.hatenablog.com