2013年12月24日火曜日

JnarioでJavaのTDD

この記事は、TDD Advent Calendar 2013 の第23日目(の一日遅れ)です。
昨年のアドベントカレンダー以来、記事を書いていないという事実に、本人も驚きです。

あなたはJavaでTDDしてる人ですね

そうじゃなくても構いませんが、一応、そういう人が対象です。
または、Javaでテスト気軽に書きたいけど面倒で〜、という方も対象です。
あるいは、Javaでテストとかただの二重苦だろw、という方も対象です。

偉大なJUnit、しかし苦行

Javaでテストと言えば、プログラマに開発者テスト文化やTDDを根付かせた偉大なツール、JUnitがあります。
JUnitの最大の利点は、Javaプログラマがその知識のままテストを書けることですが、それと同時に、Java自体の表現力の弱さも100%享受してしまうのであります。脱初心者後に、JUnitで沢山のテストを書き続けるというのは、ちょっとした苦行でしょう。

Jnarioという選択

TDDの原理は道具と無関係ですが、実践には道具の選択が大切です。
JUnitでテストデビューを飾った後は、他の効率の良い道具を試してみましょう。
GroovyのSpockや、Cucumberを選択するなど、動的型言語由来の製品を使う例も聞きますが、今回おすすめの製品は、Jnarioです。

Jnarioには、以下のような特徴があります。

  • SpecとFeature、両方のテストDSLをサポート
  • Spec/Feature専用のEclipseエディタ
  • SpecとFeatureの両方が、完全なJUnit(Java)コードにコンパイルされる
  • Xtendと同じ、型推論やラムダなどの記述能力を持つ静的型の式言語 (Xbase)
  • テスト失敗時の詳細な値のレポート
  • あわせてJava自体の開発効率向上に使いたい => Xtend

Jnarioのインストール

Jnarioを使うには、Eclipseプラグインを使うのが一番簡単です。
Eclipse Keplerに、Jnarioの更新サイトからプラグインをインストールしましょう。
更新サイト => http://www.jnario.org/updates/releases/

※同時にXtendも入るので、ぜひXtendもご利用ください


Specの作成

Eclipseの新規作成ウィザードで、Specを作ります。

Jnarioのライブラリを取り込むために、Ctrl+1 (Quick Fix)で取り込みます。

Specの記述

テスト対象クラスのSpec内容を記述します。
※1 テストに対応するプロダクションコードは別途作成済みとします
※2 例ではXtendでプロダクションコードを作成しています
※3 プロダクションとテストの両コードに対応するJavaコードが生成されています


Run As -> JUnit Test

Specを実行します。実行するには、Run As -> JUnit Testを行うだけです。

失敗テストの解析

power-assertの様に、実値式の各セグメントの解析内容を出力してくれます。

Featureの作成

Eclipseの新規作成ウィザードで、Featureを作ります。

Featureの記述 (ステップなし)

今回はFeatureとして適切な例ではありませんが、上記のSpecと同様のテスト内容にしてみます。
まずはステップなしの状態で、そのままテスト実行してみます。
テスト実行するには、Specと同様、Run As -> JUnit Testです。

Featureの記述 (ステップあり)

Featureにステップを記述して、テストを実行してみます。

解説

describeとcontextで、簡単に構造化できます

この辺りは常識的ですね。
describeは複数階層記述できます。contextは、最後の階層で使えます。
describeに指定する情報は、説明文字列か、またはクラス名です。
説明文字列は、単にテストの内容説明的なものです。
クラス名を記述した場合は、デフォルトコンストラクタで暗黙のsubjectが生成されます。

factで、簡単にテストブロックを記述できます

factは、テスト構造の中に、何度でも記述できるテストです。
factに与えられるものは、次の3つです。

  • 評価式
    fact  actual => expected (評価式)を書くと、それが直接テストになります。
    評価式はこの形式以外もありますが、一番便利なので最初に覚えてください。
  • ブロック
    fact  {...} の形式で書くと、テスト内容をブロックで記述できます。
    初期化や複数の評価式を組み合わせたい時は、この形で書きます。
  • 説明文とブロック
    fact  "説明文" {...} の形式で書くと、テストブロックは上記と同様に評価されます。
    説明文はそのまま、JUnitのテスト一覧に表示されます。

