2012年10月1日月曜日

model の decorator の話

最近の sapporo.rb などでは ActiveDecorator など、model の Decorator の話で少し盛り上がっているようだ。view に関わるコードをどう整理するか?という話について、共通見解が定まってきたということだろう。

rails の MVC に沿ってコードを書くと、view 回りがだんだんごちゃっとしてくる。scaffold のように単純に model の property を表示するような view なら良いが、STI のタイプに応じて表示内容を変えるとか、新規登録の場合と更新の場合で表示を変える、ユーザの権限に応じてどこまで情報を見せるか制御するなど、model のデータやセッションのデータに基づく条件分岐と html の描画が絡み合うような場合にこれをどこに書くのか?というのが問題になる。

伝統的な rails のレイヤーわけだと候補としては view, model, helper のいずれかになるだろう。この中のどれが適切か?

view にロジックを書くな、ということは昔から言われている。view のようなUIに最も近いところにモデルのロジックが入り込んでしまうと仕様変更に弱くなる。またデザイナーと分業しているときにメンテがしづらくなる。だから view にはできるだけコードは書かない。

では model に書くのか?これもあまりうまくない。まずmodelには html を描画するための十分な能力が無い。helper を include してしまうとよくわからないメソッドが大量に流入してくる。また、view に関する雑多なメソッドが model に入り込むとすぐに fat な model が出来上がる。あるプロパティでも画面によって微妙に表示の仕方が異なる、ということはよく起きるが(たとえばメールと画面では微妙に違う)、そういう些細な違いのメソッドが沢山modelに生えるというのは好ましくない。

では helper に書くのか?helper に書くのは良いアイデアのように見える。view に直接ロジックを書かないようにするための仕組みがhelperだ。ここしかない。実際小規模のアプリでは十分うまく働く。しかし、規模が大きくなるにつれて問題が出てくる。原因はhelperがフラットなためだ。このフラットな空間に様々な view 用の メソッドが生えてくると結局管理できなくなる。ある helper メソッドがどの view で使われているのか、いちいち grep するか?また helper のデフォルトの分け方が controller 単位なのも管理がしづらい原因でもある。view のメソッドは view 単位か model 単位で分けられるのが自然だと思う。

そこで出てきたのがこれらの Decorator だ。上であげた3つのレイヤーいずれとも異なる新しいレイヤーだ。Decorator は model をラップすることで表示専用のメソッドを追加することができる。ラップしているだけなので元のモデルの挙動は確保される。また、必要なときに必要なロジックが追加できる。ラッパーから helper へのアクセスを用意しておけばhtmlの描画もそれほど苦ではない。

この Decorator をどういう単位で作成するか、というところについては検討の余地がある。選択肢としては、

  • 一つの View につき一つの Decorator
  • 一つの model につき一つの Decorator
  • View x Model の組み合わせにつき一つの Decorator
最初のものはいわゆる Presenter パターン。ActiveDecoratorは二つ目のパターン。最後のものは Exhibit パターンという名前がある。ここの理解は少し怪しい。最後の一番細かい粒度で設定できるのが使い勝手としては一番良いと思う。view か model のどちらか選べと言われれば view にすると思う。ActiveDecorator のような、model 単位で Decorator 一つ、というのは結局 helper と大差なくなる。