RoRでプロトタイピングするのはアリだと思う

こちらの記事を見つつ考え。

迅速なプロトタイピング(アジャイルプロトタイピングと意味が同じかもしれない)は、とりあえず作ってみることで不明点を洗い出し、顧客とイメージを共有するためのツールだ。言い換えると、プロジェクトのリスクを軽減するための手法だと思う。

作成されたプロトタイプはその半分以下のコードしか本番コードに流用できないことが多いようだ。しかし、重要なのは流用性ではなく、不明点を出し、とりあえず動きそうなモデルを作るという態度の問題だろう。この態度は「リスクを回避するためにコストを投下する」というものだと言える。

リーンソフトウェア開発中での「オプション志向」、「決定を遅らせる」、もしくは熊とワルツを、におけるリスクの軽減策、いずれにも共通して登場するのは「決定をギリギリまで遅らせる」「しかしギリギリではマズいものは事前に検証する」ということで、この両者のバランスが重要。プロトタイピングに時間をかけすぎるのは、リスクヘッジにコストを投下し過ぎることになる。しかしまったく作らない場合はリスクヘッジを行なわず、情報が少ないまま(やってみたことがないのだから開発チームは想像と机上の範疇でしか物事を判断できない)突き進むことになる。

ここでいうコストはもちろん「効果」との兼ね合い、つまりコストパフォーマンスということになる。かなりカネはかかるが、流用性が高いとか、カネはかからないが、せいぜい技術検証で顧客との擦り合わせには使えない、とかいろいろとサイズがある。個人的には、RoR(Ruby on Rails)でプロトタイピングする、というのがバランスがいい投資(コストも割とかかるがいろいろなことが明らかになる選択)のような気がする。

RoRを使ってプロトタイピングする場合、画面デザインに一定の自由度が与えられる。ほとんどモックのようなページコントローラをさくさくと作り、かつ楽をしようと思えば、テーブル定義もすることになる。ActiveRecordパターンになるものの、このパターンはドメインモデルを反映したものになるのでロジックも含めて再利用性は充分に高い(本番構築フェーズでJava/C++などの実装を行なうことを想定しての考え)。そう考えると、論理データモデル、コントローラの責務、画面のフローとデザイン、という要素をそこそこに押さえておけるのだ。前者2つは開発上の問題点/リスクポイントの発見に役立つし、画面まわりは顧客との擦り合わせに効果を発揮する。

独特のフレームワーク、というか学習曲線がいびつ(入り口は緩いが、途中から随分ハードルが高くなると思う)な気がするが、プロトタイピング用のツールとして勉強する価値はあるような気がする。

…って全部Webアプリケーションの場合の話やねー。

Click入門記 その4 〜 Pageから他のPage/URIへforward

ActionLinkなどコントロールのコールバック処理の結果、他の画面/機能に遷移したいことがあるでしょう。

機能を呼び出すのであれば Service的なインタフェースを呼びだせばいいだけの話ですが、画面遷移を伴う場合にはForward/Redirectが必要です。

Clickでは2つのforwardスタイル(フレームワーク支援), 1つのredirectスタイルを実装しています。加えて、ビューのhtmlファイルだけを差し替えるsetPath()というものがあります。

以下、一般的な事柄ですが押さえておきましょう。

  • forwardの場合はリクエスURIが変化しませんのでPOSTの扱い/reload/bookmarkに注意しましょう
  • redirectの場合はクエリ文字列なしでは情報をうまく引き継げないことに注意しましょう

URIベースのsetForward

PageクラスのsetForwardメソッドを使ってforwardを実施する方法です。この方法では、RequestDispatcherを使ったforwardが実施されるため、以下のような遷移になります

  1. HogePageの処理の過程で setForward("fuga.htm") する
  2. fuga.htm はRequestDispatcherで処理され、結果FugaPageがコールされる
  3. FugaPageで後続の処理を実施すると、fuga.htmがレンダリングされる

もちろん、上記の例はclick.xml, web.xmlなどの設定に依存しますのでほんの一例です。ここで注意してほしいのは

  • HogePage内でaddModel() addControl()したものは破棄されている
  • FugaPageでaddControl()したコントロール達は onProcess()が処理されない
    • なぜなら、Clickフレームワークで混乱防止のためにわざとそうしている(らしい)
    • onProcess()はコントロールがクリックされた場合などの判定のために使われています。ということは新しいPageがコントロールを持っているとしても、そのコントロールがイベントを処理してはいけない(しないほうがいい)ことになります。HogePageとFugaPageに同じ名前のコントロールがあるとして、HogePageでイベントハンドリングして、FugaPageにforwardしたところ、FugaPageでもクリックしたと誤認してしまう可能性があるからです。

ということです。

では、どうやってPage間で情報を受け渡しするかというと、requestのattribtueを使います。HttpServletRequestを取り出してsetAttributeすればいいわけです。ヘルパーメソッドとして

 getContext().setRequestAttribute("customer", customer);

こんな風にPageクラス内でかけます。

例は以下の通りです。

 public boolean onEditLinkClicked() {
  Customer customer = customerDao.findById(_editLink.getValueInteger() );
  getContext().setRequestAttribute("customer", customer);  
  setForward("hoge.htm");

  return false;
 }
  • return falseによって後続の処理を中断します
  • 2つのPage間での情報の受け渡しは requestのattributeです

PageインスタンスでのsetForward

URIベースでディスパッチする方法以外に、Pageクラスを明示的に指定してフォワード先を指定する方法もあります。この場合、以下の恩恵があります

  • 型付けの恩恵を受けることができる
    • たとえばプロパティにsetしてからforwardできる
    • attribute経由とかにならないのでやや洗練/再利用性が高まる

もちろん、Pageクラス自体はgetContext()などを介してClickフレームワークに強く依存していますから、あまり意味がないといえばないのですが、Pageインスタンスにプロパティを設定して、あとはよろしく、という流れは割と自然なので、好き好きで使い分けるといいでしょう。

forward元で addModel, addControl しても破壊されるのは同じですのでこの点も注意してください。

例は以下の通りです。

 public boolean onEditLinkClicked() {
  HogePage hogePage = (HogePage) getContext().createPage(HogePage.class);
  hogePage.setFuga( _editLink.getValueInteger() );
  setForward(hogePage);

  return false;
 }
  • HogePageに対してtype-safeなソースです (castしてますけどね)
  • setFuga()のような渡し方が URIベース+attributeベースのパッシングと異なります
  • return falseによって後続の処理を中断します

setRedirectでリダイレクト(30x)

いわゆるリダイレクトがこちらの方法です。request#sendRedirect()同等ですね。
これはすごくシンプルです。

 public boolean onEditLinkClicked() {
  setRedirect("/hoge.htm?param1="+_editLink.getValue());
  return false;
  • 要するにURIを生成してリダイレクトしてるだけ
  • 情報を受け渡したいならパラメータにどう入れるか、というあたりも考えないといけない
    • この方法は必須だけど、なんだか悲しい
  • return falseで後続処理を中断するのは同じ

ビュー(HTML)だけ差し替える

HogePage→hoge.htmlのようなPage:HTMLの関係があるとき、HTMLファイル、つまりテンプレートだけを違うものにしたい場合があります。この場合は

 setPath("anotherView.html");

とするだけです。これはforwardでもredirectでもなく、フレームワーク(コントローラ)に対する指示だけですので、Pageインスタンスが変わることがありません。そのため、addModel(),addControl()などはそのまま活きます。

この方法はビュー層だけを置換する機能を持たせるときには有用です。