フレームワークは便利で有用なものでなければなりません。そのために一番重要なこととは何でしょうか?
機能の充実ではありません。なぜなら、難しくて使えないものは便利ではないからです。
一般的に、機能の充実を目指してしまうとこの罠にはまります。機能が多すぎて利用者の理解が及ばなくなるのです。
理解が及ばなくなると、せっかく苦労して揃えた機能も宝の持ち腐れとなってしまいます。これでは残念ながら、利用者の要求を満たすことはできません。
かと言って、機能が不足しているものは、そもそも便利ではありません。これもまた不合格です。
すると、「便利で有用」というシンプルかつ高度な要求にこたえるには、いったいどのようなものを作ればよいのでしょう?
実はこのあたりの設計が一番難しいところなのです。
人間はたくさんの新しいことを一度に覚えることができません。
ゆっくりと時間を掛け、既存の知識と関連付けながら新しい知識をひとつずつ「既知化」していくアプローチが必要になるのです。
このような手法は、比較的時間に余裕がある場合には有効ですが、目の前のニーズをただちに具現化することを迫られている開発者にはあまり向きません。
新しく覚えることは少しでなければならないのです。
もっと正確に言うと、新しく覚えることが少しでも使いはじめることができるものを作らなければなりません。
初期学習はなるべく少ない方がいいのです。とりあえず最初の第一歩を比較的簡単に踏み出せることが、「便利で有用」であるための最初のステップとなります。
Sakana Frameworkでは、このような観点から利用者による設定が極端に少なくなるように設計しました。
設定ファイルは一つしかありません。sakana.xmlと命名されたこのファイルには、必要最小限のことしか記述されていません。
運が良ければ、あなたはこのファイルを修正する必要すらないかも知れません。
既存の動いているプロジェクトがもしあったとすると、そのプロジェクトで利用しているsakana.xmlを入手してくるだけで済んでしまうかも知れません。
多くの場合、デフォルト値をそのまま利用してもあまり問題は起こらないでしょう。
もちろん設定が少ないことは、便利なことばかりではありません。
設定が存在する理由は、システムに対して利用者が何をしたいのかを教えるためです。
これはどこかで代替してやる必要があります。
その代替方法がコストの高い(俗っぽく言えば面倒な)ものであれば、あまり意味はありません。
幸いなことに、これを読んでいる皆さんは、熟練したJavaの開発経験をお持ちだと思います。
私はその豊富な知識と経験をあてにさせていただくことにしました。
つまり、Javaの地の文で設定を記述してしまうようにしたのです。
このやり方は、新しいxmlファイルの文法を覚え、配置場所を決定し、Javaのソースファイルと交互に開いては修正し、開いては修正していく手法に比べて、はるかに
効率のよい開発方法となります。
設定がJavaのソース内に集約されるので、複数のファイルを編集し、かつその内容の同期をとる(自分の目で見比べる)必要がないし、もし間違った記述をすればあなたの賢明なアシスタントであるEclipseが即座に指摘してくれるでしょうから。
このような考え方は、少ない設定(Less Cofiguration)と呼ばれ、最近(2009年5月現在)いろいろなところで耳にするようになったアプローチです。
Sakana Frameworkではこの考え方を全面的に採用し、さらに推し進めています。
インスタンスの自動生成、SQLの自動生成などの局面で、この少ない設定の考え方を利用しています。
具体的には、クラス名やメソッド名に一定の命名規則を設け、その規則に従っているクラスやメソッドを自動処理の対象にするという手法です。
例えば、あるアプリケーションで利用者がログインボタンを押下した際に呼び出されるサーブレットは、認証サービスやトランザクション制御の仕組みを利用したいとします。
サーブレットはフレームワークに対し、何も明示的な要求をする必要はありません。
サーブレットがしなければならないことは、ソース内に認証サービスクラスのインスタンスを設定するセッターメソッドを定義するだけです。
これは非常に簡単でシンプルなやり方です。ただし、シンプルすぎる故の問題も存在します。
具体的には、セッターメソッドの名称が厳密に規定されます。
仮に認証サービスを実装したクラスの名称がCertServiceだとすると、セッターメソッドは必ずsetCertServiceでなければならないのです。
これは利用者に対する制約となります。
それでも、設定ファイルにサーブレットのクラス名と、サービスのクラス名のペアを記述するよりはましだろうというのが、少ない設定の基本的な考え方となります。
利用するサービスが、認証サービスから、簡易認証サービスに変わった場合も、セッターメソッドの名称をsetCertServiceからsetFriendlyCertServiceにリファクタリングするだけで済むのです。
最後にいくつか重要なことを書いておこうと思います。
本当は最初に書いてもいいくらいの重要なことですが、話題の展開上この位置に持ってくる必要があったいくつかの事柄です。
1として、Sakana Frameworkは画面出力もJavaの地の文で行うことを基本としています。
これは、やや実験的なアプローチになりますが、画面で使用する変数などもJavaの世界で管理したかったというのが一番の理由となります。
例えば、Hello World!プログラムは以下のようになるかも知れません。
write(HTML);
write(HEAD);
write(TITLE);
write("Hello World Page");
write(TITLE_END);
write(HEAD_END);
write(BODY);
write("Hello World!");
write(BODY_END);
write(HTML_END);
普通にHTMLで記述した方が見やすいと思います。なぜなら、皆さんはHTMLは見慣れていますが、上のような記述にはなじみがないからです。
それでは、次のようなコード例はどうでしょうか?登録、変更画面で、メールアドレスの入力を行う部分だと思ってください。
write(form(POST, "/itaten/regist"));
write(input(TEXT, name(varMailAddress) + size(mailAddressSize) + maxlength(mailAddressMaxLength) + value(mailAddress)));
// バリデータを設定
addValidator(mailAddressLabel, varMailAddress, ValidatorTypes.NECESSARY, null, varChangeButton, ExecuteOption.ALWAYS);
addValidator(mailAddressLabel, varMailAddress, ValidatorTypes.MAX_LENGTH, null, varChangeButton, mailAddressMaxLength, ExecuteOption.WHEN_NORMAL);
addValidator(mailAddressLabel, varMailAddress, ValidatorTypes.MAIL_ADDRESS, null, varChangeButton, ExecuteOption.WHEN_NORMAL);
…
addValidator(new CompareNewMailAddressValidator(), "/itaten/config", null, ExecuteOption.WHEN_NORMAL);
このような例だと、Javaの地の文で記述するメリットが出てきます。
バリデータの実行条件などを細かく制御することができます。
上の例では、必須チェックのバリデータ(ValidatorTypes.NECESSARY)は常時実行されます(ExecuteOption.ALWAYS)。
しかし、最大長チェックのバリデータ(ValidatorTypes.MAX_LENGTH)やメールアドレスの形式チェックを行うバリデータ(ValidatorTypes.MAIL_ADDRESS)は、必須チェックでエラーの場合に重ねてチェックする意味はあまりないので、実行オプションを正常時のみ(ExecuteOption.WHEN_NORMAL)にしています。
また、もう一つのCompareNewMailAddressValidatorが実行されるのは、formのactionが"/itaten/config"の場合のみとなります。
画面上で使用される変数名(IDまたはNAME属性)もJavaの地の文で記述されますので、記述ミスをすることがありません。
上の例では、varMailAddressがそれに相当します。
もちろんこのようなアプローチには欠点も多々存在すると思いますが、現在はこのような仕様となっています。
さて、重要なことの2つ目としては、DIコンテナを実装していないという点があります。
自ら実装していないのみならず、他のDIコンテナを利用するというアプローチもしていません。
理由はいろいろあるのですが、主に遅かったりビルドや再起動が必要になったりするのを忌避しています。
こうした手間による時間のロスが、開発効率をかなり低下させてしまっていると思うからです。
したがってSakana Frameworkでは、インスタンスの生成やSQLの生成といった作業をすべて実行時に行っています。
もちろんこのことは実行コストを増大させますが、たいていの場合そのようなデメリットが表面化するケースは限定されています。
規模が非常に大きく、レスポンス要求がシビアなアプリケーションだけです。そもそもそんなアプリケーションはJavaでは作らないでしょう。
極論すると、実行コストはサーバを買いかえればいいのです(^_^;)
実行コストとは年とともに限りなく安くなる代物であり、多分10年もすれば10分の1くらいになります。
けれども開発コストはそうはいかないのです。
Eclipseは出現しましたが、一般人がJavaを使ってWebアプリケーションを作れるようにはならないのです。
やや意図的な例をあげてみましょう。
江戸時代の庶民に、ルノワールやモネのような絵を描ける人はほとんどいなかったと思いますが、このことは現代人にも当てはまります(開発コストは安くならない)。
一方で江戸時代の庶民に、ルノワールやモネの絵を見たことがある人はほとんどいなかったと思いますが、現代人の多くはこれらの絵を見たことがあると思います(利用コストは安くなる)。
利用コストと実行コストは厳密には違いますが、同じカテゴリに属する概念だと強弁すれば、実行コストの低減よりも開発コストの低減がより急務であるという結論に達することができます。
そんなこともあって、DIコンテナは使用していません。
ある意味でこれもまた実験的と言えるかも知れません。