beforeで、fact共通の初期化などを行えます

ごく普通のことですが、JUnitより簡潔に記述できるのが良いです。

その他のテクニック

その他にも、Jnarioには強力なことを簡単に行う仕組み沢山あります。

  • Example tableによる、簡単なParameterized Test
  • before/afterによる簡単にsetup/teardown (before all/after allもあり)
  • before/after/ユーティリティを共通化できる spec extension 機構
  • JUnitのRule定義も可能
  • 簡単なカスタムアサーション
  • (Feature) 複数シナリオに共通のバックグラウンドシナリオ
  • (Feature) シナリオ毎のbefore/after
  • (Feature) 他シナリオのステップ参照 (import & Given文一致)
詳しくは、Jnarioのサイトを見てみてください。

まとめ

Jnarioを使うと、今日からすぐに、(恐らく) JUnitよりもずっと簡単に、Javaでテスト/TDDをできるようになります。
ちなみに、私が以前やっていたJava開発チームでも、日常的に、XtendとJnarioを使って開発/テストを行っていました。

皆様も、ぜひJnarioのSpecとFeatureをお試しください。


2012年12月9日日曜日

Xtend の Lambda とストリーム処理


この記事は、Java Advent Calendar 2012 の第9日目です。
前の記事は すふぃあ氏(@empressia)の記事 です。
明日の記事は 谷本 心氏(@cero_t) です。

Java に Lambda が来る!

Java 8 に Lambda が入ることはまことに喜ばしい限りですね。でも、使えるのはまだちょっと先のことになりますし、Java 8でホントに入るの?という心配もあります (^_^A;
Java 8 が出ても、すぐには使えない仕事も多いですしね。Java プロジェクトで実際に Lambda を使うのは、しばらく先のことになってしまうかもしれません...。


そこで Xtend です!

Java プロジェクトで Xtend を使えば、今すぐに Lambda を使えます!
Xtend については、以前書いた記事(社内向けのXtend紹介資料) を参考にしてもらうことにして、今日は Xtend の Lambda とストリーム処理のシンプルさについて、ご紹介しようと思います。

Xtend の Lambda の基本

Xtend の Lambda は、Java のそれと同じく、単一メソッドインタフェースに対するシンタクスシュガーです。Xtend の Lambda は、Java と同じイディオムで使用でき、相互運用性もある点で便利です。
ただ、記法は異なります。

従来の Java の以下のような局面では、Lambda を使うことができます。

List<Person> men = ids.map(new Function1<ID, Person>() {
 @Override
 public Person apply(ID id) {
  return Person.get(id);
 }
}).filter(new Function1<Person, Boolean>() {
 @Override
 public Boolean apply(Person p) {
  return p.sex == MALE;
 }
}});

Java 8 の Lambda ならば、以下のように書くでしょう。

List<Person> men = people.map(id -> Person.get(id)).filter(p -> p.sex == MALE)

Xtend では以下のように書けます。

ポイントは、型推論と暗黙引数 it ですね。

val men = ids.map[id| Person::get(id)].filter[p| p.sex == MALE]
val men = ids.map[Person::get(it)].filter[sex == MALE]

Xtend の Lambda は、-> 記号ではなく、[]で囲んだ形になっています。
これは一見妙な感じもしますが、DSL 風の API を Lambda で作るには、結構向いているのです。

Xtend の関数型変数の記述

Xtend で関数型変数を表すシュガーは以下のようになりますが、型推論でより簡単に記述できます。

val (ID)=>Person mapper = [Person::get(it)]
val predicate = [Person p| p.sex == MALE]

Xtend の遅延コレクション評価は Stream 要らず

Java 8 では、遅延コレクション評価のために、Stream が追加されます。
曰く、無限データストリームを処理するのには遅延評価()が必要だが、外部イテレータでは記述が面倒になるので、遅延的な内部イテレータを提供する新しい仲介型が必要だ、ということです。Streamを返すように、既存のコレクション実装を修正すれば、皆が幸せになれると。

