【Rails】Decoratorについての忘備録
記事を書くことになった背景
業務で知った内容です。
1月に開発未経験でのWebエンジニアに転職しているのですが、
まずは既存のアプリケーションの改修や小さい新機能を追加することが多くあります。
リソースの状態(大体5パターン)に応じて、Viewに表示させる項目を切り替える必要がありました。
そこでの先輩との会話
私「モデルにリソースの状態に応じて判断メソッドを書けば終わりっすね!」
先輩「Viewでしか使わないからDecorator
でもいいかもなー」
私「分からん!!」
と言う状況だったのですが、調べてみたらシンプルに感激したので記事に残します。
記事で書く内容
Decoratorパターンについての説明
Railsに置けるDecoratorについて
Decoratorパターンを簡単に実装するgem
Draper
の説明
Decoratorパターンとは
Javaで学ぶデザインパターン
には下記のように例えられています。
ある元となるクラスに対して機能を拡張していく際に利用できるデザインパターンになると思われる。
まず中心となるスポンジケーキのようなオブジェクトがある それに飾り付けとなる機能を一皮一皮被せていって、より目的にあったオブジェクトに仕上げていくのです。
正直、頭の中でお花畑が見えましたが、使うと何が嬉しいかについては、下記のように理解しました。
- 元となるクラスを継承して、飾り枠(
ConcreteDecorator
)に機能を拡張していく- 元となるクラスに対して変更が発生しない
- 飾り枠をたくさん用意して組み合わせること多様なパターンの機能拡張ができる
- 表示などをカスタマイズする際には最適なパターンになるかも
- 元のクラスに対してコードを追加していく訳ではないので、1つのクラスが肥大化していくことを防ぐことができる
ただ、ConcreteDecorator
自体が肥大化したり、似たようなクラスが
増えてくると言うアンチパターンと隣合わせと言うことは認識しておかなければなりません。
実際にコードを書いてみた系は、別の記事を参照してください。。
クラス図
一般的なDecoratorパターンのクラス図は下記のようになります。
Component
- 機能を追加するときになる元となる抽象クラス
ConcreteComponent
- Componentのインターフェイス(図で言うところの
method~
)を実装している
- Componentのインターフェイス(図で言うところの
Decorator
- 装飾の元となる抽象クラス
- Componentをインスタンス変数としてもち、飾り付けの対象を知っている
ConcreteDecorator
- 実際に装飾の仕方を定義しているクラス
- これを追加していくことで、多様な拡張が可能になる
RailsにおけるDecoratorについて
大まかな特徴としては、
- Viewでしか使わないModelのメソッドを
decorator
を使うことで切り離すことができる decorator
内ではhelperを利用することができるlink_to
といったViewで使うメソッドをdecorator
で定義することができる
decorator
を使うと何が嬉しいか言うと、
ビジネスロジックとプレゼンテーション(表示)に関わる関心を分離することができる
上記を実装することで、1つのモデルが肥大化することを防ぐことができる
ことではないかと思います。
ただ、上のDecoratorパターンのところにも書きましたが、
Decoratorが肥大化する懸念があることについては認識しておく必要があります。
複数のモデルで使われる共通のロジックについてはhelper
に記載した方がいいでしょう。
Draperについて
Draperって?
Draperは、decorator(プレゼンテーション)の実装を簡単にしてくれるgemになります。
ど素人目線で行ってしまうと、ある特定のモデルかつViewでしか使わないメソッドはdecoratorに書いた方がいいよっていう感じです。
特徴としては、
- ViewとModelの中間に位置するVM(View Model)
- Vue.jsとかで言われるVMMVとは異なる
- プレゼンテーションのロジックやフォーマットの処理を実装することができるようになる
使いかた
ライブラリのインストール
おなじみのGemfile
に下記を記述して、bundle install
してください。
# Gemfile gem 'draper' # Gemのインストール bundle install
Decorator作成
bundle exec rails g decorator User Running via Spring preloader in process 284 create app/decorators/user_decorator.rb invoke rspec create spec/decorators/user_decorator_spec.rb
こんな感じでViewで利用するhelperを書くことができます。
class UserDecorator < Draper::Decorator # 全ての属性にアクセスできるようにする delegate_all def welcome_message if user_signed_in? && !current_user.confirmed? return "ゲストユーザ(登録が完了していません)" else return "ようこそ、#{name}" end end end
コントローラではdecorator
メソッドで、上で定義したメソッドを使えるようにしてあげます。
def index @user = User.find(id: ~~).decorate end
そしてViewからは、以下のようにアクセスすればOKです。
View内で、If分岐
させなくて済むのがポイントです。
h1
= @user.welcome_message
まとめ
いつも通りの雑な記事ではありますが、学んだことは下記。
- Railsでは
Decorator
を使うとことで、Viewでしか使わないロジックを分離できる- ModelやViewのコードが肥大化せずにすむ
Decorator
を使う際にはgemのdraper
を使うのがおすすめ- Controllerでは抽出したモデルに対して
decorate
メソッドを使ってあげる