Rails tips: Value Objectパターンでリファクタリング(翻訳)

概要

原著者の許諾を得て翻訳・公開いたします。

Rails tips: Value Objectパターンでリファクタリング(翻訳)

Value Objectは、Null Objectパターンと同様Rubyの純粋なオブジェクトです。こうしたオブジェクトは値を表現しますが、ユーザーオブジェクトのようなシステム内で一意のオブジェクトを表現するためのものではありません。Valueオブジェクトは常に値だけを返しますが、Policy Objectのように論理値(true/false)だけを返すのではなく、他の値を返すこともできます(ほとんどの場合文字列です)。このパターンにおけるルールは、「シンプルに保つこと」と、「オブジェクトが生きている間は属性値をしないこと」です。

デモ

今回のリファクタリングパターンの基礎となるアイデアをよりわかりやすく示すために、サンプルのクラスを作ってみましょう。

class Report
  def initialize(emails:)
    @emails = emails
  end

  def data
    emails_data = []

    emails.each do |email|
      emails_data << {
        username: email.match(/([^@]*)/).to_s,
        domain: email.split("@").last
      }
    end

    emails_data
  end

  private
  attr_reader :emails
end

渡されたメールアドレスに対して以下を行います。

  1. メールアドレス値は変更しない
  2. 値を1つだけ返す
  3. 操作はプリミティブなオブジェクト上で行う

Value Objectを作る

上のロジックからValue Objectのクラスを作ると、以下のような感じになります。

class Email
  def initialize(email)
    @email = email
  end

  def username
    email.match(/([^@]*)/).to_s
  end

  def domain
    email.split("@").last
  end

  private
  attr_reader :email
end

Value Objectパターンでリファクタリングする

最終的に得られるのはきわめてシンプルなRubyオブジェクトなので、テストも理解も簡単です。Reportクラスをリファクタリングした結果、とても明快でシンプルなコードになりました。

class Report
  def initialize(emails: emails)
    @emails = emails
  end

  def data
    emails_data = []

    emails.each do |email|
      email_obj = Email.new(Email)

      emails_data << {
        username: email_obj.username,
        domain: email_obj.domain
      }
    end

    emails_data
 end

 private
 attr_reader :emails
end

最後のリファクタリング

Report#dataメソッドがシンプルになってロジックも分離されましたが、まだコードが多少長くなっています。Email#to_hメソッドを作成し、オブジェクトのハッシュ形式を返すようにします。これは私にとって非常に自然な書き方であり、Report#dataをワンライナーに書き換えることができます。

class Email
  def initialize(email)
    @email = email
  end

  def username
    email.match(/([^@]*)/).to_s
  end

  def domain
    email.split("@").last
  end

  def to_h
    { username: username, domain: domain }
  end

  private
  attr_reader :email
end
class Report
  def initialize(emails: emails)
    @emails = emails
  end

  def data
    emails.map { |email| Email.new(email).to_h }
  end

  private
  attr_reader :emails
end

Railsでお困りの方にお知らせ

知りたいことがありましたら、twitter または連絡用フォームにてお気軽にお問い合わせください。

RSpec & TDDの電子書籍を無料でダウンロード

もっと稼ぎたい方や会社をさらに発展させたい方へ: テスティングのスキルの重要性にお気づきでしょうか?テストを正しく書き始めることが、唯一のファーストステップです。無料でダウンロードいただける私の書籍『RSpec & Test Driven Developmentの無料ebook』をどうぞお役立てください。

関連記事

肥大化したActiveRecordモデルをリファクタリングする7つの方法(翻訳)

Rails: テストのリファクタリングでアプリ設計を改良する(翻訳)

Railsアンチパターン: Decoratorの肥大化(翻訳)

デザインも頼めるシステム開発会社をお探しならBPS株式会社までどうぞ 開発エンジニア積極採用中です! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

hachi8833

Twitter: @hachi8833、GitHub: @hachi8833 コボラー、ITコンサル、ローカライズ業界、Rails開発を経てTechRachoの編集・記事作成を担当。 これまでにRuby on Rails チュートリアル第2版の監修および半分程度を翻訳、Railsガイドの初期翻訳ではほぼすべてを翻訳。その後も折に触れて更新翻訳中。 かと思うと、正規表現の粋を尽くした日本語エラーチェックサービス enno.jpを運営。 実は最近Go言語が好きで、Goで書かれたRubyライクなGoby言語のメンテナーでもある。 仕事に関係ないすっとこブログ「あけてくれ」は2000年頃から多少の中断をはさんで継続、現在はnote.muに移転。

hachi8833の書いた記事

開発
Ruby 2.7.0-preview2がリリース

2019年10月23日

夏のTechRachoフェア2019

週刊Railsウォッチ

インフラ

ActiveSupport探訪シリーズ