Java開発者の読むDjangoの設計思想

Djangoのサイトには「Djangoの設計思想」というドキュメントがあります。どんなフレームワークでもそうですが、設計思想を理解し、その流れをつかむ事で正しい利用への最短ルートです。もし、自分の思想にあわないならば問題です。可能であれば、そのフレームワークの検討を取り止めるべきでしょう。それが出来ないならば利用している時にはそのフレームワークの思想で思考することが求められます。
Djangoの設計思想は、緩く結合し、必要最低限のコードで、だが隠蔽せずに明示するという事です。DjangoではMVT(モデル/ビュー/テンプレート)と呼ばれるMVCに近い構造をとります。それらの3つのレイヤーはお互いに疎な関係を持ち、モデルとテンプレートはデフォルトの実装以外を容易に採用できるようになっています。また、ほどよく規約を適用し必要なコード量は少なくなっていまが、なんでもかんでも裏側で処理せずに、なにかを行うには明示する必要があります。
このようにDjangoの設計思想は最近のJavaフレームワーク(Spring, Seasar2など)の目指してきた所とも言えます。Railsはどうしても好きになれないなと思っているJava開発者には是非とも触ってほしいフレームワークです。
このエントリーでは、ドキュメント「Djangoの設計思想」から引用しつつ、どんな考え方でDjangoが作られているかをJava開発者の視点で書いてみます。想定される読者としては、3〜4年以上のJavaの経験があり、最近のSpringやSeasar2などのフレームワークを使用した経験のあるJava開発者です。

全体的な設計思想

ルースカップリング

Django のスタックが目指す基本的なゴールは ルースカップリングとタイトコヒージョン の実現にあります。フレームワークの様々なレイヤは、本当に必要な場合を除き、お互いの事情を知らなくてもよいという考え方です。

Webアプリケーションフレームワークの場合、O/Rマッパ+ロジック(モデル)、テンプレート(ビュー)、ディスパッチャー(コントローラ)、DIコンテナといったコンポーネントに分割できます。Djangoではそれらを全て提供しますが、それぞれが疎結合となっており、好みや開発スタイルで使い分けられるという事です。
最近のプロダクトであれば、T2フレームワークは近い思想を持っています。T2ではコントローラ層に特化した上で、その他のコンポーネントを「つなげる」をテーマにしています。逆にRuby on Railsは、事実上はフルスタックで他のコンポーネントを組み合わせるメリットが少ないと感じます。
また、選択肢が他になくともそれが標準で、かつ誰もが納得できるのであれば良いでしょう。しかし、好みの問題があります。気に食わないので置き換えたいと思うかもしれません。ルースカップリングとタイトコヒージョンである事で、簡単にテンプレートやモデルを置き換えたり開発したりする事ができるわけです。
個人的にはデフォルトのテンプレートは気に食わないのでGenshiを使用しています。今後、Javaで実装したテンプレートエンジンPilikaも対応させてみたいと思っています。

利便性のため、 Django には全てのスタックがついてきますが、スタックの各部分は可能な限り互いに独立になっています。

とはいえ、標準で用意されているモデルとテンプレートはあります。したがって、迷ったらデフォルトを使えばいいので安心できます。

コード量の低減

Django アプリケーションのコードは可能なかぎり少なくし、冗長な決まり文句を排除します。 Django では、イントロスペクションのような Python の動的な決定機能を積極的に活用します。

Javaで開発してきたエンジニアであれば、これらの事がどれだけ大切か身(XML)に染みて知っているでしょう。冗長な設定ファイルはもう勘弁です。最近はSeasar2など規約やアノテーションを適度に使い、フレームワーク部分が設定を行うことが一般的になってきました。Railsも規約重視ですが、やり過ぎ感は否めません。

DRY (Don’t repeat yourself)

個別のコンセプトやデータは、一つの、ただ一つの場所に置かねばなりません。冗長は悪、正規化は善です。

例えば、Djangoではテーブル定義とモデルのエンティティ定義とバリデータの定義などを全てモデルクラスに記述します。これによりモデルクラスを見るだけで、データに関するすべての仕様を把握できる事になります。
もし、テーブル定義とモデルの属性定義が別であれば冗長です。冗長であれば、片方を変更した場合にもう片方の変更を忘れるかもしれません。コンパイラが問題を解決できるJavaであればそもありですが、Python動的言語性を生かすとなると、モデルクラスに記述することが妥当なのでしょう。

暗示的より明示的に

Python のコア原理 の一つでもあるこの思想により、 Django は「黒魔術的」であってはなりません。どうしても必要な理由がないかぎり魔術的な処理を取り入れてはなりません。魔術的な処理を取り入れる価値があるのは、他の方法では実現し得ない多大な利便性が生まれ、かつその機能の使い方を学ぼうとする開発者が混乱しないような形で実装できる場合だけです。

