テスト駆動開発の進化

デブサミ関西2012での講演内容まとめ

はじめに

今月、GOOS日本語版が発売されました。

実践テスト駆動開発 (Object Oriented SELECTION)

実践テスト駆動開発 (Object Oriented SELECTION)

継続的デリバリーに続き、高木さんと一緒にお仕事をするのはこれで二冊目です。今回も多くの人に助けられて、目標としていたデブサミ関西での出版にこぎつけることができました。関係者の皆さま、どうもありがとうございました。


講演では触れませんでしたが、ここで「実践テスト駆動開発」というタイトルの由来について少し書いておきます。原書のタイトルはご存じの通り、"Growing Object-Oriented Software, Guided by Tests"で、前半の頭文字をとってGOOSと呼ばれています。"grow"も"guide"も本書にとって外すことのできないキーワードでしたので*1、タイトルから外すことは考えられませんでした。ただ、これをうまく収めるタイトルがどうしても浮かばなかったため、直訳を副題としました。主題に今の名前を付けたのは、ケント・ベックの著作(Test Driven Development: By Example (Addison-Wesley Signature Series (Beck)))以来、TDDにとっては二冊目のバイブルという位置づけを大切にしようと考えたのです。


講演のタイトルを「テスト駆動開発の進化」としたのは、2002年のケント・ベックの著作と比べてGOOS本はどういう所が変わっているのか、という点にフォーカスしているためです。


さて、今回の講演のアジェンダです。

  • テスト駆動開発の進化とは?
  • GOOSのポイント
    • 進化という観点だけではとらえきれないGOOSのポイントについてご紹介します。
  • 開発の現場で活かすには?
    • GOOSで紹介されるテスト駆動開発を実際のソフトウェア開発のライフサイクルに組み込むにはどうすればいいかを考えます。


それでは内容に入っていきましょう*2。当日使ったスライドはこちらです。

テスト駆動開発の進化

そもそもテスト駆動開発とは、プロダクションコードに先立ってテストコードを書く(テストファースト)ことで、コードを動かしながら完成に近づけていくという手法です。まず失敗するテストを書き(レッド)、そのテストを通るようにし(グリーン)、先に進む前にリファクタリングする、というリズムは、黄金の回転と呼ばれたりもします(詳しくは(こちら)。


具体的にFizzBuzz*3を使って例を示します。TDDでは、プロダクションコードを書く前に、まずはテストコードを書きます*4

   @Test
    public void when1then1() throws Exception {
        FizzBuzz ex = new FizzBuzz();
        String result = ex.result(1);
        assertEquals("1", result);
    }

シンプルなテストですが、これだけでも多くの判断をしています。

  • クラス名はFizzBuzzにしよう
  • メソッド名はresultにしよう(これでいいの?)
  • 戻り値は文字列にしよう("fizz"と"buzz"があるので)

この状態では実コードがありませんのでコンパイルも通りません。コンパイルを通しながら、最もシンプルな仮実装をするとこうなります。

public class FizzBuzz {
    public String result(int i) {
        return "1";
    }   
}

かならずしもreturn "1";である必要ななく、return Integer.toString(i);でも構いません。どこまで先に進んでいいかということについてTDDでは特に何も定めていないのです。重要なのは、「何をテストすべきか」という自分の理解を深めながら先に進むことです。個人的には、まずはできる限りシンプルなテストと実装(この場合であれば「1を渡したら"1"が戻る」)でインターフェイスを定め、その後で仕様に関するテストを計画的に書いていくのが良いのではないかと思っています。


さて、テストがグリーンになったところでリファクタリングです。resultというメソッド名がなんとなく気になりますが、今は先に進みましょうか。またテストを書いてレッドにします。インターフェイスが定まったので、次は仕様のテストですね。今回は、数字のテストを置いておいて先に"fizz"をやりました。

    @Test
    public void 引数が3だったらFizz() throws Exception {
        FizzBuzz fizzBuzz = new FizzBuzz();
        assertEquals("fizz",  fizzBuzz.result(3));
    }

これを通すためにコードはこうなります。

    public String result(int i) {
        if(i % 3 == 0) {
            return "fizz";
        }
        return "1";
    }

これでふたつめもグリーンです。中身もとりあえず良さそうですね。こうして、5の倍数だったら、公倍数だったら、数字だったら、とテストを書いていくと、動かしながら完成品が出来上がります。テストがあるのでリファクタリングを安心してできるところも、TDDのいいところですね。


ここまで読んで頂いておわかりの通り、TDDは何かひとつのクラスを実装するには非常に優れた手法です。しかし、普段我々が携わっているようなアプリケーション(私の場合はWebアプリケーションになります)では、どこから手を付けて良いのかわからなくなってしまいます。こうした疑問に対して、スティーブとナットはやはりテスト駆動開発の黄金律を使って立ち向かいます。その黄金律とはすなわち、まずは失敗するテストを書くというものです。ただし、そこで彼らが最初に書くテストは、「受け入れテスト」です。受け入れテストでテストするのは、システムが実装すべきフィーチャ、顧客に対して提供すべき価値ということになります。そしてこの受け入れテストはエンドツーエンドに実施しなければいけません。エンドツーエンドとは、システムの「端から端まで」、たとえば、ユーザーインターフェイスからデータベースまでということです。


この受け入れテストから書き始めるTDDは次に示すような二重のループを描くことになります。


外側のテストは実装すべき機能がどこまでできているかを示す進捗の指標となり、内側のテストによってコードの質が高く保たれます。この外側のループと内側のループを繰り返し辿ることで、ソフトウェアが作られていきます。本講演のタイトルにしたテスト駆動開発の進化とはすなわち、コードを書くことから、ソフトウェアを作ることへということなのです。

GOOSのポイント

さて、前述した二重のループに加え、GOOSにはいくつか特筆すべきポイントがあります。ここではそれを二つとりあげます。それが、モックウォーキングスケルトです。

モックについて

ケント・ベックJUnitの作者であるように、スティーブとナットはjMockの作者です。jMockは二人のオブジェクト指向観をベースに作られています。彼らはオブジェクト指向システムを、「協力しあうオブジェクトの網の目」ととらえます。クラスの継承関係よりも、オブジェクトがどのようにコミュニケーションし合うかが大切だと考える彼らにとっては、クラスよりもインターフェイスが重要です。


さて、そのような発想に従い、網の目としてオブジェクトが構成されていると、テストをするのが難しくなってしまいます。あるオブジェクトをテストしようと思うと、隣り合う他のオブジェクトも動いてしまうことになるからです。


そこで登場するのがモックです。モックを使うと、隣り合う他のオブジェクトをニセモノと差し替え、テストの際に思い通りの動きをさせることができます。この話は具体的な方がわかりやすいので、具体例として「モックによるインターフェイスの発見 - Digital Romanticism」をご紹介しました。興味のある方はご一読くださいませ。なお、前のブログの方には書いていませんので、テストコードの表現がシーケンス図と同じになるという点をここで強調しておきたいと思います*5


キーに対して値をロードして返すシーケンス図がこちら:


実装がこちらです

@Test
public void キャッシュされていないオブジェクトはロードする() throws Exception {
 
    final ObjectLoader mockLoader = context.mock(ObjectLoader.class);
 
    context.checking(new Expectations() {
        {
            oneOf(mockLoader).load("KEY");
            will(returnValue("VALUE"));
        }
    });
 
    TimedCache cache = new TimedCache(mockLoader);
    assertThat((String) cache.lookup("KEY"), is("VALUE"));
}


例に示した仕様ではオブジェクトローダーが事前に存在することが想定されていましたが、実際にテストを設計しながらインターフェイスが発見されるということも十分に起こります。受け入れテストからはじめて、外側から内側へ、インターフェイスを発見しながら開発を進めていく点がモックを使ったTDDの特徴になります。モックを使ったテストコードを書くことは設計の活動ですので、テストファーストとはいえコードを書くことにこだわりすぎず、色々な図を使いながら考えて進めていくのが良いのではないかと思います。


そのようにインターフェイス(あるいはオブジェクト)を発見するためのパターンがGOOSには三つ出てきます。

分解(Breaking out)
あるオブジェクトが責務を持ちすぎている場合、ふるまいの凝集した単位を分割するというものです。
発芽(Budding off)
新しい概念が登場したときにプレースホルダー型をラップする(値)もしくは、サービスを定義する(オブジェクト)というものです。分解は既にあるものを分けるのに対して、発芽は新しい概念が導入される点が特徴です。
包含(Bundling up)
関連するオブジェクトの集団をひとつのオブジェクトにまとめるというものです*6。上記二つは分けるパターンだったのに対して、これはまとめるパターンです。

これらのうち、分解と包含は基本的な考え方だと思いますが、発芽はきれいなコードが書けるようになるためのいいテクニックだと思います。


なお、もう一つ重要な点として、ビジネスロジックの中核は、ビジネスドメインの言語を使うべきだ、というものがあります。これについて詳しく知りたい方は、ぜひ以下の本をご覧ください。

エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)

エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)

ウォーキングスケルトンについて

GOOS流のTDDでは、フィーチャの開発とエンドツーエンドテスト基盤の開発を並行して進めなければなりません。しかしこれには、テストが失敗したときの原因がどちらにあるかわからないという点を始めとした難しさがあります。ウォーキングスケルトン(Walking Skeleton)とは、これを解決するための方法です*7。まずは、できる限り薄いフィーチャのスライスを取り出し、それをビルド/デプロイ/テストするための基盤を作ります。こうした基盤が一度できれば、あとは少しずつテストを増やし、プロダクションコードを育てていくことができるようになります。詳しくは以下の本をご覧ください。

継続的デリバリー 信頼できるソフトウェアリリースのためのビルド・テスト・デプロイメントの自動化

継続的デリバリー 信頼できるソフトウェアリリースのためのビルド・テスト・デプロイメントの自動化

中間のまとめ

これまでの内容をいったん整理します。まず第一に、GOOS流TDDの核心は受け入れテストとユニットテストにより構成される二重のループにあります。それを円滑にまわすために、まずは薄いスライス(ウォーキングスケルトン)を使ってテスト基盤を作り、フィーチャを増やしていくという形をとることになります。第二に、テスト駆動開発という名前がついてはいますが、ここで言われているテストはシステムのふるまいです。つまり、テストを考えるということは、システムを外側から見たときのふるまいを考えるということに他なりません。こうした考え方については、「BDDの導入 - Dan North - Digital Romanticism」をご参照ください。

開発の現場に活かすために

さて、ここまでGOOSに書かれたTDDの特徴を見てきました。自社システムの開発を数人で行っているチームであれば、ここまでのノウハウで十分にうまくいくと思います(もちろん、スクラムのようなある程度の開発プロセスに関する方法論は必要でしょうが)。しかし、プログラマ30名〜40名で行う受託開発を考えると、なかなかこの形に持ち込むのが難しそうです。最後に、こういった中規模〜大規模の開発現場でGOOSを活かすにはどうすればいいかを考えていきたいと思います。


二重のループをどう開始すればよいか、という問いは、二重のループの外側を見ることでしか解決できません。二重のループに入る前の段階として、GOOSには以下のステップが説明されています。

  1. 問題を理解する
  2. アーキテクチャを定める
  3. ビルド/デプロイ/テストを自動化する


ここで重要なのは1.と2.、特に2.のアーキテクチャです。GOOSが想定しているアーキテクチャは、タイトルにある通り、「オブジェクト指向ソフトウェア」です。しかし、ここで想定しているような中規模〜大規模の開発では、すべてを「オブジェクト指向」で作ることはあまり現実的ではありません。まず重要なのは、業務分析を行った上でドメイン(ここでは「機能の大分類」と読み替えて頂いても構いません)を抽出し、それぞれのドメインごとに特性を分析することです。


ここで分析すべき特性とはすなわち、業務の複雑さをどこで受け止めるべきか、ということです。ここでは二つ分類を出してみます。

  1. エンティティ主体のドメイン
  2. ロール主体のドメイン

エンティティ主体のドメインは、普段多くの方が目にしているものだと思います。つまり、業務の複雑さがデータモデルに吸収され、E-R図とSQLビジネスロジックの中核になるようなドメインです。このようなドメインであれば、ムリにオブジェクト指向的に作る必要はなく、SQLを気持ちよく書くためのSQLテンプレートエンジンを準備した上で、入力チェック、DBアクセス、編集処理といった手続きをトランザクションスクリプトとして実装することが正解となります。このようなドメインに対してO-Rマッパーを当てはめた結果、コード内に大量のJPQLが埋め込まれ結果として可読性が落ちてしまうケースを見たことがある方も多いのではないでしょうか。また、こうしたドメインの場合、テストとして設計するべきは、シナリオではなく、データのパターンになります。ユーザの操作だけで見ると「詳細画面を表示する」という一シナリオで済んでしまうところ、「このフラグがこうなっている場合はこの項目は表示しない」、あるいは「ここがこういう状態の場合にはリレーションのこっちから値を持ってくる」といったパターンが出てくることになります。


一方で、処理が複雑で、ロール間のインタラクションとしてモデル化した方が適切だと思われるドメインも中には存在します。例としては、決済処理や権限といったよくある共通処理、あるいは外貨金利の日またぎ計算といった特殊な業務ロジックが挙げられます。このようなドメインに対してトランザクションスクリプトで立ち向かってしまうと、処理が重複したり、メソッドが長くなりすぎたり、という問題が発生します。またテストの設計も、「こういう条件でこういう入力があったらこのインターフェイスを呼び出す」という形のシナリオになるでしょう。


ここでは二つの分類を出しましたがこれが二つである必然性はありません。各ドメインを記述するのに適切なパラダイムは他にもあり得ると思います。重要なのは、作るものの特性を見極め、それに合わせたアーキテクチャを策定しようということです。GOOSではソースコードは「育てる」ものだとされていますが、そのソースコードが育つ方向性を事前に規定するのがアーキテクチャなのです。

*1:詳しくは本書の序文を読んでみてください

*2:内容については、一部後から追記しています

*3:wikipediaFizzBuzzを参照

*4:このコードは行きの新幹線の中で @yusuke_arclamp とペアプロをしました。ご協力ありがとうございました。

*5:これは行きの新幹線の中で、上司の@yusuke_arclampから指摘されて気付きました。静的な構造を表現するクラス図より、動的な動きを表現するシーケンス図の方が、モックとの相性が良いのです

*6:日本語版p.64で包*括*となっていますが、ミスによる表記揺れです。申し訳ありません。

*7:直訳すれば歩くスケルトンで、骨格だけで筋肉のないガイコツが動いている、というイメージです。継続的デリバリーおよびGOOSでは、意味を取って動くスケルトン、としています

静と動の往還としてのモデリング

DCIを参照しつつ「業務分析」について考える

はじめに

