2010-09-29

Python で書いたフィルタを GAE に載せる #3

まず最初に、今回の記事で書こうと思っていることを並べてみる。

  1. ウェブアプリのテンプレートについて考えたこと
  2. 全体をフィルタ(っぽい構造)にこだわらずに書き直したこと
  3. Blogger Glass を github にプロジェクトとして登録したこと

そんなわけで、今回が「Python で書いたフィルタを GAE に載せる」シリーズの完結編。といっても、できあがったモノはようやくプロトタイプのレベルなんだけどね。

ウェブアプリのテンプレート

前回、GAE のテンプレートの仕組み (Django) では複雑なことはできないと書いた。後になって改めて考えてみたところ、これは見当違いな思い込みだと気付いた。テンプレート(というか HTML)も「何か」を記述するための言語であることは間違いないし、見方によってはプログラミング言語だと言えなくもない。しかし、その記述の仕方は手続きを積み重ねて行く Ruby や Python とは別物になる。

Python でプログラミングするなら「あれして、それして、その後こうする」と考えるし、そのようにコードに書く。言わば動的(ダイナミック)な思考方式だ。思考のかけらが時系列に並ぶようなイメージ。一方、HTML でページを作るときは「あれは○○、それは△△、そしてこれは□□」というように考えて、要素を配置する。こちらは静的(スタティック)な思考と言える。言葉を変えれば、HTML でページを作るときに「h1 の内容を、40 pt で出力してから、その後 h2 の内容を 24pt で右揃えで出力する。それが完了したら h3 の内容を 20 pt で左揃えで出力する」などとは考えない、ということだ。

テンプレートは、(汎用プログラミング言語で)プログラムを書くように作るものではない。テンプレートにループや条件分岐があるのは、そこに配置するオブジェクトを出力するための補助として、だ。

また、前回の「テンプレートとコードが密に絡んでしまっている」という評価は、リクエストハンドラからテンプレートを呼び出すと考えてしまったことでできた悪いデザイン(設計)に対するものだ。これは、本来、リクエストハンドラが「表示用データをくるんだオブジェクト」をテンプレートにわたす、と考えるべきだった。これなら、リクエストハンドラとテンプレートは(データオブジェクトを間に挟んで)分離していると言える。

言い換えると、リクエストを処理して結果のデータを用意するコードと(バックエンド層)、ユーザの目に見えるテンプレートから生成される HTML (プレゼンテーション層)を直接つなげるから(あるいは、つながっていると考えるから)ダメ(悪いデザイン)になる。ここは、両層は独立したものだと考えて、間をつなぐ「何か」をデザイン(設計)すべきなのだ。一般的には、この「何か」をプロトコルと呼ぶ。

多層構造にして、層間のプロトコルを定義する、と書くと大袈裟だけど、要はコード(バックエンド層)を考えるときにはテンプレートの構造ではなくて、処理結果の情報が何でそれをどうまとめるかを考える。次にテンプレート(プレゼンテーション層)を考えるときには、バックエンドで何が起こっているかは気にせず、下層から上がってくるデータ(をくるんだオブジェクト)を表示することに集中する。

こう考えてくると、フィルタ構造の中で HTML のリストを生成していたのはウェブアプリには適していないとわかる。フィードのタイトルとリンクをどう表示するかは、バックエンド層で決めることではないからだ。それはテンプレート(プレゼンテーション層)で扱う問題だ。順序つきリストにするかもしれないし、順序なしのリストにするかもしれない。あるいは、テーブルで表現するかもしれないし、div で囲むだけかもしれない。バックエンドはただ表示すべきデータを集めて上層に送るだけで良い。

フィルタ構造が足枷になっている

プログラムを、とくに入力を処理して出力に変えるようなモノを、いくつかの単純なフィルタの組み合わせとして実現することは、コーディング/テスト/デバッグのいずれの作業も簡単にしてくれる。しかし、ここで肝心なことはフィルタ(つまり標準入出力で互いにつながるというアーキテクチャ)そのものではなく、複雑な処理を独立した小さな処理に分割できること、そして分割した処理ごとに動作確認ができることにある。

ウェブアプリでは、(バックエンドの)処理を分割して、個別に動作確認するような手法は取りにくい、というか取れない。というのも出力をそのまま入力につなげることができないからだ。途中結果をテキストとして出力してブラウザの画面で確認することはできても、それを次の処理の入力にはできない。

つまり、フィルタとして実現したプログラムをウェブアプリに変える際に、元の構造にこだわることは足枷になる。すっぱりとフィルタのことは忘れてしまう方が良い。構造は忘れて、処理の中身だけを移すようにすれば良い。例の「ブログの記事一覧を作るプログラム」なら、フィードの取得や、そこから正規表現で記事のタイトルと URL を抽出する部分はそのまま使える。

今回の書き直しでは、Blogger のフィード自体をデータオブジェクトとして捉え直した。AtomFeed というクラスがそれだ。このクラスのインスタンスに対して、get メソッドをページ番号を引数として呼び出せば、Blogger からフィードを取得し、それを Atom としてパースし、内部リストに Entry クラスのオブジェクトとして蓄える。Entry クラスはフィードにふくまれる entry 要素(のデータ)を保持するためのもので、今のプロトタイプ版ではタイトルと URL だけを収めている。そして、この Entry クラスのオブジェクトこそ、バックエンド層(リクエストハンドラ)とプレゼンテーション層(テンプレート)をつなぐプロトコルになっている。つまり、リクエストハンドラは AtomFeed をインスタンス化し、リクエストされたページを指定して get を呼び出せば、後は Entry オブジェクトのリスト(AtomFeed オブジェクトの entries プロパティになっている)をテンプレートにわたせば良いことになる。(→ bloggerglass/src/feed/core.py)

テンプレートの方では、リクエストハンドラから渡されてきた Entry オブジェクトのリストから単純なループを使ってオブジェクトを取り出し、さらにその title プロパティと url プロパティの値から、順序つきリストを生成している。(→ bloggerglass/src/page.html)

ついに公開……そして今後の展望

GAE への配備はまだだが、先に github にプロジェクトとして登録した(関連リンク参照)。これが、初めての github プロジェクトなので、公開にあたっては多少の右往左往があったりもしたが、それは別途書く予定だ。

最初にも書いたが、今の段階ではまだアプリのプロトタイプにすぎない。「アプリのかけら」が少し成長したと言った程度だ。元々の目的である「Blogger で作ったブログを Blogger とは独立した表示システムで見る」を実現するには、まだ作業が必要だ。

今後は、まず iPhone/iPad での表示に最適化する。これは主に HTML (テンプレート)と CSS をいじる作業になる。その後は、個別の記事を Blogger Glass 内で表示する機能を追加する。記事のデータそのものはフィードとして取得しているのだから、後はそれを流し込むテンプレートを用意すれば良い。ただ、記事内に張ったブログ内の他記事へのリンクをどうするかが課題だ。これは Blogger Glass 内の記事ページへのリンクに付け替える等の処理が必要になる。ここまでできたら、Blogger Glass としては ver. 1.0.0 としても良いかと考えている。

関連リンク

関連記事

0 件のコメント:

コメントを投稿