恐らくはJava開発者がRailsよりはDjangoを好む傾向があると思います。その理由はこの思想です。他の言語をメインとしている人からすれば、変数の型を明示することすら面倒と感じるようです。しかし、Java開発者ははっきりと定義する事で、コンパイラがエラーをはかない事で落ち着く種族なのです。
例えば、黒魔術の代表格Railsでは、モデルに属性名を記述することすら省略することができます。結果としてコード量は減りますが、フィールド名を忘れた時にはDBスキーマやテーブル定義を参照する必要があります*1。これは便利な反面、なにやら危ない橋を渡っているようで受け入れられないJava開発者はいるでしょう*2
しかし、Djangoではモデルにフィールド定義を明示的にします。少々違和感を感じますが、入力フォームを想定した定義になっています。このおかげでバリデータなどを別途定義する必要なく、想定される標準の動きをモデルに集約して記述できます。
また、様々な処理や宣言が明示的になっているため、ソースコードを追う起点としても重宝します。

モデル

関連領域のロジックは全てまとめる

そのため、モデルの表現するデータや、モデル自身の情報 (人間可読な名前、デフォルトの整列順など) は、モデルクラスで定義されています。あるモデルを理解するのに必要な情報は、全てモデルの 中に 入っているのです。

Djangoのモデルは次のようなコードになります。

from django.db import models
class Poll(models.Model):
    question = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

JavaMVCを徹底してきた人は、「モデルにCharFieldってありえないでしょ、それはどちらかと言えば表示(ビュー)に近い情報じゃん」と感じると思います。ですが、Webアプリのモデルは画面の入出力に直結する事が多くあります。さらに直結するように設計しておく方が楽な事も知っている筈です。
Djangoでは純粋なオブジェクト指向の設計をしたいのではありません。Webアプリケーションを手早く変更に強く作りたいのです。そして、モデルの情報がモデルに集約されている事のメリットを天秤にかけると、モデルにあることも悪くないと思えてきます。気がつけば、「気持ち悪さ」は気にならないほど魅力的に感じるでしょう。

データベース API

必要なら生の SQL も簡単に使えるように

データベース API の設計では、ショートカットとして便利でありながらも、必ずしも全ての機能に手がとどかなくてもよいということを理解していなければなりません。フレームワークSQL 文全体、あるいは WHERE 節だけのカスタムの SQL を簡単に書けるようにせねばなりません。

Java界隈ではHibernate,S2Dao,S2JDBCなど色々なO/RマッパーやDaoがありますが、個人的に好きなのはS2Daoです。理由としては特に検索系SQLを透過的に書く事が可能という点があげられます。S2JDBCなどでも、簡単な検索クエリーであればJavaのコードでSQLを組み立てられます。ですが、解りやすさとパフォーマンスなどの柔軟性から生でSQLを記述したいと思っていました*3
逆にS2JDBCのようなタイプセーフなフレームワークJavaらしいと言えます。例えば、SQLの構文エラーはS2Daoでは検知できませんが、S2JDBCではコンパイルエラーとなります。
DjangoのデータベースAPIでは基本メソッドは提供しますが、複雑なクエリーは簡単にSQLを使用できるようになっています。これらはPythonの構文(メソッド引数など)と相性も良いと思われます。もし、S2Daoを好んでいた人にはすんなり受け入れられるでしょう。

URL の設計

ルースカップリング

Django アプリケーションでは、 URL を特定の Python コードとカップリングしてはなりません。 URL と Python 関数名の関連づけは、間違っており、美しくありません。

URLから特定のクラスの特定のメソッドを暗黙的に結びつける方法はJavaフレームワークでもRailsでも採用されている方法です。ですが、自然に見せるとなると意外と難しいものです。
例えば、Teedaでは拡張子を.htmlにできますが、同時に静的なHTMLも扱おうとすると.htmとしなければならないなど、制限が出てしまいます。Djangoでは正規表現による明示的なマッピングを行うため、柔軟で解りやすく、さらに集中的な定義を行えます。

同様に、 Django の URL システムは同じアプリケーションを異なるコンテキストで使えねばなりません。例えば、あるサイトで記事 (story) にアクセスするのに /stories/ を使っていたとしても、別のところで /news/ という URL で記事にアクセスできねばなりません。

さらに別のURLで同じページを見せるなどの柔軟な定義もできるのはDjangoの特徴かと思います。

王道を進みやすく

Web ページの URL にファイル拡張子を使うのは避けねばなりません。

もろばれなStrutsアプリだと、拡張子が.doのままだったりします。この手の拡張子は避けた方がいいでしょう。

URL にカンマを入れる Vignette スタイルは厳しく禁じねばなりません。

Vignette スタイルってのは海外で有名な変態CMSらしく、カンマでパラメータを渡すそうです(笑)
http://japan.cnet.com/news/tech/story/0,2000056025,20319009,00.htm

テンプレートシステム

プレゼンテーションとロジックの分離