最近「アジャイル」という言葉をなるべく使わないようにしています。なぜなら、この言葉に込められた「桃源郷へのあこがれ」が、色々なものを見えなくしてしまうような気がするから*1。例を挙げましょう。アジャイルの基本は、「タイムボクシングによるインクリメンタルかつイテレーティブな開発」であると言え、それを実現するために流派によって様々なテクニックが提示されます。Scrumを見てみましょう。Scrumを実現するために絶対的に必要なのは、プロダクトオーナーによって適切に優先順位付けされたプロダクトバックログなわけですが、網羅性と整合性を保証したかたちでバックログアイテムを優先度順に並べるって、実はものすごく難しいことを言っていませんか? 確かにタイムボクシングによる軌道修正がある程度できるとは言え、「優先順位の低いところは粒度が粗くてもいいよ」で先々問題が起きないとは、私にはどうしても思えないのです。どんなにプロダクトオーナーが先を読んでいても、どれほどチームが優秀でも、プロダクトオーナーとチームとの間で最終的なアーキテクチャが共有されていない限り、コードが先々の変化に耐えられるかたちになっているとはどうしても思えない。


逆に言えば、網羅性と整合性が保証されたかたちでプロダクトバックログなり機能一覧なりが整備されていて、それを実現するためのアーキテクチャが定まっているのであれば、「それは、もう真っ当にウォーターフォールをやってもうまくいくのでは?」という気もします。しかも、継続的デリバリーのようなテクニックをウォーターフォールでは使えないかというと、それはまた別の話。


こうして見ると、アジャイルウォーターフォールかという区別は「アップフロントでの作業云々」ではなく、「何を固定するか」という話と社内事情とを照らし合わせたときに出てくる解ととらえるとすっきりします(これもよく言われることではありますが)。つまり、自社メンバだけで開発するという前提ならリソース固定(アジャイル)にならざるを得ないし、「どうしてもこの日までにこの機能を」というのがお客様のビジネスにとって必要なら、リソースのスケールアウト戦略(ウォーターフォール)を考える必要が出てくるでしょうし。


前置きが長くなりましたが、こうしたことを踏まえて、いずれにせよ必要になるであろう「アップフロントな分析・設計作業」が本エントリの主題です。

ソフトウェアをデザインする

業務を分析してソフトウェアを設計する作業は、抽象度をコントロールしながらシステムのモデルを作り上げていく作業です。どういうものを書かなければいけないかは、教科書を見ればいくらでも出てきます。業務フロー、機能一覧、ER図、画面遷移図、入力チェック仕様、データアクセス仕様、クラス図、シーケンス図 etcetc・・・。このとき重要なのは、こうした複数のモデルが、1つのシステムの別々の側面を記述しているものであること。つまり、モデル1つ1つが、明確につながっていなければならないということです。このつながりが見えているかどうかが、設計の整合性と網羅性に決定的な影響を与えます。


このつながりを考える上で、大きなヒントを与えてくれるのがDCIアーキテクチャです。

システムが「どのようなものか」/「何をするのか」

DCIの根底にある考え方を簡単におさらいします。DCIでは、人間がシステムをとらえる際には「システムがどのようなものであるか」と「システムが何をするか」の2つから考えているとします。その上で、オブジェクト指向のことを、後者を表現することには長けているが、前者をうまく表現することはできないと批判します。さらに、「人間のメンタルモデルを表現する」というオブジェクト指向の理念に従い、この「システムが何をするか」と「システムがどのようなものであるか」という異なる位相で重なり合う2つの軸を表現するために、ロールという概念が導入され、実装のためにScalaのTraitが提示されます(詳しくはこちら)。


これを実装の話としてだけとらえてしまうと、実務で使うにはちょっと怖い感覚があるのですが、システムを「どのようなものか」と「何をするか」という2つの軸でとらえるという発想は、システムをデザインする上で大きなヒントを与えてくれます。この軸は「構造とふるまい」であり、「静と動」であり、「空間と時間」でもあるのです。

静的なモデルと動的なモデル

業務分析の入り口は、ユーザーの動きです。システム以前に、ユーザーが何をするのかがまず問題になるということですね。これを表現するためのテンプレートとしては、業務フローやユースケース、あるいはユーザーストーリーなど、そのコンテキストに応じて適切なものを選択すればよいですが、表現したいのはユーザーがシステムで何をしたいのか、それを踏まえてシステムは何をしなければならないのか、という動的な側面です。


動的なモデルは時間軸を含むため、単一のモデルで全体を表現することができません。したがって、ソフトウェア全体を俯瞰するためには、つまり「システムがどのようなものか」をとらえるためには、時間軸を無視して全体を一枚に表現する静的なモデルが必要となります(Copeであれば、空間的という表現を使うでしょうか)。それは、ユースケース図かもしれないし、機能構成図かもしれないし、ER図かもしれない。どのかたちに落とすのが適切かは、先行する動的モデルの性質と精度によるでしょう。静的なモデルを引くことにより、先行する動的なモデルの検証も行うことができます(「これがあるということは、こういうこともしないといけないですよね?」)。


こうして出来上がった静的なモデルは、今度は動的なモデルによって検証されることになります。静的なモデルの構成要素が登場するインタラクションを時間軸を導入しつつより具体的なレベルで分析することで、必要な要素が抜けていないか(網羅性)、構成要素に矛盾がないか(整合性)といったことを検討できるのです(「これをやるためには、こういうものも必要ですよね?」)。


このとき重要なのは、「動的」という言葉が時間軸を含んでいるということです。たとえば、「メッセージング」のような概念はそれ自体が動的であるかのように思えますが、単純に2つの構成要素間でメッセージングが行われていることを示しているだけなら、それは静的なモデルです。コンテキストを限定し、時間の流れとともに何が起きるのかを追跡するのが動的なモデルだということです。


こうして、全体像とその動き(静と動)をクロスチェックしながら、抽象的なところから具体的なところに落としていくことで、その作業の結果が設計成果物となっていくわけです。

プログラムをデザインする

こうした静と動の関係は要件定義フェーズだけに限定されるものではなく、成果物の抽象度が詳細レベルまで落ちてきても本質的には変わりません。たとえば、ER図の妥当性は最終的にはSQLを設計する際に検証される、ということです。プログラムの設計の場合にはどうかと考えると、ここにはSteve FreemanとNat Pryceの提唱している「モックを使ったTDD」がうまく当てはまります(「モックを使ったTDD」について詳しくはこちら)。


「この入力に対してはこの出力が返される」という仕様は静的なものです。モックを使ったTDDの場合、実装時にはユニットテストコードを書きながらオブジェクトとコラボレーターとのインタラクション(動的)を規定していきます。テストコードがグリーンになったら、今度はコードを静的な構造としてとらえ直し、コラボレーターとしてのインターフェイスを実装したクラスの配置をリファクタリングします。ここではTDDのサイクルが、静と動との往還と一致しているのです。

まとめ

「システムがやること」と「システムのあり方」あるいは「静/動」という概念を軸に、設計について考えてきました。最後に、ドメイン駆動設計について少し触れておきたいと思います。『エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)』の中で登場するのは、ほとんどがエンティティ同士の関係を示す静的なモデルです。しかし、これらの静的なモデルを作り上げる過程では、シナリオの中でそうしたエンティティがどう動くのかという動的な側面が、顧客との対話の中で考察されていることが読み取れるでしょう。


事実、DDDを行うプロセスとして現在エリックが整備している「モデルを探究するうずまき*2では、動的な要素としてのシナリオと、静的な要素としてのモデルとの往還関係を繰り返しながらモデルが進化していくとされています。


Copeが「アーキテクチャ」という言葉を使うときには、システムをとらえるためのこうした大きな枠組みが含まれています*3。こうしたアーキテクチャに対する思考は、アジャイルであれ、ウォーターフォールであれ、ある程度はアップフロントに行うべきものだと思うのです。

*1:にも似たようなことは書いた気がしますが

*2:以前、こちらのエントリで少し整理しました

