LoginSignup
4
0

More than 1 year has passed since last update.

【Rails】 Decoratorパターンとは(コードは動けば良いわけではない)

Last updated at Posted at 2021-06-12

1.はじめに

共同開発中にメンターさんから、「Decoratorについて調べてみて」とアドバイスを頂きました。
実装したい内容が実現できて安心してしまっていたので、「コードは動けば良いわけではないんだな...」と実感しました。
今回はDecoratorについて調べてみました。

2.環境

  • mac.os バージョン10.15.6
  • Ruby 2.7.2
  • Rails 6.1.3.1
  • psql (PostgreSQL) 12.6

3.MVCについて

冒頭でも少し触れましたが、メンターさんにDecoratorについてご紹介頂いたのは、「ビューのロジックに関することを、コントローラで定義してコードを書いていたから」だと思いました。

app/controllers/movies_controller.rb
def index
 #動画の一覧を昇順(1,2,3...)にする
 @movies = Movie.order(created_at: :asc)

 #動画の番号を表示する(ために必要な変数を定義) ←ビューのロジックに関すること
 @base_number = ...
end

どこにコードを書くのか曖昧だと感じたので、まずMVCについて復習も兼ねて書いていきます。

3.1 モデル(M)

データとデータに関わるビジネスロジック(アプリケーション特有の処理)をオブジェクトとして実装したもの。データベースの保存や読み込みを行う。
※オブジェクト:関連する変数(値)とメソッド(動作)をまとめて、そのまとまりに名前を付けたもの。

3.2 ビュー(V)

ブラウザに表示する画面、すなわちHTMLなどのHTTPレスポンスの中身を実際に組み立てる部分。必要に応じてコントローラからモデルなどのオブジェクトなどを受け取り、画面表示に利用する。

3.3 コントローラ(C)

ユーザーが操作するブラウザなどのクライアントからの入力(リクエスト)を受け、適切な出力(レスポンス)を作成するための制御を行う。MとVのコントロールを行う。

4.Decoratorとは

原則として、モデルにはビジネスロジック、ビューではプレゼンテーション(見た目の)ロジックを記述するとした時に、「モデル固有のプレゼンテーションロジックはどこに書けばいいのか?」という問題が発生します。

ビューに書いた場合:モデル固有の処理なのに離れて分散してしまう。重複が発生しやすくなる。
モデルに書いた場合:コードの再利用性やメンテナンス性を損なう恐れがある。モデルが太る。

そこでDecoratorパターンを使うと、モデルごとや利用シーンごとにプレゼンテーションレイヤーの実装をまとめることができます。

アプローチの例

パターン1:モデルオブジェクトをインスタンス変数として保持し、ビュー用の処理を閉じ込めたクラスを作成して利用する

:icecream:例:「アイスクリームのトッピングの処理のクラスを作り、トッピングクラスのインスタンス変数にアイスクリームのインスタンス変数を持たせる」ことでトッピングがのったアイスクリームを表現する。

パターン2:ビュー用の処理をモジュールとして定義し、ビューの文脈でモデルオブジェクトをextendして機能追加する

:pencil:モジュール:一連の振る舞いの設計図を一箇所にまとめたもの。
モジュールはオブジェクトを生成できず、includeメソッドを使ってクラスに取り込んで使用します。

includeはクラス(のインスタンス)に機能を追加しますが、extendはある特定のオブジェクトだけにモジュールの機能を追加したいときに使用します。

 class VanillaIcecream
  # CashewNutsToppingIcecreamというモジュールを取り込みたい
  include CashewNutsToppingIcecream
 end
パターン3:DraperやActiveDecoratorのgemを利用する

gemを導入し、decoratorファイルに記述するという方法です。

# Gemfileに記述
gem 'draper'

①gemを導入し、bundle install(またはbundle)を実行します。
②次に、

# generateはgでも可
# ターミナルで実行する
rails generate draper:install
rails generate decorator movie(モデル名)

を順に実行します。

③decorator.rbにコードを記入します。

movie(モデル名)_decorator.rb
class movieDecorator < Draper::Decorator
 delegate_all # movie(モデル)のメソッドを全て呼び出せる

 def base_number(メソッド名)
   # モデルのメソッドを使用することもできる

 end
end

④decoratorを用いたコードに書き換えます。
(1)コントローラ

app/controllers/movies_controller.rb
def index
 #動画の一覧を昇順(1,2,3...)にする
 @movies = Movie.order(created_at: :asc)

 #動画の番号を表示する(ために必要な変数を定義) ←ビューのロジックに関すること
 @base_number = ...
end

書き換えた後(decoratorインスタンスを作成)

app/controllers/movies_controller.rb
def index
# 複数のオブジェクトの場合はdecorate_collectionを使用する
 @movies = MovieDecorator.decorate_collection(Movie.order(created_at: :asc))
end

(2)ビュー

app/views/movies/index.html.erb
<% @movies.each.with_index(1) do |movie, i| %>
  <p class="movie-title">
     No.<%= @base_number + i %>:<%= movie.title %>
  </p>
<% end %>

書き換えた後(decoratorメソッドを呼び出す)

app/views/movies/index.html.erb
<% @movies.each.with_index(1) do |movie, i| %>
  <p class="movie-title">
     No.<%= movie.base_number + i %>:<%= movie.title %>
  </p>
<% end %>
おまけ

draperやactive_decotratorを調べている際、「ヘルパーと何が違うのか?」混乱してきたので調べたところ、こちらに違いがありました。特定のモデルに関連しているビューの描写に関するものはdecoratorということですね。

5.まとめ

「モデル固有のプレゼンテーションロジックはどこに書けばいいのか?」という問題が発生した時に、「Decoratorパターンを使い、そこに記述しましょう」ということですね。
モデル→decorator→ ビュー
とすることでモデルに関連したビューの表示をすることができました。

6.参考

1.大場寧子他, 現場で使えるRuby on Rails5速修実践ガイド, マイナビ出版, 2018.
2.Rubyのオブジェクトとは
3.draper
4.ActiveDecorator
5.decoratorを導入して、viewの記述をすっきりさせ、modelの肥大化を回避する【Day 3/30 2nd】
6.Rails Viewの表示のためにDecoratorを用意してHelperとModelを助ける
7.12. Decorator パターン
8.instance method Object#extend
9.Decoratorの役割とDraperについて
10.【入門】Draperを使ってみる

7.最後に

記事の感想や意見、ご指摘等あれば伝えていただけるとありがたいです。
読んでいただき、ありがとうございました。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0