実用的なGroovy

マークアップ言語の詳細から離れ、アプリケーションの内容に集中する

Groovy Builderを使うと、マークアップ言語、例えばXMLやHTML、Antタスク、さらにはSwingのようにフレームワークを持つGUIまで、を真似ることができます。これは、素早くプロトタイプを行う際に特に便利です。今回の実用的なGroovyでは、コラムニストのAndrewGloverが、Groovy Builderを取り上げます。手軽に使えるマークアップを瞬時に作りたい場合に、データ・バインディング・フレームワークに代わるものとしてGroovyBuilderがどれほど便利なものか、皆さんも理解できるでしょう。

Andrew Glover, CTO, Stelligent Incorporated

Andrew GloverAndrew Gloverは合衆国ワシントン特別区にある、Vanward TechnologiesのCTO(最高技術責任者)です。Vanward Technologiesは自動化テスト・フレームワークの構築を専門としており、ソフトウェアのバグ発生数や統合時間やテスト時間の減少、また全体的なコード安定性改善に貢献しています。



2005年 4月 12日

数ヶ月前、私が初めて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プログラミングの成果
図1. GroovyでのGUIプログラミングの成果

何か本物を見せてください

こうした例は、取るに足らないものですが、面白いものと言えるでしょう。GroovyのBuilderを使うことによって、下にある特定なマークアップ言語、例えばXMLなどを避けられることが、これまでの例でよく分かったと思います。当然のことですが、XMLやHTMLを避けることは、時には望ましいものであり、マークアップ機能は、Java™プラットフォームにとって決して新しいものではありません。例えば、XML機能を持った、私の好きなフレームワークの一つはJiBXです。

JiBXを使うと、XML構造をオブジェクト・モデルに、あるいはその逆に、容易に割り当てることができます。バインディングは強力なものですが、バインディングを行う似たようなツールは、JAXBやCastor、そしてZeusなど、山ほどあります。

バインディング・フレームワークの唯一の問題は、少し時間がかかる可能性がある、という点です。幸いGroovyのBuilderは単純なソリューションとして使うことができ、場合によると、それだけでも非常に効果的です。

Builderによる疑似バインディング

ちょっとここで、英語の単語辞書を表現する単純なデータベースがある、と想像してみてください。そうすると、word(単語)に対するテーブルや、そのdefinition(定義)に対する別のテーブル、そして最後にsynonym(同義語)に対するテーブルがあることになります。図2は、このデータベースを簡単に表現したものです。

図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を使うのか、についてです。

参考文献

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Java technology, Web development, XML
ArticleID=218878
ArticleTitle=実用的なGroovy
publish-date=04122005