*3:Alexanderの思想を受け継いでいる彼の場合、実際にはそれをも超えて、人とシステムとのかかわり全体(バリューストリームと呼ばれるもの)までが視野に入っているのですが

モックによるインターフェイスの発見

設計ツールとしてのモックの使い方について考える。

導入

先日、"Mock Roles, not Objects"の日本語版「ロールをモックせよ」を公開しました。この論文は2004年に書かれたもので、著者はSteve Freeman氏、Nat Pryce氏、Tim Mackinnon氏、Joe Walnes氏という豪華メンバーです。また、Steve Freeman氏とNat Pryce氏は『Growing Object-Oriented Software, Guided by Tests (Addison-Wesley Signature Series (Beck))』(いわゆるGOOS)の著者でもあり、"Mock Roles, not Object"で語られている思想はGOOSのベースになっているとも言えます。


今回は、この"Mock Roles, not Objects"(以下、MRnO)で語られているモックの基本的な考え方について、GOOSを意識しつつ掘り下げてみたいと思います。

設計手法としてのモック

まずは"MRnO"で紹介されているサンプルを元に具体的なコードを見ていきましょう(なお、このエントリで紹介するサンプルは"MRnO"を元にjMock2に移植したものです)。実装する仕様は以下の通りです:

オブジェクトをロードするフレームワークに対してキーを元にした検索を行い、その結果をキャッシュするコンポーネントを考えてほしい。ロードされてから一定時間が経つと、そのインスタンスは使えなくなる。そこで、時々リロードをしなければならなくなる。
Mock Roles, not Objects(p.7)

最初はシンプルな正常ケースを書きます。

@Test
public void キャッシュされていないオブジェクトはロードする() throws Exception {
 
    final ObjectLoader mockLoader = context.mock(ObjectLoader.class);
 
    context.checking(new Expectations() {
        {
            oneOf(mockLoader).load("KEY1");
            will(returnValue("VALUE1"));
        }
    });
 
    TimedCache cache = new TimedCache(mockLoader);
    assertThat((String) cache.lookup("KEY1"), is("VALUE1"));
}

「cache.lookup()のタイミングで、TimedCacheのコンストラクタに渡されているmockLoaderのloadが呼び出され、その際には"VALUE1"が返されるのでlookupの戻り値も"VALUE1"になる」というからくりです。重要なことですが、この時点ですでにObjectLoaderというコラボレーター(隣接オブジェクト)が発見されています。図示するとこんな感じ:

1つ補足しておくと、ここでObjectLoaderをモック化しているのは、それが外部リソース(DBなど)にアクセスしているっぽかったり、サードパーティーライブラリを想定していたりするからではありません。テスト対象のオブジェクトが動作する上で必要なコラボレーターを、テストを書きながら発見することがモックを使ったTDDの中核です。


この後、2回目に呼ばれたときにはObjectLoaderを呼び出さずにキャッシュを使うテストも当然書かなければいけないのですが、アサート文が2つになるだけですので省略します。キャッシュを実装した段階でコードはこうなっています:

public class TimedCache {
 
    final private ObjectLoader loader;
    final private Map<String, Object> cachedValues = new HashMap<String, Object>();
 
    public TimedCache(ObjectLoader loader) {
        this.loader = loader;
    }
 
    public Object lookup(String key) {
        if (!cachedValues.containsKey(key)) {
            cachedValues.put(key, loader.load(key));
        }
        return cachedValues.get(key);
    }
}

さて、ここで時間の概念を導入しましょう。仕様書にあるこの一文ですね。「ロードされてから一定時間が経つと、そのインスタンスは使えなくなる。そこで、時々リロードをしなければならなくなる。」これに対応すると、テストはこうなります:

@Test
public void タイムアウト後のキャッシュされたオブジェクトはロードする() throws Exception {

    final Clock mockClock = context.mock(Clock.class);
    final ObjectLoader mockLoader = context.mock(ObjectLoader.class);
    final ReloadPolicy mockPolicy = context.mock(ReloadPolicy.class);

    final Timestamp loadTime = new Timestamp("2011/09/17 00:00:00.000");
    final Timestamp fetchTime = new Timestamp("2011/09/17 00:00:01.000"); // 1秒後
    final Timestamp reloadTime = new Timestamp("2011/09/17 00:00:02.000"); // 2秒後

    context.checking(new Expectations() {
        {
            exactly(3).of(mockClock).getCurrentTime();
            will(onConsecutiveCalls(returnValue(loadTime), returnValue(fetchTime), returnValue(reloadTime)));

            exactly(2).of(mockLoader).load("KEY");
            will(onConsecutiveCalls(returnValue("VALUE"), returnValue("NEW-VALUE")));

            atLeast(1).of(mockPolicy).shouldReload(loadTime, fetchTime);
            will(returnValue(true));
        }
    });

    TimedCache cache = new TimedCache(mockLoader, mockClock, mockPolicy);
    assertThat("ロードされたオブジェクト", (String) cache.lookup("KEY"), is("VALUE"));
    assertThat("キャッシュされたオブジェクト", (String) cache.lookup("KEY"), is("NEW-VALUE"));
}

図に表すとこんな感じ:

モックを使ったTDDのユニットテストが通常のTDDと大きく違う点が、実はここに現れています。通常のTDDの場合、こうしたコラボレーターが登場するのは「レッド・グリーン・リファクタリング」のうち、主に「リファクタリング」のプロセスになります。まずは動くようにした上で、より適切な責務分割を目指すわけですね。


それに対して、モックを使うとコラボレーターは、テストを書いている段階すなわちレッドの手前で登場します。実際に写経してみるとわかるのですが、登場するオブジェクトとそうしたオブジェクト間のインタラクションを相当はっきりイメージできていないと、テストを書くことができません。


この意味で、モックを使ったテスト駆動開発ではテストを書くことが設計することに直結するわけです。ただし、ここで設計されるのはあくまでオブジェクト間のインタラクションであり、クラスではありません。「では、クラスはどうやって設計するのか?」という疑問が当然出てくるわけですが、GOOSを読むと、いったんはインターフェイスの匿名クラスとして実装し、あとから名前のついた適切なクラスを抽出するというスタイルがとられていることがわかります。そうして抽出されたクラスの内部で責務があいまいだと感じた場合には、再びユニットテストを書いてコラボレーターを発見するというプロセスを繰り返すことになります。

まず受け入れテストを書く

これが、このアプローチが「外から内へ」と言われる理由です。外側(=システムへのエントリポイント)に近い場所からユニットテストを書き、インターフェイスを発見しながら内側へと入っていくわけです。すると当然、「最初のテストはどこから書き始めるのか」という疑問が生じると思いますが、それに対する答えは「まず受け入れテストを書く」ということになります。全体を図示するとこんな感じでしょうか:

実際には、受け入れテストをグリーンにする過程で必要に応じてユニットテストを書きつつインターフェイスを発見し、グリーンになった後も責務があいまいなクラスに対してまたユニットテストを書き、という具合にループが2重になっています。一見、受け入れテストの話とユニットテストの話は別々のものであるようにも見えます。しかし、モックを使ったユニットテストが、オブジェクト間のインタラクションという内側にフォーカスしている分、システムが全体として正しく動いているかという外側を明示的に意識する必要があると考えれば、両者をまとめて1つのプロセスとする考え方は筋が通っていると言えるでしょう。「受け入れテスト+モックを使ったユニットテスト」をセットで考えれば、ユニットテストを書くという行為自体が従来のリファクタリングフェーズに当たると考えることができるかもしれません。