Xtend も、遅延コレクション評価の簡潔な記述を提供します。
しかし、Xtend が Java 8 と異なるのは、Stream のような新しい仲介型は導入せず、Iterable/Iterator をそのまま使える点です。
つまり、既存のコレクション(Iterable)も、改造無しにストリーム処理できるのです。

例えば、Xtend で以下のように記述すると、バルク処理ではなく、ストリーム処理になります。このことを少し詳細に見てみましょう。

ids.map[Person::get(it)].filter[sex == MALE].forEach[println(it)]

上記の式のチェーンのつなぎ目の型遷移は、以下のようになっています。

val Iterable<ID> ids = ...
val Iterable<Person> people = ids.map[Person::get(it)]
val Iterable<Person> men = people.filter[sex == MALE]
men.forEach[println(it)]

ここで生じる疑問は2点です。

  1. Iterable に map や filter というメソッドは無いはずでは?
  2. Iterable から Iterable への変換時にバルク処理になっているのでは?


1の答え。それは、Extension method です。

Xtend には Extension method があるので、Iterable に対して外からメソッドを追加したように記述できます。
具体的には、IterableExtensions によって、これらのメソッドが提供されています。


2の答え。それは、IterableからIterableを作るのに評価は不要ということです。

前段の Iterable を、後段の Iterable でラップして行くのです。
最終的な評価は、forEach の中で Iterator 経由で遅延的に行われることになります。これによって、ストリーム処理による遅延評価()が可能になっているわけですね。
ちなみに、この処理の実装として、Xtend の裏で Guava が使われています。

まとめ

Xtend を使うと、今日からすぐに Java プロジェクトに Lambda とストリーム処理を利用できます。

Xtend は、型推論や Extension method の導入によって、生の Java よりもプログラムを大幅に簡潔にしてくれます。
にもかかわらず、Xtend は Java の型システム、Java の思考方法を維持しているので、Java プログラマに大きな学習負担を強いることなく、実装効率を向上してくれます。
Java プログラマなら、1日勉強すると、Xtend をだいたい使いこなすことが出来ます。
Xtend は、Java ソースを生成するので、納品 Java プロジェクトにも使いやすいという特徴があります。

これらの特徴を持つ Xtend は、Java プログラマのための Better Java 言語として、とても魅力的ではないでしょうか。

ちなみに、私のチームでは、今では日常的に Xtend で Java プロジェクトを開発/納品しています。皆様も、ぜひ Xtend の Lambda お試しください。

2012年6月7日木曜日

XtendによるCSV集計サンプルコード

Xtendで、CSVファイルの内容をカラム毎に集計する、サンプルプログラムを書きました。会社の朝Xtend勉強会のお題で、その回答例として書いてみたものです。


以下がポイントでしょうか。
  1. 行コレクションに対する map/reduce を、クロージャで書いている
  2. 行からRecordオブジェクトへの変換を、Extension methodで書いている
  3. Recordの足し込みを、"+"演算子オーバロードで書いている

ありきたりの内容ですが、Javaプログラマ向けの最初の練習問題としては、まぁまぁかな?

2012年1月20日金曜日

社内向けのXtend紹介資料

私の所属する会社には、毎月1回「帰社日」という、社員が集まって色々なセッションを行う日があります。私は今日そこで、Xtendを紹介しました。今後Xtendの機能を紹介するとっかかりにもなりますので、そのスライド資料をここに公開しておこうと思います。

ちなみに他のセッションとして、Xtendの強力なライバルでもあり先生でもある、Groovy紹介セッションもありました。Groovy紹介セッションの後に、私のXtend紹介セッションの流れだったので、むしろ短い時間でXtendの魅力を伝えることができた気もします。

2012年1月19日木曜日

JavaをよりパワフルにするXtend

EclipseのXtextチームが、Xtendという新しいJVM言語処理系をリリースしています。
Javaでソフトウェアを開発している人にとって、Xtendは導入の検討に値する言語処理系です。
沢山の言語やフレームワークがある現在、Javaよりも開発しやすい言語の選択肢は沢山ありますが、にも拘らずJavaを採用しているのには、他の言語を採用できないそれなりの理由があると思います。
Xtendは、Javaのソースを生成する言語処理系であり、Javaを置き換えるものではないため導入の障壁が低く、しかもJavaの多くのものをそのまま利用しているため、学習コストがとても低いというのが、魅力です。

