レベル: 中級 Andrew Glover (aglover@stelligent.com), CTO, Stelligent Incorporated
2005年 4月 12日 Groovy Builderを使うと、マークアップ言語、例えばXMLやHTML、Antタスク、さらにはSwingのようにフレームワークを持つGUIまで、を真似ることができます。これは、素早くプロトタイプを行う際に特に便利です。今回の実用的なGroovyでは、コラムニストのAndrewGloverが、Groovy Builderを取り上げます。手軽に使えるマークアップを瞬時に作りたい場合に、データ・バインディング・フレームワークに代わるものとしてGroovyBuilderがどれほど便利なものか、皆さんも理解できるでしょう。
数ヶ月前、私が初めてGroovyによるAntスクリプトについて書いた時、私はGroovyでのBuilderの概念について触れました。その記事の中では、AntBuilderと呼ばれるGroovyクラスを使って表現力豊かなAntビルド・ファイルを作ることが、いかに容易かを示しました。この記事では、GroovyBuilderの世界をさらに深く探り、こうした強力なツールで他にどんなことができるかを説明します。
Builderでビルドする
GroovyのBuilderを使うと、マークアップ言語、例えばXMLやHTML、Antタスク、さらにはSwingのようにフレームワークを持つGUIまで、を何の苦もなく真似ることができます。ビルダーを使うと、XMLのような高度なマークアップを、XML自体を扱うことなく、素早く作ることができるのです。
Builderの使い方は、実は非常に単純なのです。Builderのインスタンスに付加されたメソッドは、そのマークアップ(例えばHTMLでの<body>タグ)の要素を表します。メソッドに付加されたクロージャー(closure)内で作られたオブジェクトは、子ノード(例えば、<body>タグの内側にあるタグ<p>)を表します。
これが実際にどうなっているかを示すために、リスト1に示す構造を持つXML文書をプログラム的に表現する単純なBuilderを作ります。
 |