なお、受け入れテストから始めるというこの考え方は、ふるまい駆動開発(BDD:behaviour-driven development)とも強い親和性があります。すなわち、「システムが何をすべきかをテストで表現する」ことにより、どこから始めればよいか、どうすればそのフィーチャの実装が完了したと言えるのかという問いに同時に答えることができるのです。

最後に

プログラマにとって、コーディングをしながら構造を発見していくプロセスというのはかなりエキサイティングに感じられるものですが、現実問題として本当に何もないところからこれを始めるのはいつになったら終わるのか全然わからないという怖さがあります。したがって、ある程度は事前に分析/設計を行い、大まかな構成のメドを立てておく必要があるということになります。よく批判される"Big Design Up Front"を避けつつも、事前の分析/設計を適切に行うバランス感覚が重要だということですね(それが簡単にできれば苦労はしないという話ですが)。


モックに興味を持たれた方は、ぜひ「ロールをモックせよ」を読んでみてください。ここで取り上げたこと以外にも重要な示唆が色々と書かれています。また、サンプルコードはこちらで公開しています。

SIを仕事にするということ

パラダイムを学ぶことと、実際にデリバリーすることとのバランスについて。あるいは転職報告。

導入

8月1日にグロースエクスパートナーズ株式会社に入社しました。人生で2回目の転職となります。入社してまもなく一ヶ月が経とうとしていますので、本日はその報告を。ブログ翻訳プレゼンに続く舞台裏記事の第4段ですね。私とは違う物事のとらえ方をする方々も多くいらっしゃることは重々承知しておりますし、それを批判するものではないこともあらかじめご了承ください。

転職をした理由

私が7月まで勤めていたのは、いわゆる「ITゼネコン」と呼ばれる元請けSIerでした。開発の実務は協力会社さんにお任せしつつ、自分はメールと打ち合わせに埋もれる日々を送っていたわけです。要件定義から保守まで一通り経験できたという意味で学ぶこともありましたし、アーキテクチャ策定やデータモデル設計のようなことも隙を見てやっていたことは事実ですが、ソフトウェアを作るということと自分との間に、どうしても超えられない壁が立ちはだかっているような感覚が拭えませんでした。これを読む多くの方がいろいろな立場で普段職場で目にしているであろう「アレ」と言えば、大体おわかり頂けるでしょう。


それと平行して、DDDやDCIといったソフトウェアのパラダイムを学びつつ、それをアウトプットする作業もここ数年で実を結び始めていました。DDD日本語版の翻訳は自分にとっての1つの大きな成果ですし、DevLOVEをはじめとしたコミュニティでも話をする機会を頂けるようになりました。


要するに「仕事は日々の糧を得るための手段として割り切り、空いた時間で勉強をしつつ、コミュニティに対してアウトプットしていく」という日々になっていたのですが、やはり自分の中にある矛盾が少しずつ大きくなっていきました。

エリックとの約束

4月にDDDの著者であるエリックが来日し、かなりいろいろなことを話すことができました*1。エリックと会うまでは、DDDを理論書としてとらえていたのですが、本人と会って話したことでずいぶんと考えが変わりました。別れる日の夜にエリックに「これまでは翻訳者として批評家としてDDDを日本に紹介してきたけれど、これからはそれを実践していきたい」と伝えたところ、エリックの答えは、"Right thing to do, and you SHOULD do"でした。

野村さんとの会話

その後、ジュンク堂様にてフューチャーセンターファシリテータの野村さんとトークセッションを開催する機会を頂きました。事前準備のときに、「ユーザとのコミュニケーションの中で業務のとらえ方を理解し、ソフトウェアを作っていく*2」ということを話した後で、野村さんから「そういう開発をしてほしいという人がいた場合、どこにいけばいいですか?」と訊かれました。おそらくすごく素朴な質問だったと思うのですが、それに対して「僕に紹介してください」と言えなかったことが、本当に恥ずかしかったことを覚えています。


結局のところ、組織に守られながらその組織に対して文句を言い、自分という人間を認めてくれる人たちの中で夢を語るのは、ものすごく居心地がよく、楽なことでした*3。でもそれは、中学生が小遣いをもらいながら自分の母親をけなし、仲間内でギターを弾いて悦に入っているのと変わらない。「じゃあそれ、路上で、知らない人の前で胸を張って弾けるの?それで金を稼げるの?


それをやろうと思ったときに目の前に立ちはだかるのは、「生々しい現実」です。要件定義はやったことがあるし、設計も保守もやったことがある。アジャイルや設計手法の本も割と読んでいる。でも、「自分でリードしながら開発をやり切ることができるのか?」と問われたときに、胸を張ってできると言う自信はありませんでした。ユースケース記述のフォーマットを知っていることと、実際にユーザーさんとやり取りをしながら実装可能な設計に落とし込むこととの間には、「ボクシングジムに通うこととリングに立つこと」程度の開きがあります。私にはまだ、師が必要なんですね。

今の会社を選んだ理由

少し前後するのですが、転職自体はDDDを翻訳する前から考えていました。というより「DDD翻訳の仕事が入ってそれどころではなくなった」という表現が正しいですね。今のIT業界では「SIerからサービスを提供する企業へ」という人材の流れが(少なくとも私の周りでは)起きているように感じていますので、それについても少し触れておきます。


私も「実装技術」のようなものにもっと焦点を当てて力を伸ばしたいと思った時期もあり、非SIerへの転職を目指そうと思ったこともありました。しかし、私には「専門的で特殊なアルゴリズムを実装するスキル」はありません。それを育てたいとも思ったのですが、あまりそこをゴリゴリやっている自分の姿をリアルにイメージできなかったのは事実です。おそらく私には「漠然とした物事を言語化する」「物事を抽象的にとらえて、論理的に切り分ける」という作業が似合っているのでしょう。そういう経緯とDDDからの流れがシンクロして、「バリューストリームの中で自分の手を動かしつつ」「企業をお客様として業務に使うアプリケーションを作る」ためには何をしたらいいかを考えるようになりました。


一時は自分で会社を作ることも考えたのですが、尊敬する友人*4から何冊か本をプレゼントして頂き、それを読んだ結果、踏みとどまることにしました。一方で、これができる会社を探そうと思うと、それなりに大変だと思うのですが*5、私の場合は、幸運にもQCon Tokyoをきっかけとしたゆうすけさんとの出会いがあったおかげで、「探す苦労」をせずにすみました。本当にいろいろな方に助けられて今の自分があると思います。


GxPは「DDDでの開発」や「アジャイル開発」を必ずしも前面に押し出している企業ではありません。むしろ「ソフトウェアを作る」ということを、ものすごく真っ当にやっている会社です。手法としてのDDD、手法としてのアジャイルが強調されることはなくても、DDD的モデリングやチケット駆動型の開発がごく当たり前のものとして普通に行われています。これはとても健全で、正しいことだと思うのです。

入社してからの生活

入社してからの4週間でやったことを挙げると、こんな感じです。

  • 提案書を書く
  • ソフトウェアアーキテクチャを設計する
  • タスクスケジュールを引く
  • コードを修正して、テスト計画を書いて、デプロイして、テストする
  • Jenkinsを立てて、自動テストツールと戯れる
  • デプロイを簡単にするためのシェルを書く
  • スレート端末と戯れる
  • JSONプロトコルのRESTっぽいAPIをアプリに追加する
  • Groovyで移行スクリプトを書く
  • Lucene in Actionを読み返す


