Railsで、モデルの内容をビューに出すときにちょっと加工するみたいな時、そのコードはどこに書けばいいんだ問題。
デザインパターンの一つにDecoratorパターンというのがあって、これを適用させるのが良いらしい。この用途ではactive_decoratorと並んで人気のgem、draperの説明がとてもわかりやすかった。
draperのREADMEで挙げられているのは、Articleという記事のモデルがあり、これの公開状態を表示させる機能を実装するという例だ。これを読み、なぜヘルパーメソッドやモデルに直に実装するんじゃだめなの?という疑問に対する答えを意訳していく。
ヘルパーメソッドを作る場合
ヘルパーメソッドで書くと、こうなる。
# app/helpers/articles_helper.rb def publication_status(article) if article.published? "Published at #{article.published_at.strftime('%A, %B %e')}" else "Unpublished" end end
しかし、これには以下の問題がある。
But it makes you a little uncomfortable. publication_status lives in a nebulous namespace spread across all controllers and view. Down the road, you might want to display the publication status of a Book. And, of course, your design calls for a slighly different formatting to the date for a Book.
(意訳:けど、これはちょっと気持ち悪い。publication_statusは、全てのコントローラとビューに広がったはっきりしないネームスペースに存在している。将来、(Articleではなく)Bookのpublication statusを表示したくなるかもしれない。そしてこれにはもちろん、違った日付フォーマットが必要になるだろう。)
そりゃあまずいですね。
モデルに書く場合
Without a decorator, you'd have to implement the publication_status method in the Article model. That method is presentation-centric, and thus does not belong in a model.
(意訳:デコレーターを使わないとすると、Articleモデルにpublication_statusメソッドを実装することになるだろう。それはプレゼンテーションセントリックで、そのため一つのモデルには属さない。)
プレゼンテーションセントリックってのはビュー優先の考え方ってことかな。「ビューのためにモデルの責務を逸脱してない?それだめじゃない?」みたいな解釈をした。
Decoratorの役割
そこでお待ちかねのDecoratorの登場。Decoratorっていうのはデザインパターンの1つで、オブジェクトに対して新しい責務を追加してくれるもの。draperを使うと、
@article = Article.find(params[:id]).decorate
という感じでモデルを装飾することができる。
デコレーターというと難しい印象を受けるけど、装飾という意味の英単語なので、オブジェクトが最初から着膨れしないように、外に出るときだけおめかしして出れるようにしてくれるのがデコレーターだと捉えると腑に落ちる感がある。
雑感
こういうのが良いんだとすると、rails g controller
した時にモデル名に対応するヘルパーが生成される意味がよくわからない。PostsHelperというモジュールがあったら、「Postに対して何かするときのメソッド書いていきゃいいんだなー」と思って好き勝手書いてしまう。「あ、いや全体に影響するからDecorator作った方がいいよ」と後から知らされるのはどうなんだろう。
あと、オブジェクト指向理解するのに相変わらず苦労してるんだけど、やたら「責務」という単語が頻出することに気付いた。とにかく「おいそれ誰(どのオブジェクト)がやるべきなんだ」というのを強く意識し続けなければいけないというのがオブジェクト指向のつらみなのかなと、今のところはそんな感じで捉えてる。
追記
そもそもhelperのロードを対応するコントローラだけにする設定もあるとのこと。