えせMVC?

[Ruby on Railsの「えせMVC」の弊害] というエントリが話題になっているので釣られてみます。
http://satoshi.blogs.com/life/2009/10/rails_mvc.html

MVC

まず結論から。Railsが本当にMVCかどうかですが、僕は真っ当なMVCだと思います。

MVCが指すもの

例のエントリの次のエントリ[O/Rマッピング技術の進化が皮肉にも助長している「えせMVC症候群」]で、

MVCのコンセプトは「はやりすたり」とは関係のない良いアプリケーションを設計する上での基本中の基本。Ruby on RailsJ2EEを使ったウェブアプリケーションにも、Cocoa/UIKitの上のiPhoneアプリケーションにも通じる話だということを覚えておいてほしい。

確かにMVCは重要だけど、「「はやりすたり」とは関係のない」というのは言い過ぎだと思います。というのはGoFデザインパターンとかが流行った頃のMVCと、現在のWebアプリ界隈でのMVCとは結構違っています。Strutsより前のMVCは主にデスクトップのGUIを実現するための仕組みで、Modelの変更をViewに伝えるためにObserverパターンを使うのが一般的だった気がします。

StrutsなどのWebアプリのためのフレームワールが流行ってMVCという言葉も、Observerパターンを使うMVCから、使わないパターンを主に指すようになりました。かつての過渡期には(今も?)MVC2とか呼ばれていましたね。
http://www.ibm.com/developerworks/jp/java/library/j-struts/index.html

かつてのObserverパターンを使うMVCJavaScriptFlashなどでGUIの部品を作る場合には重要だと思うんですけどね。あ、Flashではbindingがありましたっけ。iPhoneアプリはよく分からないけど、たぶん役に立つのはObserverパターンを使う方のMVCなんじゃないかな。

そんなに強制したい/させたいの?

いきなり脱線しましたが、元のエントリは「モデルにビジネスロジックを書くことを強要しないものはMVCじゃない。だからRailsMVCじゃない」と言っているように僕は受け取ったのですが、どんなフレームワークでもMVCっぽくない書き方はいくらでもできるはず(例えばビューでSQLをいきなり発行するとか)なので、Railsが「えせMVC」なら他のフレームワークもほとんど「えせMVC」なんじゃないかなーと思います。

また、ビジネスロジックをモデルに書かせることをプログラマに強制するツールを使いたいならRailsは向いてないと思いました。いろんな書き方ができるっていうのがRubyの良いところの一つなわけだし。

僕もプロジェクトのメンバーに「ビジネスロジックはモデルに書け!」と口うるさく言ってますが、かと言って「モデルにしかビジネスロジックを書けないRailsっぽいやつ」が出てきたら・・・どうやってビジネスロジックか否かを判断するのかにもよりますが、あんまり使いたくないですね、なんかプラグインとか作り難くなりそうだし。

Skinny Controller, Fat Model

http://d.hatena.ne.jp/higayasuo/20091013/1255408723Seasarのひがさんが、Fat Modelについて書かれてますが、Railsに限って言えばFat Modelの方が良いと僕は思っています。理由は以下の2点。

  • 1. コントローラよりモデルの方がscript/consoleで動かし易い
  • 2. コントローラよりモデルの方がテストが書き易い

1については、ちょっと試したいときに非常に便利です。テストで書くべきかどうかをちょっと迷っちゃうところ ---例えば、このメソッドどう動くんだっけ?とか、正規表現のチェックとか電卓としてとか--- をサクッと試せちゃうところが素晴らしい。あ、正規表現とか電卓はirbを直で使ってるなw。irbが素晴らしいからですけど(関係ないけど、まつもとさんはirbじゃなくてruby -eを使ってたっけ)。

2については、RCovなどでのカバレッジ率を上げたい場合、複雑なパラメータを受け取るコントローラに条件分岐があると、条件分岐の数だけコントローラにget/post/put/deleteなどで複雑なパラメータを持つリクエストを送るようなテストを書かなければなりません。正直めんどいです。幾つかのテストで大量のパラメータの組み合わせが微妙に違ったりしやすいので、テストが読み難いし。できるだけ条件分岐をモデル側に移すことでテストのコードをすっきり読みやすくさせることができます。

ただし、モデルがメンテしにくいなーと思ったら別のクラスやモジュールに分けるべきです。べつにモデルはActiveRecord::Baseを継承しなくたっていいんだし(後述)。

個人的な感覚

さて、3年ほどRailsばっかりやってきましたが、MVCについて感覚的には「そういやRailsってMVCだよね」くらいです。モデル/ビュー/コントローラが、それぞれapp/models、app/views, app/controllers にあるクラスを指す感じで、特にいちいちMVCを意識しません。

慣れてくると役割をはっきりさせやすいので「テストし難いから」とか「他でも使うから」などの理由でクラス構成を見直すことはあっても、「MVC的にこのクラス構成は・・・」みたいな話は滅多に出ませんね。Railsの使いやすさが名前の浸透しやすさに繋がっているんだと思います。

具体的に言うと、app/models以下のビジネスロジックを書くところが「モデル」であり、そこにRDBに簡単にアクセスできる機能が付いている。RDBを使わないビジネスロジックもapp/modelsの下に入れるけど、その中のクラスではActiveRecord::Baseは継承しない。もちろんコントローラやビューへの依存はさせない。

コントローラやビューにビジネスロジックを書く人がメンバーにいたら「ここをこうすればモデルに持っていけるよね?」とコードをできるだけモデルに移すように促す。そこはメンバー間で話しながらやるべきでしょう。チームのレベルアップに繋がる大切なチャンスだし、フレームワークにその責任を持たせるのは現時点では難しいんじゃないかな、っていうかそんなのなんか勿体ない気がしてきました。

結論

MVCって言葉はキャッチーで、なんとなく分かった気にさせやすいから便利だろうけど、所詮時代と一緒に変わりゆくもの。「MVCだから簡単!」とか「MVCだからメンテしやすい!」っていうことはほとんどなくて、簡単さとかメンテのしやすさなんてフレームワークによって違ってきて当然。なにより一番影響があるのはそのフレームワークを使う人間がどれだけ分かってるかであって、「正しくMVCを実装しているか」とかはそんなに大した問題じゃないと思います。釣られてる僕が言うのもなんですがw

追記

これを書いた次の日に仕事で正に「Fat Model」を発見。24KBもあるモデル群の中身はある程度メソッドやインナークラスに分かれているんだけど、巨大すぎてSVNで変更を追うのも一苦労でした。これは良くない > <

重要なクラスに処理が集中するのは仕方がないとはいえ、VCSで変更履歴を追うのが難しかったり読み難かったりするのはやはり健康状態としてはよろしくないので、そうなったらインナークラスは別のファイルに分けるべきなのは当たり前として、メソッド群も関係するものを集めて適切な名前がつけられるならモジュールやクラスにまとめて構成を見直すべきですね。