見て頂いてわかるとおり、上流/下流という区別なく、割と何でもやっている感じです。でも、「華々しいこと」は特にやっていません。スタンドアップミーティングもやってないし(朝会はそろそろやる)、顧客の前でドメインモデルを書いたりもしていない。壁にバーンダウンチャートが貼ってあるわけでもない。テストコードは必要に応じて書きますが、ペアプロやいわゆるTDDも特にやっていません(これは声をかければできるかも)。提案書はパワポで書いていますし、表を作るときにはExcelも使います。機能一覧とかテストケースもExcelで書いています。要するに「アジャイル」と「ウォーターフォール」という区別にこだわらず、必要なことを当たり前にやっているだけです。たぶん、「アジャイル」という言葉にこだわるあまりにウォーターフォール的なものを忌避しすぎると、不幸なことになると思うんですよね。


一番大きな変化は、仕事に関してグチや言い訳を言わなくなったことでしょうか。上司は話がわかるし、仕事ができる方々に囲まれている。ミスしていないわけではないし、見落としをリカバリーするために会社に泊まったりもしましたが、それも自分で勝手にやったことです。すべてに自分の手が届くこの状況下では、何かあれば完全に自分の責任ですよね。

まとめ

ソフトウェアをデリバリーすることには、多くの「泥臭い作業」が伴います。そしてその「泥臭い作業」は本に書かれたり、語られたりすることがあまりない。それはたぶん、語るまでもなく当たり前のことであったり、語れるほどには言語化されていなかったり、語るほど面白くなかったりするせいでしょう。だからといって、「そういうこと」をしなくていいわけではない。このあたり、パラダイムと泥臭い作業をひっくるめた「デリバリー」の全体像が見えずに苦しんでいたわけですが、他にもそういう方はいるのではないかと思います。「バリューストリームの中で自分の手を動かしつつ」「企業をお客様として業務に使うアプリケーションを作る」ことができる場所があるんだということは、多くの方に伝えたいメッセージです。


最後に1つ。コミュニティで出会った人と働くことに、少し怖さはありました。プレゼンをするときにはそれなりの準備をしてデキるコっぽく装うわけですが、仕事となれば完全に生身での勝負ですからね。「がっかりされたらどうしよう」とかは当然思うわけです。でも・・・「一番こわいのはこの痛みなの?痛いのってこわい? あんたいつまでも…大人になってもひとりじゃなんにもできない方がもっとこわいとは思わないの?」ということですよ。


あしたって今さ!

*1:豆蔵の羽生田さんをはじめ、この機会を与えてくださった多くの方々に感謝します

*2:当たり前のこと、というより他にどうやるのか、今となっては謎ですが

*3:誤解が怖いので念のため強調しますが、コミュニティがそういう人の集まりだと言っているわけでは断じてありません。「自分がどう向き合うか」というきわめて個人的な問題です。

*4:最近幸せそうなあの人です。おめでとうございます!

*5:「そんな会社あるんですか?」と何人もの人から聞かれました

書評:アジャイルサムライ―達人開発者への道

オーム社様と監訳者の方々より献本いただきました。厚くお礼申し上げます。ありがとうございます!


アジャイルサムライ−達人開発者への道−

アジャイルサムライ−達人開発者への道−

はじめに

まずは「読者の声」から:

最初は、軽いノリの入門書だろうと高をくくっていた。「マスター・センセイ」だし、そもそも「サムライ」だし。でも、そんなに甘くない。軽い文体とは裏腹に、アジャイルな開発のありかたが、きわめてロジカルかつ網羅的に語られている。ありそうで、なかった本。手元に置いておけば、きっといいことがあるはずだ。
> 和智右桂 『エリック・エヴァンスのドメイン駆動設計』訳者

私はこの本に査読者として関わらせていただきました*1。原文も含めてかなり丁寧に読み込んだという自負はあるのですが、「読者の声」として書かせていただいたのはそのときの率直な印象です。要約すればこの言葉に尽きるのですが、書評としましては、「アジャイルな開発のありかたが、きわめてロジカルかつ網羅的に語られている」この一文をもう少し展開することにします。

アジャイルな開発のありかた

アジャイル」という言葉はずいぶんと色々な誤解に取り巻かれているように思います。端的に言えば「ウォーターフォールに対するアンチテーゼ」という側面に目が行きすぎて、ウォーターフォールのいやなところを解消してくれるもの、というイメージを持っている人が多いのではないでしょうか。「あの大量のドキュメントから解放される」と思っているエンジニア、「いつだって要件を変えていい」と思っている顧客・・・。確かにアジャイルならそういうこともできるかもしれませんが、そのためにやらなければいけないことがあるはずです。


アジャイルな開発のありかた」という表現をしたことには理由があります。この本の中心に据えられているのが、ペアプロテストファーストといったプラクティスに留まらず、そもそものアジャイルの目的であるということを伝えたかったのです。第1章「ざっくりわかるアジャイル開発」を開くと、まず目に飛び込んでくる絵にはこう書かれています。「お客さんにとって価値ある成果を届ける・・・毎週ね!」―動くソフトウェアを顧客に届け、そこからフィードバックを受け取る。そのためにどうすればよいか、ということが語られている本なのです。

ロジカル

文体の軽さと論理的な厳格さは両立し得るものだということを、私はこの本で学びました。「ロジカル」という言葉で表現したかったのは、「構造がしっかりしている」ということです。どの話も、ベースとなっている理念から始まり、それを実現するためにはどうすればよいかが1つ1つ積み上げられて語られています。


これは言い方を変えると、この本を読むことでそれぞれのプラクティスを「なぜやらなければならないのか」がわかるようになるということであり、同時に「実際にどう使えるか」がわかるようになるということです。たとえば、本書のハイライトのひとつである「インセプションデッキ」の説明も、「プロジェクトがだめになってしまうのは、最初の認識が合っていないからだ」という話から始まっています。そして、インセプションデッキをはじめ、リリースボードやバーンダウンチャートについても、実際にどう使うのかということが丁寧に(そして面白く)語られているのです。単純な「書き方」を示したハウツー本とは一線を画していると言っていいでしょう。

網羅的

アジャイル開発が大切にしている価値とは何か」から始まり、チーム作り、認識合わせ、計画作り、要件定義、イテレーションの回し方、さらにはユニットテストリファクタリングといったかなり具体的なプラクティスまで、アジャイル開発を実践する上で必要になるであろうことが一通りこの薄さに収まっています。ではその分表層を触っただけかというと、そんなことはありません。たとえば、ユーザーストーリーとして書き留めておくべき要件の粒度やそれをどう使うかといった話にもきちんと触れられています。


逆にアジャイル開発に関する本を何冊か読んだことがある人であれば、「いまさらこの薄さの本を読む必要はないだろう」と思うかもしれません。しかし、そんなこともありません。インセプションデッキの話は本邦初公開ですし、それ以前にチームメンバーに求められるロールや、運営の手法など、必ず新しい発見があるはずです。


なぜこんなことができるのか正直不思議なのですが、おそらくは「豊富な実践によって蓄積した知識を自分の言葉で語っているだけ」ということなのでしょう。「恐ろしく頭のいい人」それが私が著者に対して抱いているイメージです。

最後に

翻訳書として見たときに、本書の素晴らしいところをあとふたつ挙げておきましょう。1つは「訳質」、もう1つは「監訳者あとがき」です。訳質については実際に手にとってご覧いただきたいのですが、翻訳であることを意識させないレベルで完璧な日本語になっています。また、訳語の選択についても練られていて、「期待マネジメント」という少し耳慣れない言葉に対しては、日本語版だけのコラムが付いていたりもします。監訳者あとがきも必読です。本書の内容について整理しつつ、アジャイルの歴史の中にしっかりと本書が位置づけられています。アジャイル開発について学びたい人のための金字塔が打ち立てられたことを、心からお祝いします。「とにかく読んでみてください」が私からのメッセージです。


