No Programming, No Life

プログラミング関連の話題や雑記

Groovyでクラスを静的に動的拡張する方法

はじめに

GroovyではExpandoMetaClassの機能を利用してメタプログラミングが可能です。これにより動的にクラスにメソッドを定義できるため豊かなプログラミングスタイルを提供できます。
しかし、GroovyにはRubyのようなincludeキーワードがないため、拡張部分をモジュール化して扱う機能が弱いと思います。Groovyスクリプトとして書く場合には先頭で拡張を込みでスクリプトを書けばよいが、そうではなくてクラスベースでGroovyを書く場合はどこに書いておけばいいのか迷うところです。
タイトルがまどろっこしいですが、やりたいことは「あるドメインではあるクラスには拡張されたメソッドがある体で動作させたい」ということを静的と表現しています。

サンプル

ということで私が普段利用している方法をひとつご紹介します。とりあえずサンプルで用意したクラスは以下のような感じです。

/root
 +-- Main.groovy
 `--/ext
     +-- StringExt.groovy  <- Stringの拡張をここに定義
     `-- FileExt.groovy    <- Fileの拡張をここに定義

Main.groovy

StringExt.groovy

FileExt.groovy

解説

ポイントとなるのはstaticイニシャライザです。今回のサンプルではMain、StringExt、FileExtの中で定義しています。staticイニシャライザはクラスのロード時に読み込まれるため、まずMain.groovyの中で

static {
  ext.StringExt
  ext.FileExt
}

のようにクラス名のみ記述することによりStringExtとFileExtのそれぞれのクラスがロードされます。ロードされたそれぞれのクラスの中のstaticイニシャライザでmetaClassにより拡張を定義していますので、StringとFileにメソッドが拡張定義されるわけです。これによりRubyで拡張定義されたRubyファイルをincludeしたような効果が得られるようにしています。

リポジトリ

追記 (2011-04-05)

Githubにてリポジトリ管理始めました。
fumokmm/groovy-gdkpp · GitHub

追記 (2011-05-03)

当初はMOP使ってたのですが、@Categoryを使う方針に変更しました。

追記 (2011-05-05)

groovy-extensionsというリポジトリが既にあったため、リポジトリ名を変更しました。