このシリーズについて
どのようなツールであれ、開発作業の中に採り入れるためには、どういう場合に使うべきか、または使うべきではないかをよく知る必要があります。スクリプト言語は非常に強力なツールですが、その強力さは、適切なシナリオで適切な使い方をした場合にのみ発揮されます。実用的なGroovyシリーズはそうした点を念頭に置き、Groovyの実用的な使い方に焦点を絞って、どういう場合に、どのように使うのかを解説して行きます。
|
|
リスト1. 単純なXML構造
<person>
<name first="Megan" last="Smith">
<age>32</age>
<gender>female</gender>
</name>
<friends>
<friend>Julie</friend>
<friend>Joe</friend>
<friend>Hannah</friend>
</friends>
</person>
|
この構造を表現するのは、ごく単純です。まず、personメソッドをBuilderインスタンスに付加すると、これがXMLのルート・ノード(<person>)を表すことになります。子ノードを作るためにクロージャーを作り、mapという形式のパラメーターを持つ、nameという名の新しいオブジェクトを宣言します。ついでに言うと、こうしたパラメーターは、要素に付加される属性の基礎となります。
次にnameオブジェクト内で、2つの追加オブジェクトを持つクロージャーを付加します。この追加オブジェクトはageとgenderであり、<name>の、同じような子要素に対応します。だんだん分かってきましたか。ごく簡単なことなのです。
<friends>要素は<person>の兄弟分なので、私はクロージャーから飛び出し、friendsオブジェクトを宣言し、そしてもちろん、friend要素の集合を含むクロージャーを付加します。これをリスト2に示します。
リスト2. Builderはこんなに単純です
import groovy.xml.*
import java.io.*
class XMLBuilder{
static void main(args) {
writer = new StringWriter() builder = new MarkupBuilder(writer)
friendnames = [ "Julie", "Joey", "Hannah"]
builder.person() {
name(first:"Megan", last:"Smith") {
age("33")
gender("female")
}
friends() {
for (e in friendnames) { friend(e) }
}
}
println writer.toString()
}
}
|
ご覧の通り、Groovy表現は非常に優雅であり、対応するマークアップ表現に、容易に割り当てることができます。もちろんGroovyはこの下で、面倒なマークアップ要素(<や >)を処理しています。ですから私は、構造の詳細に対して注意を払うことなく、内容に集中することができるのです。
HTMLを見せてください
Builderを使うと、HTMLの構築も容易になります。これはGroovletを開発する時に便利です。極端に単純な場合として、リスト3のような単純なHTMLページを作る場合を考えてみましょう。
リスト3. 基本的なHTML
<html>
<head>
<title>Groov'n with Builders</title>
</head>
<body>
<p>Welcome to Builders 101. As you can see this Groovlet is fairly simple.</p>
</body>
</html>
|
これをGroovyでコード化するのは簡単です。これをリスト4に示します。
リスト4. Groovyでの基本的なHTML
import groovy.xml.*
import java.io.*
class HTMLBuilderExample{
static void main(args) {
writer = new StringWriter() builder = new MarkupBuilder(writer)
builder.html(){
head(){
title("Groov'n with Builders"){}
}
body(){
p("Welcome to Builders 101. As you can see " +
"this Groovlet is fairly simple.") }
}
println writer.toString()
}
|
もう少し面白くするために、Builderを使うと完全機能のGUI が簡単に作れることを示しましょう。先ほど、GroovyのSwingBuilderを使うと非常に簡単にGUIが作れることに触れました。SwingBuilderの実際を、リスト5に示します。
リスト5. GroovyによるGUI Builderは素敵だ
import java.awt.FlowLayout
import javax.swing.*
import groovy.swing.SwingBuilder
class SwingExample{
static void main(args) {
swinger = new SwingBuilder()
langs = ["Groovy", "Ruby", "Python", "Pnuts"]
gui = swinger.frame(title:'Swinging with Groovy!', size:[290,100]) {
panel(layout:new FlowLayout()) {
panel(layout:new FlowLayout()) {
for (lang in langs) {
checkBox(text:lang)
}
}
button(text:'Groovy Button', actionPerformed:{ swinger.optionPane(message:'
Indubitably Groovy!').createDialog(null, 'Zen Message').show()
})
button(text:'Groovy Quit', actionPerformed:{ System.exit(0)})
}
}
gui.show()
}
}
|
この結果を図1に示します。悪くありませんよね。
図1. GroovyでのGUIプログラミングの成果
何か本物を見せてください
こうした例は、取るに足らないものですが、面白いものと言えるでしょう。GroovyのBuilderを使うことによって、下にある特定なマークアップ言語、例えばXMLなどを避けられることが、これまでの例でよく分かったと思います。当然のことですが、XMLやHTMLを避けることは、時には望ましいものであり、マークアップ機能は、Java™プラットフォームにとって決して新しいものではありません。例えば、XML機能を持った、私の好きなフレームワークの一つはJiBXです。
JiBXを使うと、XML構造をオブジェクト・モデルに、あるいはその逆に、容易に割り当てることができます。バインディングは強力なものですが、バインディングを行う似たようなツールは、JAXBやCastor、そしてZeusなど、山ほどあります。
バインディング・フレームワークの唯一の問題は、少し時間がかかる可能性がある、という点です。幸いGroovyのBuilderは単純なソリューションとして使うことができ、場合によると、それだけでも非常に効果的です。
Builderによる疑似バインディング
ちょっとここで、英語の単語辞書を表現する単純なデータベースがある、と想像してみてください。そうすると、word(単語)に対するテーブルや、そのdefinition(定義)に対する別のテーブル、そして最後にsynonym(同義語)に対するテーブルがあることになります。図2は、このデータベースを簡単に表現したものです。
図2. 辞書のデータベース
見て分かると思いますが、このデータベースは極めて単純です。wordは、definitionとsynonymとの間に、1対多のリレーションシップを持っています。
この辞書データベースには、このデータベース内容で鍵となる面を表現したXML構造を取り入れようと探っている利用者がいます。検索の対象となるXML構造をリスト6に示します。
リスト6. 取り入れ可能な辞書XML
<words>
<word spelling="glib" partofspeech="adjective">
<defintions>
<defintion>Performed with a natural, offhand ease.</defintion>
<defintion>Marked by ease and fluency of speech or writing
that often suggests or stems from insincerity, superficiality, or deceitfulness</defintion>
</defintions>
<synonyms>
<synonym spelling="artful"/> <synonym spelling="urbane"/> </synonyms>
</word>
</words>
|
この問題に対して、JiBXのようなバインディング・フレームワークを使って対応しようとすると、リレーショナル・モデルから目的とする最終的なXMLモデルを作るまでの間に、恐らく何か中間的なオブジェクト・モデルを作る必要が出てきます。その後でデータベースの内容をオブジェクト・モデルの中に読み込み、オブジェクト・モデルの内部構造をXMLフォーマットに整合するように、下にあるフレームワークに要求する必要があります。
このプロセスでは、(求めるフレームワークの手順を使って)オブジェクト構造をXMLフォーマットに割り当てるという、暗黙的なステップも行われます。JAXBのような一部のフレームワークでは、XMLや、例えばJiBXのような他のフレームワークから、実際にJavaオブジェクトを生成してくれるため、自分のJavaオブジェクトをXMLフォーマットにカスタム割り付けすることができます。ただし、いずれも大仕事です。
確かにそれは、高貴な努力でもあります。私は、バインディング・フレームワークを避けるように訴えているわけではありません。私は一応、それを言っておきたかったのです。皆さんは事前警告を受けました。これから皆さんにお見せするのは、もっと早く、そのXMLを生成する方法です。
手軽に使えるXMLが一瞬で
GroovyのMarkupBuilderと、皆さんお気に入りの新しいデータベース・アクセス・フレームワーク、GroovySqlとを組み合わせると、手軽に使えるXMLを容易に生成することができます。必要なことは、必要なクエリーを見つけ、その結果をBuilderインスタンスに割り当てることだけです。そうすると、辞書データベースの内容を表すXML文書が、一瞬でできあがります。
これをステップ毎に見て行きましょう。まず、Builderのインスタンスを作ります。この場合はXMLを生成しようとしているので、MarkupBuilderです。一番外側のXML要素(つまり、ルート)はwordなので、wordメソッドを作ります。クロージャーの中で最初のクエリーを呼び、イテレーター(iterator)の中にある、そのクエリーの結果を、子ノードであるwordに割り当てます。
次に、wordの2つの子ノードを、2つの新しいクエリーを使って作ります。そのためにdefinitionsオブジェクトを作り、イテレーター内部に割り当て、synonymsに対しても同様のことをします。
リスト7. Builderを使って全てをまとめる
import groovy.sql.Sql
import groovy.xml.MarkupBuilder
import java.io.File
import java.io.StringWriter
class WordsDbReader{
static void main(args) {
sql = Sql.newInstance("jdbc:mysql://localhost/words", "words", "words", "org.gjt.mm.mysql.Driver")
writer = new StringWriter() builder = new MarkupBuilder(writer)
builder.words() {
sql.eachRow("select word_id, spelling, part_of_speech from word"){ row |
builder.word(spelling:row.spelling, partofspeech:row.part_of_speech){
builder.definitions(){
sql.eachRow("select definition from definition where word_id = ${row.word_id}"){ defrow |
builder.definition(defrow.definition)
}
}
builder.synonyms(){
sql.eachRow("select spelling from synonym where word_id = ${row.word_id}"){ synrow |
builder.synonym(synrow.spelling)
} }
}
}
}
new File("dbouuput.xml").withPrintWriter{ pwriter |
pwriter.println writer.toString()
}
}
}
|
まとめ
ここで示したバインディング・ソリューションは、特に純粋Java主義者の観点から見ると、申し訳ないほど単純です。このソリューションは、JABXやJiBXのようなバインディング・フレームワークでできることに比べて、より良いわけではありませんが、より素早く行うことができ、少し容易であることを私としては強調したいと思います。同じようなことが、単純なJavaコードでできるでしょうか。確かにできますが、途中のどこかで必ずXMLをいじる羽目になります。
GroovyのBuilderを使って開発することによるスピードと簡潔さは、マークアップが必要な状況では便利なはずです。例えば2番目の例で見た通り、データベースを表現するXMLを一瞬で作ることができました。Builderは、プロトタイピングや、最小の時間と努力で実働ソリューションを作らなければならない場合にも、素晴らしい選択肢となります。
次回の実用的なGroovyでは何を取り上げましょう? もちろん、なぜJava言語内部でGroovyを使うのか、についてです。
参考文献
著者について  | 
|  | Andrew Gloverは合衆国ワシントン特別区にある、Vanward TechnologiesのCTO(最高技術責任者)です。Vanward Technologiesは自動化テスト・フレームワークの構築を専門としており、ソフトウェアのバグ発生数や統合時間やテスト時間の減少、また全体的なコード安定性改善に貢献しています。
|
記事の評価
|