アジャイル」という言葉を見て、「自分には読む必要がない」と思ってしまう人がいるかもしれませんので、すこし蛇足を加えます(そういう方がこのエントリを読んでいるかどうかは甚だ疑問ですが)。ここで語られているのは、「顧客に価値を届けるための方法」です。たとえ職場がウォーターフォールであっても、Wordで要件定義書を書くのだとしても、この本で語られている内容は必ず役に立ちます。副題にある「達人開発者」とは、「コードを書く人」ではなく、「顧客の願望をかたちにして届けることのできる人」なのですから。


※なお、冒頭にAmazonのリンクを貼りましたが、PDFが必要な方はこちらのオーム社さんのサイトからご購入することをお勧めします。
http://estore.ohmsha.co.jp/titles/978427406856P




アジャイルサムライ−達人開発者への道−

アジャイルサムライ−達人開発者への道−

*1:実は人生初の査読でした

書評:プログラミングGroovy

著者の方々より献本頂きました。厚く御礼申し上げます。ありがとうございます!


プログラミングGROOVY

プログラミングGROOVY

はじめに

まずはじめに、これはGroovyを好きな方々、Groovyのことが気になっている方々にとっての必携書になると思います。素晴らしいお仕事をなさった著者のみなさまに感謝します。


さて、本の紹介をする前に、私のGroovyに対するポジションを明らかにしておきたいと思います。過去の記事を見て頂けばおわかり頂ける通り、私は時々Groovyを触っています。たとえばDSLを書いてみたいとき、DCIアーキテクチャで何か作ってみたいとき、モダンTDDを試したいとき・・・。意図が伝わりやすいGroovyのコードと、動くアプリが一瞬で作れるGrailsは、私にとって実に頼もしいツールです。


「ちょっと待て、Scalaのエントリも書いているじゃないか」そうですね、先日のTDDBC仙台でもScala組だったように、私は時々Scalaも触っています。「どっちなんだ?」私自身はこの2つの使い分けをかなり意図的に行っています。


一言で言えば、ScalaネクスJava、Groovyは軽量Javaです*1Scalaを使うときには、Scalaの持つJavaにないパラダイム、特に関数型のパラダイムを学ぶためという明確な目的があることが多いです*2。逆に、とにかくさらっとコードを書きたいときにはGroovyを選択しています。そういう意味で、動的言語的な破壊力を隅々まで知り尽くしているわけではありません。そういう比較的中立的(?)なGroovyユーザの視点で書かれた紹介だと思って続きをお読みください。

概要

この本の対象読者について、「はじめに」に書かれている言葉を引用します。

本書の対象読者としては、Groovyのことをまだあまりよく知らないJavaプログラマを想定しています。(p.vi)

もうすこし補うと、Javaをそれなりに使い込んでいて、Javaの「面倒なところ」も含めて理解している方のツボにはまるのではないかと思います。


もう1点、この本の読み方ですが、まずは適度に写経しながら全体をさらっと通読してみるのがいいと思います。細かいAPIは表形式でまとめられていますので、必要に応じて後で調べることができます。むしろ頭の中に、「あ、こんなことができるんだ」というインデックスを作ることが重要だと思います。以下、勝手に3部に分けてみました。

準備編

「まだGroovyに触ったことがない」という方は、第1章「Groovyの世界へようこそ」をさらっと読んでみてください。「Javaを置き換えるとまでは言わないけれど、とりあえず使えるところから使ってみなよ、便利だからさ」というGroovyの声が聞こえてくると思います。


第2章「Groovyの利用方法」は開発環境整備です。これから準備される方はここを見ながらどうぞ。なお、IDEとしてIntelliJがイチオシされていますが、初めてGroovyに触るのであればエディタの方をお勧めします。IntelliJは私もぼちぼち触り始めており、使い込めばかなり良さそうだと思っているのですが、ある程度設定しないといけないことはありますし、一度に色々やると混乱しますので・・・。

写経編

準備が整ったら、ぜひ第3章、第4章を写経してみてください。第3章「プログラミング言語Groovy」ではBeanやGString、クロージャ、コレクションなどGroovyの「軽量」度合いがよく伝わると思います。たとえば、Beanであれば余計なものを一切書く必要がありません。

class MyClass {
    String msg
}

これでGetter/Setterが自動生成されます(同書, p.52)。JavaでもIDEを使えば自動生成できますが、リファクタリングでフィールドが移動する場合などを考えると結構メンテナンスが面倒であることや、そもそもの圧倒的な可読性のよさを考えると、やはりこうやって書けるのはうれしいものです。


第4章「Groovyのライブラリ」ではGroovyに準備されているさまざまなライブラリが紹介されます。ここで紹介されているものは、笑いがこみ上げるくらい小気味よいものばかりです。たとえば、こんなXMLを出力する処理を考えてみてください。

<Products>
    <Product type='regular'>
        <Name>Instant Noodle</Name>
        <Price>147</Price>
    </Product>
</Products>

Javaであれば、あの面倒なDOM/SAXプログラミングが頭をよぎるところですが、Groovyならこれだけです(同書, p.130)。

import groovy.xml.MarkupBuilder

def xml = new MarkupBuilder()

xml.Products() {
    Product(type:'regular'){
        Name('Instant Noodle')
        Price(147)
    }
}

すごいですよね。動的言語である強みを存分に活かした設計と言えるでしょう。


また、第4章はデータベース処理についても、それなりの紙面が割かれているのが大きな特徴だと思います。実際にエンタープライズで使おうと思ったときに必要なものはきちんと押さえてくれているということですね。

応用編

この後はすこし進んだ話題になります。第5章「進んだ話題」で紹介されているものは、すぐには使わないかもしれないものも多いかもしれないですが、適度に写経しながら読み進めると色々参考になります。AST変換で特に目を引くのが、@Singletonや@Immutableというアノテーションですね。こうしたアノテーションを使うことで必要なコードが自動生成されるというのは、Groovyの大きな魅力です。


第6章「Groovyのエコシステム」ではGroovyを取り巻くさまざまなプロダクトが紹介されます。1つ1つは基本的には概略ですが、いくつかのプロダクトにはそのまま動かせるコードもついていますので、手を動かしながら全体像をとらえるのに丁度よいと思います。


第7章「Groovy1.8の新機能」は最新版で追加された機能の紹介ですね。ちなみに、さらっと書いてありますが、Sqlクラスがページングに対応したそうです。これってすごいことじゃないですか?(同書, p.274)

sql.eachRow('select * from EMP', 101, 20) { row ->
    // 各行ごとの処理
}

終わりに

エントリの本文でも書いている通り、この本は電車の中で「読む」のではなく、ある程度時間をとって手を動かしてみるのがよいと思います。新しい考え方が紹介されているのではなく、「これまでやってきたことがこんなに楽にできますよ」ということが豊富なサンプルコードとあわせて説明されているからです。書くコードの量がすくなくてすむGroovyの性質上、ある意味写経のやり甲斐はないかもしれませんが、読み終わる頃にはある程度Groovyのことがわかるようになっているのではないでしょうか。Javaプログラマに対するGroovyの敷居の低さは驚異的です。


実は、Groovyにも黒魔術っぽいところはあります。私自身もそんなに知っているわけではありませんが、たとえば、以前DSLでご紹介したASTの操作は明らかにそちらの世界に一歩踏み出しているでしょう。ただ、この本では、Groovyのそういう部分に対してそれほど触れていません。Groovyの持つ普段の仕事を確実に楽にしてくれるであろう部分が、いわば「謙虚に」まとめられています。この辺りは、「Groovy便利なんだからもっと使えばいいのに」という著者の方々の思いがこもっているように感じられます。