Xtend == Java + α != 新言語
Xtendは独立した新言語というより、Java言語のエクステンションです。Xtendは、Javaプロジェクトの中で、Java言語と一緒に使います。
Xtendは、XtendクラスをJavaクラスのソースに変換します。
Xtendは、Javaのインタフェース、列挙、アノテーションなどは、そのままの形で利用します。

Xtendの考えは、Javaのインタフェースなどの要素は十分簡潔かつパワフルであり、複雑冗長になりがちなクラス実装記述さえ簡潔に置き換えれば、Javaの開発効率は大幅に改善できる、というものです。
このように、XtendはJavaのクラス実装しか置き換えないため、その習得は、新しい言語処理系をまるまる学習するよりも、はるかに容易です。
「Javaクラスの簡潔な書き方を覚える」という、とても軽い気持ちで学ぶことができるんです。

Hello Xtend !
Xtendを始めるのは、とっても簡単です。
Eclipseを用意すれば、以下の要領ですぐに始められます。
  1. Eclipse MarketplaceからXtendをインストールする
  2. Javaプロジェクトを作る
  3. Xtendクラスを新規作成
    srcフォルダにXtendクラスを作ります
    [File]/[New]/[Xtend Class] -> Name: HelloXtend
  4. Xtendライブラリをビルドパスに追加する
    Xtendエディタ上のビルドパスエラーを、Ctrl + 1でQuickFixします
  5. xtend-srcフォルダにJavaソースが生成されていることを確認
  6. Xtendクラスを編集する
  7. Javaソースも生成されていることを確認
  8. HelloXtend.xtendをJavaアプリケーションとして実行
  9. 実行結果を確認

クロージャの例
Hello Xtendの例だけでは、Xtendの魅力が全く伝わらないですよね。
せっかくなので、Xtendのクロージャの例を見てみましょう。
コレクションのフィルタ処理が、簡潔に記述できることが分かると思います。



生成されるJavaソースの例
この時に生成されているJavaソースは以下のような感じです。
Xtendでのクラス実装がJavaソースにコンパイルされていることがわかります。
Xtendのトリックも、ちゃんとJavaコードになっています。


まだまだあるXtendの機能

Xtendには、クロージャの他にも、便利な機能がいくつもあります。
それらの機能が、クラス実装を簡潔にする手助けをしてくれます。
そしてそれらもまた、使うのはとても簡単なんです。
今度は、それらを紹介しようと思っています。

2011年9月18日日曜日

型クラスとEclipse Adapter

まことに恥ずかしながら、ScalaとHaskellの型クラス(Type Class)を初めて知りました。
きっかけは、@kmizuさんのプレゼン「言語アップデート -Scala編-」で、@eed3si9nさんのアーティクル「Scala Implicits: 型クラス、襲来」で学びました。
初めて知った型クラスではありましたが、日頃Eclipseアプリケーションっを作っていて、近いことをやっていることに気づきました。

Eclipse Adapterは型クラスに近い
型クラスを学んですぐに感じたのは、これはEclipseのAdapters Extension Pointを使ってやっていることに近い、ということです。

上記サイトの例から、型クラス導入のモチベーションを整理します。
  1. モデルをLabelMakerインタフェースに適合させたい
  2. モデルをラップする形でアダプターを実装するとアイデンティティ問題が起こる
  3. 如何にモデルのアイデンティティを維持しつつ、インタフェース適合させるか
  4. 型によるアダプタインスタンスの選択を組み込む
このような、インタフェース適合と型によるアダプタ選択は、Eclipse Adapterの仕組みを彷彿とします。

Eclipseにおける型によるインタフェース適合
Eclipseアプリケーションを作っていると、再利用部分と拡張部分を粗結合にするために、アダプターを使用することが多くなります。Eclipseでは、このような時にインタフェースを適合する方法として2種類の方法があります。
  1. モデルがIAdaptableを実装し、getAdapter()がアダプタを返すよう実装する
  2. Adapters Extension Pointを利用する
