倭マン's BLOG

くだらない日々の日記書いてます。 たまにプログラミング関連の記事書いてます。 書いてます。

FactoryBuilderSupport よ、我に力を! (1) : FactoryBuilderSupport クラス

以前から気になっていた FactoryBuilderSupport をあれこれイジっていきます。

Groovy で独自のビルダーを作る場合 BuilderSupport クラスのサブクラスを作成するのが一般的かと思いますが、ある程度込み入ったものを作りたいときにはちょっと役者不足。 そういう場合にはノードごとにファクトリを対応させてオブジェクトを構築できる FactoryBuilderSupport の出番。 FactoryBuilderSupport クラスはもともと SwingBuilder の実装に使われていたものだそうですが、独自ビルダーの作成に便利なため、汎用性がある部分を抽出してクラスにされたようです。 ちなみに Griffon の UberBuilder にも使用されています。

FactoryBuilderSupport は通常(groovy.util パッケージの) Factory インターフェースや AbstractFactory クラスとともに用いるものですが、それは後日にまわして、今回は FactoryBuilderSupport の簡単な動かし方だけ見ていきます(動作確認 Groovy 1.8.0)。

参考 URL

ビルダーの実装


まずは構築対象の Bean クラス。 いくつか適当にプロパティを定義してあります:

@groovy.transform.ToString
class MathGirl {
    String name
    int age
    String ability
}

これを踏まえて、MathGirl オブジェクトを構築するビルダーを作りましょう。 FactoryBuilderSupport クラスは抽象クラスですが抽象メソッドは定義されていないため、サブクラスを作るために何らかのメソッドを実装しないといけないというわけではありませんが、ノードからオブジェクトを生成する何らかのファクトリ (groovy.util.Factory) を登録する必要があります。 独自のロジックでオブジェクトを生成するファクトリは後日試すことにして、今回は Bean クラスを簡単にインスタンス化できるものを作ります。

class MathGirlBuilder extends FactoryBuilderSupport{

    MathGirlBuilder(){
        // 'mathGirl' というノードに MathGirl クラスの Bean ファクトリを登録
        registerBeanFactory('mathGirl', MathGirl)
    }
}

ここでは 'mathGirl' というノードから MathGirl クラスのオブジェクトを生成するようにしています。 このビルダーを使って、実際にオブジェクトの構築を行ってみましょう。 大体、こんな感じのビルド方法があります:

  1. 基本ビルド
  2. スクリプトからビルド

2番目の方法は build() メソッドを使います。 他に withBuilder() メソッドってのもありますが、まだあんまり使い方理解してないのでスルー*1

基本ビルド


まずは基本的なビルダーの使い方。 手順は

  1. ビルダーのインスタンス取得
  2. ビルダーに対するメソッド呼び出しでオブジェクトを構築

です。

// 1. ビルダーのインスタンス取得
def builder = new MathGirlBuilder()
// 2. ビルダーに対するメソッド呼び出しでオブジェクトを構築
def milka = builder.mathGirl(name:'Milka', age:17, ability:'Mathematics')

assert milka.toString() == 'MathGirl(Milka, 17, Mathematics)'

Bean ファクトリはノードに渡された属性(今の場合 name, age, ability)を自動で Bean オブジェクトに注入してくれます。 ただ、Bean ファクトリはクロージャ(ネストノード)が使えないので、これだけだとあんまり有難味が感じられないけど・・・ まぁお楽しみは次回以降に。

スクリプトからビルド


スクリプトからビルドを行いたい場合、FactoryBuilderSupport クラスの build() メソッドを使います。 このメソッドがとれる引数は

  • Class オブジェクト
  • Script オブジェクト

の2つです。

Class オブジェクトからビルド

まずは Class オブジェクトからビルドを実行する方法。 このクラスは Groovy スクリプトをコンパイルしてできた Script のサブクラスです。 たとえば以下のような Groovy スクリプト(ファイル名 TetoraScript.groovy)

// TetoraScript.groovy
mathGirl(name:'Tetora', age:16, ability:'English')

をコンパイルしてできたクラス TetoraScript があるとすると、

def builder = new MathGirlBuilder()
def tetora = builder.build(TetoraScript.class)
// def tetora = builder.build(TetoraScript) でも OK

assert tetora == 'MathGirl(Tetora, 16, English)'

のようにして、オブジェクトを構築することができます。

Script オブジェクトからビルド

次は Script オブジェクトからビルドを実行する方法。

// Script オブジェクト生成
def shell = new GroovyShell()
def script = shell.parse('''mathGirl(name:'Yuri', age:14, ability:'Logic')''')

// 以下、ビルドの実行
def builder = new MathGirlBuilder()
def yuri = builder.build(script)

assert yuri.toString() == 'MathGirl(yuri, 14, Logic)'

GroovyShell を使って Script オブジェクトを生成してます。 まぁ、改めて書くほどのものでもなかったかな。 次回はファクトリを作る予定。

Groovy for Domain-specific Languages

Groovy for Domain-specific Languages


数学ガール ゲーデルの不完全性定理 (数学ガールシリーズ 3)

数学ガール ゲーデルの不完全性定理 (数学ガールシリーズ 3)

*1:API ドキュメントによると、ビルダーでオブジェクトを構築してる際に、あるノード下の構築を別のビルダーに丸投げするときに使うようですが・・・