Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Railsデザインパターン徹底解説:プレゼンターとサービスオブジェクトの実践的な使い方

なぜRailsにはデザインパターンが必要なのでしょうか?

RailsのアーキテクチャであるMVC(Model-View-Controller)は、コードを配置するための基本的な構造を提供してくれます。

しかし、それだけでは十分ではありません。

本来は情報を表示することだけが役割のはずのビューが、ロジックだらけの巨大なファイルへと成長してしまう。

コントローラーにも、本来の責務以外の詳細な処理が詰め込まれていく。

解決策は何でしょうか?

こうした問題を解決するために、デザインパターンの形で2つのソリューションが生まれました。

  • プレゼンターパターン(Presenter Pattern)
  • サービスオブジェクトパターン(Service Object Pattern)

実装方法については人それぞれ意見が分かれるところですが、この記事では実際にうまく機能しているバージョンをご紹介します。

それでは、これらのパターンを見ていきましょう!

Railsでのプレゼンターの使い方

ビューの役割は「プレゼンテーション」、つまりHTML・CSS・ERB(Embedded Ruby)による表示です。

ビュー内にActiveRecordのクエリを書くべきではありません。

さらにほとんどのロジックも排除すべきです。ビューをできる限りクリーンで扱いやすい状態に保ちたいなら。

ここでいう「ロジック」とは、if文や三項演算子を使った条件分岐による判断処理のことです。

では、どうすればいいのでしょうか?

その答えは次の通りです。

ビュー内のロジックに対処する最初のツールは、ヘルパー(helpers)です。

複数のビューで共通して使うグローバルなフォーマットメソッドがある場合、ヘルパーは非常に便利です。

例えば:

Markdownのレンダリング、特定フォーマットでの日付表示、テキストから特定の単語を除去する処理などが挙げられます。

具体的にはこんな感じです:

module DateHelper
  def date_as_month_and_year(date)
    date.strftime("%B %Y")
  end
end

このコードはapp/helpersフォルダ配下のdate_helper.rbファイルとして保存します。

ここでひとつヒント:

ヘルパーメソッドへの入力は必ず引数として渡し、インスタンス変数に依存しないようにしましょう。

この習慣が、後々多くのトラブルからあなたを守ってくれます。

ただし、ヘルパーメソッドには限界があります。特に、ビューのあらゆるフォーマット処理にヘルパーを使い始めると顕著です。

メソッドが際限なく増えていき、組織化された構造を持たないカオスな状態になりがちです。

そこで登場するのが次のソリューションです!

複雑な条件分岐とフォーマット処理をプレゼンターオブジェクトに置き換える

例えば、次のようなビューがあるとします。

<p>
  Post title: <%= post.title.gsub("forbidden word", "") %>

  <%= link_to "Read post", post, class: "w-75 p-3 text-#{post.draft? ? "orange" : "green"} border-#{post.draft? ? "orange" : "green"}" %>
</p>

かなり短いビューですよね?

しかし、三項演算子のコード重複によって、非常に複雑に感じられます。

これは良くありません。

そこで、この問題を解決するプレゼンタークラスを作成しましょう。

やり方は以下の通りです:

class PostPresenter
  def initialize(post)
    @post = post
  end

  def title_without_forbidden_words
    @post.title.gsub("forbidden word", "")
  end

  def css_color
    @post.draft? ? "orange" : "green"
  end
end

このファイルはapp/presenters/post_presenter.rbとして保存します。presentersフォルダがまだなければ作成してください。

これでビューを書き換えられます。

こんな感じになります:

<% presenter = PostPresenter.new(post) %>

<p>
  Post title: <%= presenter.title_without_forbidden_words %>

  <%= link_to "Read post", post, class: "w-75 p-3 text-#{presenter.css_color} border-#{presenter.css_color}" %>
</p>

これで完成です!

  • ビューからすべてのロジックを取り除きました
  • フォーマットや判断処理に、意味のある名前を付けました
  • このクラスは他のビューでも再利用でき、コードの重複を避けられます

これがRailsにおけるプレゼンターの使い方です 🙂

サービスオブジェクトの使い方

コントローラーの役割は、他のコンポーネントに「何をすべきか」を指示することだけです。ツイートの送信方法、顧客への課金処理、PDFファイルの生成方法といった詳細を知っている必要はありません。

こうした操作はサービスオブジェクトに委譲すべきです。

私の定義によれば、サービスオブジェクトとは「あるアクションを完了するためのロジックをカプセル化したRubyモジュール」のことです。

例:

module TwitterService
  def self.send_welcome_message(twitter_handle)
    client.update("@#{twitter_handle} welcome to 'Oranges & Apples', we hope you enjoy our juicy fruit!")
  end

  def self.client
    @client ||= Twitter::REST::Client.new do |config|
      config.consumer_key        = "..."
      config.consumer_secret     = "..."
      config.access_token        = "..."
      config.access_token_secret = "..."
    end
  end
end

慣習としては、app/servicesフォルダ配下にtwitter_service.rbのようなファイル名で保存します。

では、どう使うのでしょうか?

Railsはapp/配下のすべてをオートロードするため、このコードはコントローラーからすぐに利用できます。

使用例:

class UsersController
  def create
    # ...
    TwitterService.send_welcome_message(user.twitter_handle)
  end
end

これがサービスオブジェクトパターンの実際の活用例です。

まとめ

この記事では、賢く使えばプロジェクトのコード品質を向上させてくれる、2つの便利なRailsパターンを学びました。

  • プレゼンター: ビューからロジックとフォーマット処理を追い出し、再利用可能な形に整理する
  • サービスオブジェクト: コントローラーからビジネスロジックを切り離し、単一責任の原則に従わせる

さあ、今度はあなたが自分のプロジェクトに適用してみる番です 🙂

最後までお読みいただき、ありがとうございました。


  1. AWS LambdaでRailsアプリを動かす方法!Lambyを使ったデプロイ手順を徹底解説

    サーバーレスコンピューティングは、サーバーの管理やプロビジョニングの作業をクラウドプロバイダーにオフロードできる仕組みとして、多くのテックチームで急速に標準になりつつあります。その代表格がAWS Lambdaです。LambdaはNode.js、Java、Python、Rubyといった主要なプログラミング言語を幅広くサポートしています。しかし、言語そのものだけでなく、それらの言語で構築されたフレームワークの機能に依存するサーバーレス関数を実行したいケースもあるでしょう。 本記事では、RailsアプリケーションをAWS Lambda上で実行する方法を解説します。サーバーレスコンピューティングとAW

  2. Rails5でのAngularの使用

    あなたは前にその話を聞いたことがあります。分散型で完全に機能するバックエンドAPIと、通常のツールセットで作成されたフロントエンドで実行されているアプリケーションがすでにあります。 次に、Angularに移動します。または、AngularをRailsプロジェクトと統合する方法を探しているだけかもしれません。これは、この方法を好むためです。私たちはあなたを責めません。 このようなアプローチを使用すると、両方の世界を活用して、たとえばRailsとAngularのどちらの機能を使用してフォーマットするかを決定できます。 構築するもの 心配する必要はありません。このチュートリアルは、この目的のた