私達は、テンプレートシステムはプレゼンテーションとプレゼンテーション関係のロジックを制御するためのツールであり、それ以上のものではないと考えています。その本分をこえた機能をテンプレートシステムに求めるべきではありません。

Djangoでは、MVCとは呼ばずにモデル-ビュー-テンプレート(MVT)と呼びます。テンプレートはモデルをどのように表現するかを責務とし、ビューはどのモデルを表示するかを責務としています。したがって、テンプレートがテンプレート以上の機能を提供しないとしています。

何もかもテンプレートに押し込みたかったのなら、今ごろ PHP を使っていたでしょう。かつてそうしていましたが、今はやめ、そこから学んだのです。

JSPでもなにもかもビューに押し込んで記述していた時期もありましたが、行き着いた場所はテンプレートの分離だったわけです。ちょっとした規模であればPHPで全部ビューに押し込めるのもありかと思います。PHPはむしろそのような状況の方が向いていると思います。適度に分離するならばしっかりとした基盤の組める言語であるべきですが、Pythonはそれを充分に提供しています。

XML をテンプレート言語に使わない

テンプレートのパージングに XML エンジンを使うと、テンプレート編集における人為エラーという新たな問題に直面します。それに、テンプレート処理に受け入れがたいオーバヘッドを被ることになります。

テンプレートをXMLにした場合、パフォーマンス面の問題がどうしても発生します。それは、XMLのパーシングに関するコストです。とはいえ、JSPのように一度解析すれば後はコンパイル済みのコードが実行されるような仕組みを提供できれば、そこまでの問題にはならないでしょう。
尚、自分はXMLであるべきという立場です。人為的エラーも構文を間違えたりレイアウトを崩したりする問題は残るのでデザイナーに優しいXMLがベターと考えます。しかし、Djangoではテンプレートを切り替える事が容易です。よって、GenshiなどのXMLテンプレートエンジンを使用する事もでき、柔軟に対応することが出来ます。

プログラミング言語を作り直さない

テンプレートシステムでは、以下の機能を意図的に使えないようにしています:
* 変数の代入
* 高度なロジック
テンプレートシステムが目的とするのは新たなプログラミング言語の発明ではありません。目的は、分岐やループといった、プレゼンテーションまわりの判定で必須のプログラム機能の提供だけです。

Django テンプレートシステムでは、最もテンプレートを良く書くのは プログラマ ではなく デザイナ とみなしており、 Python の知識を前提にはしていません。

Djangoの思想ではある程度ロジカルな考え方のできるデザイナが簡単なロジックを埋め込みながらテンプレートを構築する事を想定しています。これはJSPなどに近い考え方です。
Djangoでは見た目はRailsのrhtmlに近いテンプレートを提供していますが、Railsでは何でも出来るのに対して、Djangoでは意図的に出来る事を制限しています。これによりデザイナが書きやすいテンプレートとなっています。
繰り返しになりますが、自分ではページデザインをメインとして行うデザイナがHTMLをコーディングし、そのままの形でテンプレートとして利用できる形が好みです。理由はプレビューの問題や分業の問題です。

ビュー

簡潔性

ビューは Python の関数として可能な限りシンプルに書きます。開発者は関数でできることを実現するために、クラスのインスタンスを生成する必要はありません。

Djangoのビューはシンプルな関数です。必要な機能をモジュールで組み込む事は出来ますが、ビュー自体は単純な関数でしかありません。裏で黒魔術が行われている事もありません。
T2フレームワークでもそうですが、ビューはシンプルで薄いレイヤーである方が奇麗なWebアプリケーションフレームワークと感じます。

リクエストオブジェクトの利用

ビューはリクエストオブジェクトにアクセスします。リクエストオブジェクトとは、現在のリクエストに関するメタデータを入れるオブジェクトです。ビューはこのオブジェクトをグローバル変数経由でアクセスするのではなく、引数として直接受け取るようにすべきです。それにより、「偽の」リクエストオブジェクトを渡してビューを簡単かつクリーンにテストできるようになります。

この思考もT2フレームワークに近い考え方です。ビューは薄くあるべきですが、抽象化されすぎている必要は有りません。また、リクエストオブジェクトを直接受け取る形をとることで、柔軟な処理とテストの容易性を担保しています。
Servletは同じようにリクエストオブジェクトを引数に持ちましたが、それ自体のインスタンスを生成するのが困難でした。単純な関数の第1引数がリクエストオブジェクトであるだけというシンプルな形を採用しています。
T2フレームワークでも同様にPOJOのメソッドにリクエストオブジェクトを渡す形式です。

まとめ

Djangoの設計思想が少しでも伝わったでしょうか?
DjangoJava開発者にとって馴染みやすいWebアプリケーションフレームワークかと思います。ドキュメントも充実していますし、これを機に興味を持たれる人が1人でもいれば幸いです。

*1:NetBeansを使うと補完してくれるので助かります

*2:最近は慣れましたが、自分もその1人。

*3:S2JDBCなども使う事はできるが基本はタイプセーフに使う