1のIAdaptableによる方法は、モデルにインタフェースの実装を強要するので、どのようなモデルにでも使える方法ではありません。もっぱら、適合元オブジェクトがWorkbenchPartのような場合に使います。
2のAdapters Extension Pointによる方法は、IAdaptableを実装しないモデルに対してもインタフェースを適合させられます。適合元と適合先の組み合わせによってアダプタを生成するAdapterFactoryを実装して拡張ポイントにコントリビュートします。


Eclipse Adapter使用例
Eclipse Adapterを使ったコード例は以下のようになります。

void printLabel(Address address) {
ILabelProvider provider = (ILabelProvider)
Platform.getAdapterManager().getAdapter(address, ILbelProvider.class);
System.out.println(provider.getText(address));
}

上記のAdapterManagerの仕組みが、型クラスに期待する振る舞いに概ね対応します。Eclipseアプリケーションを作っていて、祖結合のために型クラスの必要性を感じたら、その時はAdaptes Extension Pointを使用すると良いでしょう。
なお、Eclipse Adapterのランタイムの振る舞いはExtension Object Patternここに詳しいです。


Eclipse Adapterと型クラスとの違い
Eclipse AdapterとScalaやHaskellの型クラスとは決定的に異なる部分があります。
  • Eclipse Adapterは実行時の仕組みであり、静的な型保証はできない。
  • Adapterルックアップのために、AdapterManager APIに依存してしまう。
実装言語がJavaであり、Eclipseアーキテクチャがとても古いことを考えると、致し方ないところでしょうか。
それでも、モデルの型によるアダプタの選択により、アイデンティティ問題の回避に期待通りの効果を発揮してくれます。


ともあれ、Eclipseアプリケーションの開発でEclips Adapterの仕組みを理解していたおかげで、ScalaやHaskellの型クラスについても、割とスムーズにイメージできました。
それにしても、HaskellやScalaの表現力はすごいですね。

2011年8月30日火曜日

Asakusa DSLを知り、Xtext適用について夢想する

クラウド温泉2.0@小樽に参加して、Asakusa Frameworkについて、恥ずかしながら初めて知りました。そこで、Asakusaについてちょっと調べてみたことと併せて、Xtext応用の可能性について考えたことをメモします。
なお、Asakusaについての調査は、あくまで斜め読みですので、誤解してる箇所についてはご容赦頂き、指摘して頂けると有難く思います。


言語指向フレームワークとしてのAsakusa
Asakusaは、技術的な面だけでざっくり言うと、Hadoop用のMapReduceコードを生成するフレームワークで、開発者ロールによって異なる3種類のデータフローDSLと、データモデルDSLを持っている、本格的な言語指向フレームワークと理解しました。

  • 現時点で、データフローDSLはJavaをホスト言語とする言語内DSLで、データモデルDSLは専用の外部DSLのようです。このJava DSLは、実行時リフレクション方式ではなく、静的コード生成方式のようです。
  • Javaをホスト言語とする言語内DSLは一見珍しい気もしますが、アノテーションによるメタプログラム(DSL)からAPTでコード生成を行うのは、Javaによる静的メタプログラミング手法の一つであり、Slim3でもそうしたものを伺えます(だからといって真似が簡単だというわけでもないです...)。
  • 一方、データモデルDSLはDMDLスクリプトと呼ぶ外部DSLであり、DMDLコンパイラによってJavaモデルコードを生成するようです。
  • データフローDSLの場合も、データモデルDSLの場合も、結果として静的にJavaコードを生成する原材料で、Hadoop実行時には生成MapReduceコードが走る仕組みのようです。

Javaだけじゃない、ScalaによるDSL定義
ちょっと情報を探してみると、AsakusaデータフローDSLをJava以外の言語でも記述できるようにする計画があるらしく、最初の実装としてScalaが検討されているようです。

Javaの場合もScalaの場合も、汎用プログラミング言語内DSL(Scalaの場合はホスト言語内DSLとは言えない感じなので)ですが、いずれの場合も実行時方式ではなく静的コード生成方式なのは同じようです。
また、このような複数のDSL表現言語から、共通に利用できる内部表現形式を定義する予定もあるようです(もうあるのかもしれません)。
さらに、AsakusaのDSLコンパイラは、データフローを最適化圧縮する仕組みがあるらしく、内部表現形式がDSL定義言語から独立することで、このオプティマイザも再利用できるようになると思われます。
言語指向の王道設計パターンと言えばその通りですが、それがHadoopのような領域で実用となると、とてもすごい応用ですよね。


