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

### 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して機能追加する

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

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

```ruby
 class VanillaIcecream
  # CashewNutsToppingIcecreamというモジュールを取り込みたい
  include CashewNutsToppingIcecream
 end
```

##### パターン3:DraperやActiveDecoratorのgemを利用する

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

```ruby
# Gemfileに記述
gem 'draper'
```
①gemを導入し、`bundle install(またはbundle)`を実行します。
②次に、

```ruby
# 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を調べている際、「ヘルパーと何が違うのか？」混乱してきたので調べたところ、[こちら](https://qiita.com/jonson29/items/00077b54bb91ed74fdb8)に違いがありました。`特定のモデルに関連している`ビューの描写に関するものはdecoratorということですね。

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

### 6.参考
1.大場寧子他, 現場で使えるRuby on Rails5速修実践ガイド, マイナビ出版, 2018.
2.[Rubyのオブジェクトとは](https://techacademy.jp/magazine/18767)
3.[draper](https://github.com/drapergem/draper)
4.[ActiveDecorator](https://github.com/amatsuda/active_decorator)
5.[decoratorを導入して、viewの記述をすっきりさせ、modelの肥大化を回避する【Day 3/30 2nd】](https://note.com/kentarotawara/n/n0c1ff2060cf7)
6.[Rails Viewの表示のためにDecoratorを用意してHelperとModelを助ける](https://qiita.com/shizuma/items/ce9e863e9c94e88e0a57)
7.[12. Decorator パターン](https://www.techscore.com/tech/DesignPattern/Decorator.html/)
8.[instance method Object#extend](https://docs.ruby-lang.org/ja/latest/method/Object/i/extend.html)
9.[Decoratorの役割とDraperについて](https://qiita.com/jonson29/items/00077b54bb91ed74fdb8)
10.[【入門】Draperを使ってみる](https://tech.mof-mof.co.jp/blog/draper/)

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