動的言語である強みを存分に活かした設計と言えるでしょう。第4章はデータベース処理についても、それなりの紙面が割かれているのが大きな特徴だと思います。実際にエンタープライズで使おうと思ったときに必要なものはきちんと押さえてくれているということですね。応用編この後はすこし進んだ話題になります。第5章「進んだ話題」で紹介されているものは、すぐには使わないかもしれないものも多いかもしれないですが、適度に写経しながら読み進めると色々参考になります。AST変換で特に目を引くのが、@Singletonや@Immutableというアノテーションですね。したアノテーションを使うことで必要なコードが自動生成されるというのは、Groovyの大きな魅力です。第6章「Groovyのエコシステム」ではGroovyを取り巻くさまざまなプロダクトが紹介されます。1つ1つは基本的には概略ですが、いくつかのプロダクトにはそのまま動かせるコードもついていますので、手を動かしながら全体像をとらえるのに丁度よいと思います。第7章「Groovy1.8の新機能」は最新版で追加された機能の紹介ですね。ちなみに、さらっと書いてありますが、Sqlクラスがページングに対応したそうです。各行ごとの処理}終わりにエントリの本文でも書いている通り、この本は電車の中で「読む」のではなく、ある程度時間をとって手を動かしてみるのがよいと思います。新しい考え方が紹介されているのではなく、「これまでやってきたことがこんなに楽にできますよ」ということが豊富なサンプルコードとあわせて説明されているからです。書くコードの量がすくなくてすむGroovyの性質上、ある意味写経のやり甲斐はないかもしれませんが、読み終わる頃にはある程度Groovyのことがわかるようになっているのではないでしょうか。Javaプログラマに対するGroovyの敷居の低さは驚異的です。実は、Groovyにも黒魔術っぽいところはあります。私自身もそんなに知っているわけではありませんが、たとえば、以前DSLでご紹介したASTの操作は明らかにそちらの世界に一歩踏み出しているでしょう。この本では、Groovyのそういう部分に対してそれほど触れていません。Groovyの持つ普段の仕事を確実に楽にしてくれるであろう部分が、いわば「謙虚に」まとめられています。この辺りは、「Groovy便利なんだからもっと使えばいいのに」という著者の方々の思いがこもっているように感じられます。個人的にはGroovyに対して、どう控えめに見てももっと評価されてよいと思っています。この本がきっかけで、Groovyがもっと多くの人に使ってもらえるようになることを祈っています。見る*1:こういうまとめはどなたかがなさっているかもしれませんが、不勉強ゆえよく知らず・・・*2:先日のTDDBCはちょっと特別ですね。08:38書評:プログラミングGroovyTweet広告を非表示にするもっと読むコメントを書く2011-07-06技術系プレゼンテーションの前にやっておくべきことEssayプレゼンテーションの下準備を行う際に、普段心がけていることを整理する。導入ブログ、翻訳に続く舞台裏シリーズの第3弾として、プレゼン前の下準備の際に自分が心がけていることをまとめておきたいと思います(もう舞台裏ネタはないので、これで最後です)。Development"が、私が個人として行う初めてのパブリックスピーキングでした*1。その時から今まで何度か登壇の機会を頂いていますが、最初のとき以来、事前に必ずやるようにしていることが3つあります。一般的に基本と言われるものがすべからくそうであるように、たいしたことではないのですが、地道にやることでそれなりの効果は得られているような気がします。「こうしなければいけない」という話ではありません。「私はこうしています」という話です。参考にして頂ければ幸いです。内容に入る前に、すこし本のご紹介を。プレゼンテーションのマニュアルとして私が読んだのは次の2冊です。見るプレゼン資料を見て頂いたことのある方ならおわかりのとおり、私はzen主義者です。それほど原理的ではなく「写真を使ってメッセージを伝える」ということを除いて、書いてあったことを色々忘れているような気もします。『パブリックスピーカーの告白』もやはりいい本で、これを読んでからプレゼンを技術としてとらえるようになりました。私が必ず守っている教えは、『zen』から2つ、『告白』から1つもらっています。メッセージを明確に(プレゼンテーションzen)プレzenの奥義は「Flickrを使いこなすこと」ではもちろんありません。大切なのは「メッセージを伝えること」です。「メッセージ」という言葉が何か格式張った印象を与えるのであれば、「エレベーターピッチ」と言い換えてもいいでしょう。要するに、聞きに来てくれた方(しかも、仕事帰りだったり、休みの日だったりする貴重な時間を割いて来てくれた方)に対して、何を持って帰ってほしいのか、一言で、ということです。「ドメイン駆動設計入門」のメッセージは、「オブジェクトが表現するのは概念である」と「MVCのMはモデルであって、スクリプトではない」の2つでした。メッセージは、「戦略的設計とは何かを具体的にイメージしてほしい」というものでした。抽象的な議論が多く、DDD本を読んでいるだけだとイメージが湧きにくいのですが、かなり重要な箇所なのです。これはドメイン駆動設計入門の続編であると同時に、DDD前夜祭陽の巻の第4部編でもありました。Application」のメッセージは、「DDDを実践するために、欠けているものを埋めよう」でした。DDDは設計の話はしてくれますが、意外と具体的なプロセスの話をしてくれておらず、実際現場に適用するには、いくつかのプラクティスと組み合わせる必要があるのです。抜き打ちモデリングセッションで交流を、というのも大きなテーマでした。他にも、「旅行手配ドメイン」とか「Geb+Spock」とか、スパイスっぽいものはあるのですが、メインとなるメッセージをぼかしてしまうような話はかなり削っています。メッセージに焦点を合わせ続けられるようにし、余計なものを削る上では次のプラクティスが助けになります。発表原稿を作成する(プレゼンテーションzen)プレzenのいいところは、スライドを眺めているだけでそれなりに楽しいところ。悪いところは、話者がいないと何の話かさっぱりわからないところです。それを補うためにプレzenには「ハンドアウトを配ればいいじゃないか」という素朴なソリューションが提示されます。いえ、「ハンドアウトをプリントアウトするのもそれなりに手間がかかるし、どうしたものか」と考えた結果が、「発表前に書いて、後でブログで公開」でした。原稿を書くとストーリーを前もって組み立てられるので、論理に飛躍があるところとか、脱線しているところに気づくことができます。読み上げはしていません。ライブ感が失われるような気がするので。原稿も読まずにどうやっているのか、暗記でもしているのか、という話が次のプラクティスにつながります。リハーサルをする(パブリックスピーカーの告白)これは目からウロコが落ちました。曰く「うまくなりたいなら、練習しろよ」と。発表原稿を書くのとは前後するケースがありますが、私は最低2回リハーサルをやっています。PCを操作しながら、立って実際に声を出してやっているので、傍から見たら相当奇妙に見えるでしょう。2回というのは、別に勤勉なわけではありません。1回目は時間が大きくズレたり、話がうまくつながらなかったり、脱線したり、言いよどんだり・・・要するにうまくいかないんですよ。それを発表原稿にフィードバックして、スライドを増減させた上でもう一回やっています。これでうまくいくこともありますが、うまくいかないこともあります。いう場合は、もう一度。大体3回やれば感じがつかめます。このときに心がけているのが、「セリフを覚える」のではなく、「論点を整理する」ことです。「このスライドではこれとこれとこれを伝えよう」―こうしておけば、わりと普通に話すことができます。セリフがとんでいることもありますが、本当に伝えるべきメッセージが1つか2つに絞られているので、多少細かいことを言いそびれても、全体のメッセージを大きく外すことはありません。時間も、すこし早めに終わるように事前に調整していれば、手元のタイマーを見ながら話して大体ぴったりに収められます。@t_wadaさんに紹介してもらいました。お値段はちょっとするのですが、それに十分見合う便利なアイテムです。見る最後に一言で言えば、私のプレゼンは「上演」です。準備してきたものを「演じる」わけですね。準備がしっかりできていれば、当日はそれなりに心に余裕が持てます。実際にプレゼンをしているときに意識しているのは、なるべく聞いてくださっている方の反応を見ること。うなずいてくださったり、笑ってくださったりすると、うれしいじゃないですか。スライド作りとか、リハとか、それなりに準備に時間はかかるんですけど、でも喜んでくださる方の顔を見ると、やってよかったなー、と思います。大事なことですが、プレゼンは一人ではできません。場を準備して登壇させて下さる方がいて、聞いてくださる方がいて、初めて成り立つことなんですよね。いつも、どうもありがとうございます!*1:学会発表は以前にもやったことはありますが、あれはちょっと別ですので。2010年のデブサミで、JavaEE勉強会のメンバーとパネルディスカッションをやらせて頂いています。あれは勉強になりました。Scala2011年7月2日に開催されたTDDBC仙台のレポート。Camp」通称TDDBCにはずっと参加したいと思っていたわけですが、今回仙台で機会を得ることができました。最初はJavaでと思っていたのですが、Scala組に入れて頂きまして、山中(@ymnk)さん、武田(@takedasoft)さんと3人でチームを組んでペアプロという貴重な体験をさせて頂きました(どうもありがとうございました!)。最終的には仕様変更2が何となくかたちができたところで時間切れとなりました*1。プログラムが組み上がっていく過程や、興味深いリファクタリング、うっかりテストを書かずにコードを修正してしまったことによるバグの埋め込み、モックを使ったタイマー処理の分離など、非常に興味深い体験を数多くさせて頂きましたので、ここにご報告させて頂きます。作業中のコードは記憶を頼りに書いていますので、もしかすると動かない(コンパイルすらできない)可能性もありますが、その点はどうかご容赦ください(バージョン管理しておけばよかったと激しく後悔)。最終的なコードはこちらで公開しています。ネタバレを気にして和田さんに確認をとったのですが、「問題なし」とのこと。プロの仕事に頭が下がります。前半お題LRU方式で、最も使われていないキーに紐づくデータを追い出すようなマップを実装する。lru.get("KEY1"))}コンパイルすら通りませんが、外側=使う側から設計していくのがTDDです。たいしたことのない実装に見えますが、このコードを書いた瞬間に、かなりの設計判断をしています。クラスの名前はLruにする。Mapと同じように、putで値を設定し、getで取得する。コンパイルを通るようにして(つまり、クラスとメソッドを空で作って)テストを実行すると、結果はレッドになります(getでnullが返りますので)。これを通すためのコードも簡単です。}おなじみの仮実装ですね(よくよく見ると、keyフィールドも要らなかったですね)。テストを実行させてグリーンになることを確認するのは大切です。自分たちがすくなくとも、テストの書き方と実行のさせ方をわかっていることが確認できるわけですから。して、これからの「黄金の回転」のための基礎ができあがります(この段階ではリファクタリングをやりませんでしたが、今思えば、「クラス名ってLruでいいんだっけ?」という議論をしてもよかったかもしれません)。次に、この実装に対してレッドになるテストも簡単に書けます。lru.get("KEY2"))}もちろん、仮実装で粘ることもできるのですが、ここでフィールドをマップに変えました。HashMap[String,String]個人的には、TDDで一番"難しい"ポイントは「どのタイミングで最終的な実装に切り込むか」にある思っています。テストに通すだけなら、永遠に仮実装で凌ぎ続けることもできるわけです。それでは実際の使用に耐えられません。重要なのは次の2点だと考えています。テストは無限な現実の1ケースであり、実装は抽象的に考える必要がある。テストをグリーンにするコードは、最終形の途中にある不完全なかたちになる。この不完全なかたちを作る作業は、頭の中では先読みしつつちょっと手前でとめる作業になります。次にどうすればレッドにできるかわかっている状態でコードを書くことがあるということですね。逆に言えば、テストをレッドにするという行為は、目指す最終形に対して抽象化/一般化できていない部分を突き崩すものであるべきなのです。3つ目のテストでいよいよ、核心部に切り込むことになります。}保持しておくキーの数に特に指定はなかったのですが、とりあえず「2つ持つ」という仕様で作業を始めました。これはある意味現実を抽象化し切れていない部分ですので後で修正することにして、先にコアの実装をやっつけます。完成形に至る不完全なかたちとしては、「とりあえず受けとった順にキーをリストに詰めていって、3つ以上あったら先頭から消していく」という実装にしました。}これでテストはグリーンになりますが、バグがあることはすでにわかっています。同じキーを連続していれたら他のものを追い出してしまうんですね。そこを突いてレッドにするテストを書きます。lru.peek("KEY1"))}前の実装だと、keystackの中身が["KEY1","KEY1"]になってしまうので、"KEY2"が追い出されてレッドになります。そこでコードを修正します。「keystackの中にもうすでにキーが入っていたら、keystackをいじるのをやめよう!」if文で囲むだけですね。}たしかこのあたりで、「Scalaなんだし、nullを返すのはやめようよ」というリファクタリングが入りました。実装自体は一見良さそうに見えたのですが、まだレッドにできます。lru.peek("KEY3"))}"KEY1"に対応する値がNoneであるということで、レッドになってしまいます。理由は「★」の場所でkeystackの中身が["KEY1","KEY2"]のまま変わらないからですね。["KEY2","KEY1"]にしないといけません。「keystackの中から消して、後ろにつければいいじゃないか」ここでScalaっぽさが炸裂します。value}これでグリーンになります。見ると、keystackに対する2つの操作(★)は1行にできますね(実際にこのリファクタリングを行ったのはもうすこし後のことでしたが)。key一瞬完成したかと思ったのですが、ここで1つ忘れていたことを思いだしました。lru.get("KEY3"))}もちろん★の部分でkeystackの中身が更新されないので、"KEY1"が取得できず、テストはレッドになります。実装はそれほど難しくありません。putの先頭でkeystackをいじっているところをprivateメソッドに切り出し、getの先頭でも呼ぶようにしました。map.get(key)}これで完成だろうとドヤ顔でテストを実行したところで、和田さんの準備した落し穴に顔面から突き刺さりました。「いままで通っていたテストが通らなくなった!なんだこれは?」こういうとき、原因に一瞬で気づく人がいるのが強力なチームのよいところ。状態を変更しないで値が取れるメソッドpeekを準備して、テストをすべて書き換えました。map.get(key)「これで要件を満たしただろう」と考えて、ちょっとしたリファクタリングを行いました。keystackを触る処理をメソッドではなくプライベートクラスとして抽出したのです。ListBuffer[String]]()その上で、先ほどのペンディング事項「キャッシュサイズは変えられるようにしないと」を実装しました。[...]Scalaのデフォルト引数機能を使うことで、これまでのテストは触らなくてすみます。{ここで前半が終了しました。Lru()をテストフィクスチャに追い出しています。Lru();}コードレビューコードレビューの最中、Ruby組の方から驚きのバグが報告されました。「入っていない値をgetしたときにスタックの順序が狂ってしまいますね」自分たちのコードのバグとしての気づきでしたが、我々のコードにも同様の問題がありました。和田さんの準備した落し穴に、気づかぬうちに落ちていたわけです。ここでまたしても重要だと思うのが、テストは現実のさまざまなケースをパターン化し、具体的にしたものです。「本当にこれまでのテストですべてのパターンを網羅できているか?」「気づいていない操作パターンはないか?」そういうことを考えることが非常に重要だということですね。「レッドにする」ことは時として非常に高度な技術を要します。後半:仕様変更ここで和田さんから(うれしそうに)仕様変更のお知らせが入りました。仕様変更キャッシュのサイズを後から変更できるようにしたい。時間が経ったら消えるようにしたい。スレッドセーフにしてほしい。後半のセッションの開始時、「存在しないものに対するget」のバグが悔しかった僕は、ついうっかりコードを先に書いてしまいました(和田さんが隣にいたらものすごく怒られたところです。None書いた後で気がつきます。「ごめんね、和田さん」反省して、テストコードを書きました。lru.peek("KEY2"))}しかし、レッド。getで相変わらず状態が変わってしまっています。眺めて気がつきました。メソッドの途中ではreturnが省略できないんですよね。Noneテストは先に書かなければいけないし、すくなくとも実装したものはテストしないといけないということですね*2。仕様変更1でちょっと難しいところは、キャッシュサイズを減らした場合にkeystackを削らなければいけない点にあります。この「削り」に関して、またしてもScalaのかっこいいAPIが炸裂します(Scala勉強会@東北のページを紹介して頂きました)。}あふれている分を取得(dropRight)してマップから削除し、stackから必要なところだけ取り出して(takeRight)再代入します。これで、テストはグリーンになります。ここで、すこし大きめのリファクタリングを行いました。仕様変更2で結構大きな変更が想定されたので、それに耐えられるようにこのタイミングできれいにしておきたかったのです。map.remove(0)}「やっていることはすごく似ていないだろうか?」すこし考えて、「どちらの場合もcacheSizeに合わせて、keystackとmapを削っているだけだ」ということに気づきました。今まで使っていたコード(すぐ上)は、新しく書いたコード(2つ上)の特殊ケースにすぎないのです。それをまとめた結果、コードはかなりすっきりしました。}}実は、午前中に行った「KeyStackクラスの作成」というリファクタリングの恩恵に、ここであずかることができています。この変更はKeyStackクラスの中に閉じており、外側のLru本体には影響を与えていません。「古いものを消す」という仕様を入れるためには、時間の概念を導入しなければなりません。スレッドを新しく走らせる必要もあるでしょう。これをいままでのクラスに入れたくはなかったですし、テストコードも書きにくくなってしまいます。そこで、テストコードを書く前に図を書いて、設計の検討を行いました。TimerとLruとの間にはRemovable(これも名前の検討は必要ですが)というインターフェイスを立てます。Timerのテスト(=時間の概念が入る)は、Removableをモック化して定期的にメッセージが送られていることを確認します。Lruのテストは直接Removableに定義されたメソッドを呼び出し、正しくふるまっていることを確認します。Timerのテスト時にモックが必要になります。本当はjMockやMockitoのようなモックフレームワークがあるとよかったのですが、限られた時間の中で(しかもScala)で動かす自信がなかったので、プライベートクラスとしてごりっと実装してしまいました。}}ここに重要な設計が1つあって、RemovableのメソッドがDateオブジェクトを受けとるものであることを定めています。本体はこうなりました。}}タイマーができたところで、Lru本体に取りかかります。lru.peek("KEY1"))}「removeに渡されたDateよりも前に使われたものは消そう」と決めました。keystackに時間の概念を導入します(このとき、Lruクラスには影響がなく、変更はすべてプライベートクラスであるKeystackクラスに閉じています)。Dateはアレですが、それはリファクタリングできれいになるでしょう)。removeが呼ばれたときにそのタイムスタンプを見て、古いものを削除することになります。)}このあたりでタイムアップとなりました。(Beck))」で詳しく説明されています。講演の中で和田さんがモダンTDDのバイブルとして位置づけていた本ですね。補足TimerとLruを分割して個別のテストから書き始めましたが、2つを組み合わせて使おうと思うと、もう一工夫必要です。lru}という感じになってしまいます。「外側から内側へ」という発想を常に忘れてはいけないという教訓を示す好例です。Removableインターフェイスの仕様を事前に明確に定めていなかったため、現段階ではちょっとしたバグがあります。一定時間ごとにその瞬間より前のキャッシュが消えてしまうんですね。実際には、「○○分/秒前の値を消す」という考慮が必要でしょう。どちらのケースに対してもTDDのサイクルを使って切り込んでいくことができます。レッドになるコード(もしかしたら、コンパイルもできないコード)を書くところから。まとめTDDBC仙台で窓際にいたScala組の歩みをそれなりに正確に再現できたのではないかと思います。すべてがこんなにスムーズに行ったわけではなく、僕が「あれ、この場合for文ってどう書くんでしたっけ?ちょっとお願いします!」とかいってキーボードを渡したシーンもありましたし、「あれ?なんで通らないんだ?」とデバッグコードを埋め込んでみたシーンもありました。逆に、想定外の落ち方をしたときにその原因にすぐ気づいてもらえたり、タイムスタンプの実装で「keystackの中身をタプルにすればいいよね」と一瞬でコードを書いてもらえたりと、強力な方々とペアプロをすることの生み出す豊かさを随所で感じることができました。非常に贅沢な時間をすごさせていただいたことを、チームを組ませて頂いた山中さん、武田さんを始め、講師の和田さん、そして開催に尽力してくださった方々に深く感謝します。体験したことがないという方は、ぜひ参加されるといいと思いますよ!最後にもう一度、TDDの黄金の回転について整理したいと思います。レッド「失敗するテストを書く」これは新しいパターンのテストケースを書く作業であると同時に、インターフェイスを設計する作業でもあります。適切な粒度で最終形との距離を縮めていくか。ここに、TDDのエッセンスが凝縮されているんじゃないかと思います。グリーングリーンにすること自体は、おそらく最も純粋な「プログラミング」の作業です。これが適切に行うためには、言語やアルゴリズムに対する知識が必要になりますし、それはそれで身につけるべきものです。1つつけ加えると、コードを書いていると「どのみちこれではこういう場合に落ちるな」ということに気づくことがあります。それが次のレッドの種になるわけですから、本当は手元に紙を置いておいて、書き留めてみるといいのかもしれません。リファクタリング「リファクタリング」というと、コードを修正しないといけないような気になりますが、多分そんなことはありません。グリーンになったところでコードを見直し、「これで大丈夫かな?」と次のレッドの種を探したり、あらためて構造を頭の中で整理してみたり、言葉に出して確認してみたり、という作業が非常に有効だということに気づきました。コードを修正するかどうかは、結果にすぎないということですね。ひどく頭を使ったせいか、終わった後は予想以上に消耗していました。Week」ルールがありますが(残業は週40時間まで、じゃないですよ)。このレベルで日々の仕事をやっていたら、残業なんてとてもじゃないけど身体がもちません。見る*1:時間内ではテストが1つ通らなかったのですが、家に帰って見てみたら、変数の再代入が原因のなんてことないバグでした。公開しているコードでは修正しています。*2:経験ありませんか?ちょろっとバグを直して、テストをしないでいたら、あとですげー怒られることになったという。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。