AsakusaにXtextを適用できるか
さて、このAsakusaのDSL定義として、Xtextを適用できるかどうかを、ぼんやり考えてみたので、ざっとメモとして挙げてみます。なお、設計や実装、ドキュメントに詳細に当たったわけではないので、かなり想像/妄想で考えていることについては、ご了承ください。誤りがある場合は、ご指摘頂けると有難いです。
  • AsakusaのメタプログラムDSLは、実行時リフレクションではなく静的コード生成方式なので、まずはXtextが行けそうな分野です。
  • Asakusaが、汎用言語内DSLを採用しているのは、エディタなどのDSL技術開発生産性や、IDEによる開発との親和性を考えてのことのようなので(出典)、この点はまさにXtextの得意分野と言えると思います。
    ただし、XtextのDSLエディタは、Eclipseプラグインとして生成されるので、DSLエディタ機能がEclipseにロックインされてしまうという、若干の懸念はあります(パーサなどはその限りではないです)。
  • AsakusaのDSL内部表現形式については、現時点では恐らくPOJOとして実装されているとは思いますが、それがEMFモデルとして定義されると完璧にXtextと調和します。
    AsakusaのDSL内部表現形式は、ランタイムでHadoop上で動作するものではなく、Javaコード生成に使われるだけのものだと思うので、EMFのランタイム特性については、気にする必要が無いと思います。
  • EMFで表される内部表現からのJavaコード生成も得意分野だと思います。
    自分はまだ不勉強ですが、XtendとMWEでJavaコードを生成するイメージになると思います。
  • Asakusaのワークフロー最適化については、DSL実装/内部表現形式/オプティマイザが全て独立していることにより、オプティマイザは再利用可能なので、Xtextを適用しても影響ないと思います。
  • ワークフローDSL以外に、DMDLからのJavaモデルコード生成にも、上記と全く同じ考え方で、Xtextを採用できると思います。
    ワークフローDSLは汎用言語内DSLとし、DMDLは外部DSLとしていることで、現状はそれぞれ別なDSL表現技術となっていると思いますが、Xtextを使って両方とも外部DSL方式とすれば、同じDSL表現技術で統一することができます(それが良いことなのかは自明ではありませんけれども...)。
  • DSLエディタやコードジェネレータの実装コストについては、Xtextでかなり削減できるのは間違いないですが、エディタにJDTほどの使い勝手を求めるとなると、(それが偉大であるが故に)結構な手間がかかることが想像できます。この辺については、まだ自分も突き詰めていないので、まだハッキリとはイメージできません。
  • Xtextを使って外部DSLとすることで、汎用言語内DSLでの実装に比べて、構文ノイズを確実に減らすことができます。これは、ユーザにとってメリットだと思いますし、コンサルティング上も有利に働くような気がします。
  • ホスト言語が持つ汎用機能は完全に使えなくなるため、DSLに対する制約の実装は確実に容易になります。この辺りでコストが下がれば、DSLエディタの使い勝手を向上させるコストに回せそうな気がします。

浅い理解
まだまだいろいろな検討事項があるとは思いますが、自分の中に沸き上がった興味については、一通り挙げられたように思います。
ただし上記の検討は、あくまでDSL一般論としてであって、自分はまだHadoop MRの仕組みや、Asakusaワークフローについて本質的に理解していないので、DSLの複雑性を過小に見積もっている可能性が高いです。その結果として、汎用言語内DSL方式からXtextによる外部DSL方式に移行するコストが、ごっそりと過小評価されてしまい、上記の検討が完全に外れた内容になっている可能性もあります。その点については、今後知識をアップデートしていきたいと考えています。


汎用言語内DSL方式に対して、外部DSLを作成するコストを、どれくらい抑えることができるのかは、Xtextにかけられる大きな期待の一つだと思いますし、自分がXtextを今後活用して行く上でもとても気になる部分です。
Asakusaをネタにしていろいろ検討することで、また少し検討が深まった気がします。