個人的にはGroovyに対して、どう控えめに見てももっと評価されてよいと思っています。この本がきっかけで、Groovyがもっと多くの人に使ってもらえるようになることを祈っています。


プログラミングGROOVY

プログラミングGROOVY

*1:こういうまとめはどなたかがなさっているかもしれませんが、不勉強ゆえよく知らず・・・

*2:先日のTDDBCはちょっと特別ですね。

技術系プレゼンテーションの前にやっておくべきこと

プレゼンテーションの下準備を行う際に、普段心がけていることを整理する。

導入

ブログ翻訳に続く舞台裏シリーズの第3弾として、プレゼン前の下準備の際に自分が心がけていることをまとめておきたいと思います(もう舞台裏ネタはないので、これで最後です)。2010年10月のDevLOVE "Beautiful Development"が、私が個人として行う初めてのパブリックスピーキングでした*1。その時から今まで何度か登壇の機会を頂いていますが、最初のとき以来、事前に必ずやるようにしていることが3つあります。一般的に基本と言われるものがすべからくそうであるように、たいしたことではないのですが、地道にやることでそれなりの効果は得られているような気がします。もちろん、「こうしなければいけない」という話ではありません。「私はこうしています」という話です。参考にして頂ければ幸いです。


内容に入る前に、すこし本のご紹介を。プレゼンテーションのマニュアルとして私が読んだのは次の2冊です。

プレゼンテーションzen

プレゼンテーションzen

  • 作者: Garr Reynolds,ガー・レイノルズ,熊谷小百合
  • 出版社/メーカー: ピアソン桐原
  • 発売日: 2009/09/04
  • メディア: 単行本(ソフトカバー)
  • 購入: 51人 クリック: 927回
  • この商品を含むブログ (186件) を見る
パブリックスピーカーの告白 ―効果的な講演、プレゼンテーション、講義への心構えと話し方

パブリックスピーカーの告白 ―効果的な講演、プレゼンテーション、講義への心構えと話し方

プレゼン資料を見て頂いたことのある方ならおわかりのとおり、私はzen主義者です。ただ、それほど原理的ではなく「写真を使ってメッセージを伝える」ということを除いて、書いてあったことを色々忘れているような気もします。『パブリックスピーカーの告白』もやはりいい本で、これを読んでからプレゼンを技術としてとらえるようになりました。


さて、私が必ず守っている教えは、『zen』から2つ、『告白』から1つもらっています。

メッセージを明確に(プレゼンテーションzen)

プレzenの奥義は「Flickrを使いこなすこと」ではもちろんありません。大切なのは「メッセージを伝えること」です。「メッセージ」という言葉が何か格式張った印象を与えるのであれば、「エレベーターピッチ」と言い換えてもいいでしょう。要するに、聞きに来てくれた方(しかも、仕事帰りだったり、休みの日だったりする貴重な時間を割いて来てくれた方)に対して、何を持って帰ってほしいのか、一言で、ということです。


具体例のためにさらに舞台裏をさらすと、

  • ドメイン駆動設計入門」のメッセージは、「オブジェクトが表現するのは概念である」MVCのMはモデルであって、スクリプトではない」の2つでした。サブテーマとして、これを軸にDDD本を概観し、その後の佐藤さん、増田さんの発表の土台をつくる、というものがありました。
  • 戦略的設計入門」のメッセージは、「戦略的設計とは何かを具体的にイメージしてほしい」というものでした。抽象的な議論が多く、DDD本を読んでいるだけだとイメージが湧きにくいのですが、かなり重要な箇所なのです。これはドメイン駆動設計入門の続編であると同時に、DDD前夜祭陽の巻の第4部編でもありました。
  • Growing Grails Application」のメッセージは、「DDDを実践するために、欠けているものを埋めよう」でした。DDDは設計の話はしてくれますが、意外と具体的なプロセスの話をしてくれておらず、実際現場に適用するには、いくつかのプラクティスと組み合わせる必要があるのです。もちろん、抜き打ちモデリングセッションで交流を、というのも大きなテーマでした。


他にも、「旅行手配ドメイン」とか「Geb+Spock」とか、スパイスっぽいものはあるのですが、メインとなるメッセージをぼかしてしまうような話はかなり削っています。メッセージに焦点を合わせ続けられるようにし、余計なものを削る上では次のプラクティスが助けになります。

発表原稿を作成する(プレゼンテーションzen)

プレzenのいいところは、スライドを眺めているだけでそれなりに楽しいところ。悪いところは、話者がいないと何の話かさっぱりわからないところです。それを補うためにプレzenには「ハンドアウトを配ればいいじゃないか」という素朴なソリューションが提示されます。


とはいえ、「ハンドアウトをプリントアウトするのもそれなりに手間がかかるし、どうしたものか」と考えた結果が、「発表前に書いて、後でブログで公開」でした。やはり原稿を書くとストーリーを前もって組み立てられるので、論理に飛躍があるところとか、脱線しているところに気づくことができます。ただ、読み上げはしていません。ライブ感が失われるような気がするので。じゃあ、原稿も読まずにどうやっているのか、暗記でもしているのか、という話が次のプラクティスにつながります。

リハーサルをする(パブリックスピーカーの告白)

これは目からウロコが落ちました。曰く「うまくなりたいなら、練習しろよ」と。発表原稿を書くのとは前後するケースがありますが、私は最低2回リハーサルをやっています。PCを操作しながら、立って実際に声を出してやっているので、傍から見たら相当奇妙に見えるでしょう。


2回というのは、別に勤勉なわけではありません。1回目は時間が大きくズレたり、話がうまくつながらなかったり、脱線したり、言いよどんだり・・・要するにうまくいかないんですよ。それを発表原稿にフィードバックして、スライドを増減させた上でもう一回やっています。これでうまくいくこともありますが、うまくいかないこともあります。そういう場合は、もう一度。大体3回やれば感じがつかめます。


このときに心がけているのが、「セリフを覚える」のではなく、「論点を整理する」ことです。「このスライドではこれとこれとこれを伝えよう」―こうしておけば、わりと普通に話すことができます。たまにセリフがとんでいることもありますが、本当に伝えるべきメッセージが1つか2つに絞られているので、多少細かいことを言いそびれても、全体のメッセージを大きく外すことはありません。時間も、すこし早めに終わるように事前に調整していれば、手元のタイマーを見ながら話して大体ぴったりに収められます。


ちなみに、タイマーとレーザーポインタのついたリモートコントローラを @t_wadaさんに紹介してもらいました。お値段はちょっとするのですが、それに十分見合う便利なアイテムです。

LOGICOOL プロフェッショナルプレゼンター タイマー機能・LCD搭載 R800

LOGICOOL プロフェッショナルプレゼンター タイマー機能・LCD搭載 R800

最後に

一言で言えば、私のプレゼンは「上演」です。準備してきたものを「演じる」わけですね。だから、準備がしっかりできていれば、当日はそれなりに心に余裕が持てます。実際にプレゼンをしているときに意識しているのは、なるべく聞いてくださっている方の反応を見ること。やっぱり、うなずいてくださったり、笑ってくださったりすると、うれしいじゃないですか。スライド作りとか、リハとか、それなりに準備に時間はかかるんですけど、でも喜んでくださる方の顔を見ると、やってよかったなー、と思います。


一番大事なことですが、プレゼンは一人ではできません。場を準備して登壇させて下さる方がいて、聞いてくださる方がいて、初めて成り立つことなんですよね。とても感謝しています。いつも、どうもありがとうございます!

*1:学会発表は以前にもやったことはありますが、あれはちょっと別ですので。また、2010年のデブサミで、JavaEE勉強会のメンバーとパネルディスカッションをやらせて頂いています。あれは勉強になりました。