完成形となったテスト駆動開発によるボウリングスコア計算プログラム
2015年3月4日(水)
はじめに
前回は「責務の分担」をテーマに、「設計の洗練」とそれに伴うリファクタリングについて、初学者の「古谷」がTDDの達人である「高梨先輩」に教えを乞う形で進めてまいりました。今回はいよいよ最終回です。「責務の分担」を行ったクラスを実装し、大規模なリファクタリングを行います。なお、この連載ではプログラミング言語RubyでTDDを実現しておりますが、必ずしもRuby経験者が対象ではありません。Ruby未経験者でもプログラミング経験者であれば、ある程度理解できるように考慮して進めてまいります。
______________________________________
- [古谷]古谷です。前回は、ありがとうございました。前々回、静的設計の見直しを行う際は不安で仕方なかったのですが、前回実際に先輩に教えていただきながら少しずつコードにしていくことで、ちょっとずつ自信がついてきました。
- [高梨]高梨です。前回の目標は、前々回に設計を手掛けたBowlingGameクラスとFrameクラスの「責務の分担」の実装を進めることで古谷さんの不安を一掃していくことだったので、まずは目標達成ですね、安心しました。不安を自信に変えていくのがTDDですからね。では、最終回も全力で行きましょう。古谷さん、前回はここまで進みました。覚えていますか?
- [古谷]はい、今回の目標は「ボーナス点の保持と記録した点数を返す責務をFrameに委譲した後、リファクタリングを極める」、でしたよね。とはいえ、何から手を付けたらいいか……
- [高梨]大丈夫です、最終回もインクリメンタルに行きましょう。まずはこの図2の通り、ボーナスの振り分けを行う部分以外は、Frameに委譲しましょう。
今BowlingGameクラスにある3つの変数
@spare
@strike_bonus_count
@double_bonus_count
ですが、これらはボーナス判定及びボーナス計算のためのものですので、Frameに渡しましょう。そして、Frameにボーナス点を記録するためのメソッドadd_bonusとボーナスをセットする必要があるかどうかを判断するneed_bonus?メソッドを用意しましょう。
- [古谷]は、はい……
- [高梨]そしてボーナス回数もFrame側で管理します。スペアなら1回、ストライクなら2回。受取り終わったらneed_bonus?がfalseになるようにしましょう。ん?どうしました?顔色がよくないようですが。
- [古谷]すみません、私、今の高梨先輩についていけていません……
- [高梨]大丈夫です、インクリメンタルに行きましょう。まずはテストケースから考えましょう。古谷さん、下のテストケースを実行したら、第1フレームの点数は何点になりますか?
- 第1投: 5ピン倒す
- 第2投: 5ピン倒す
- ボーナスとして5点追加
- [古谷]第1フレーム…… フレーム単位ですよね? だったら単純に15点でいいですか?
- [高梨]その通りです! では、テストケースを実装してみてください。
- [古谷]はい、テストケース自体はとっても簡単です!
- [高梨]実装も簡単ですよ。まずボーナスを格納するための@bonusを今まで通りinitializeに定義します。そして点数は、このボーナスを加算したものを返すように修正しましょう。それからボーナスを追加するadd_bonusメソッドの定義、これだけです。
- [古谷]こんな感じで大丈夫ですか?
- [高梨]大丈夫です!ではまたBowlingGameクラスに組み込んでいきましょう。
- [古谷]うっ、難しそう・・・
- [高梨]大丈夫です、インクリメンタルに、いつも通りテストケースから考えましょう。
- 第1投: 3ピン倒す
- 第2投: 7ピン倒す
- 第3投: 4ピン倒す
- 残り17投 全てガタ—
- [高梨]この場合、トータルの点数と第1フレームの点数は、それぞれ何点になると思いますか?
- [古谷]あ、それならわかります。2投目でスペアになるので、3投目の4ピン分がスペアボーナスになります。ですから、トータルの点数は18点、第1フレームの点数は14点になるはずです。
- [高梨]素晴らしい! それではまず、このテストケースを実装して実行してみてください…… と言いたいところですが、実はこのテストケース、「test_スペアをとると次の投球のピン数を加算」で既に実装されています。今回は「第1フレームの点数」のコードだけ追加してください。
- [古谷]はい。えーと、スペアボーナスのロジックが実装されていないので、14点ではなく10点になります。
- [高梨]では、どうしましょうか?
- [古谷]スペアをとった時点では、スペアのボーナス得点ってわからないですよね?
- [高梨]そうですね、次の投球で判明します。
- [古谷]安直ですみませんが、@spare_frameという変数を定義してスペアをとった時点でそこにスペアフレームを格納し、次の投球でそのスペアフレームに倒したピン数を格納する…… で大丈夫でしょうか?
- [高梨]いいんじゃないですか? 実装してみてください。
- [古谷]はい! あ、Rubyの配列って参照渡しですよね?
- [高梨]大丈夫です!
- [古谷]それではこれでどうでしょう?
- [高梨]素晴らしい!それではこのまま、ストライクのテストケースにも挑戦しましょう。
- 第1投: 10ピン倒す(ストライク)
- 第2投: 3ピン倒す
- 第3投: 3ピン倒す
- 第4投: 1ピン倒す
- 残り15投 全てガタ—
- [高梨]この場合のトータルの点数と第1フレームの点数は、それぞれ何点になると思いますか?
- [古谷]えーとこれはですね、次の2投分が追加されるので、10+3+3=16点になります!
- [高梨]その通りです。こちらも既にテストケースは「test_ストライクをとると次の2投分のピン数を加算」で実装されていますので、追加するテストケースは第1フレームの点数のみです。どうなりましたか?
- [古谷]はい、FrameにStrikeのロジックが実装されていないので、10点が返ってきます。
- [高梨]それでは、スペアと同じ要領でストライクも実装してもらえますか?
- [古谷]こんな感じでしょうか?
- [高梨]素晴らしい! 慣れてきましたね!!
- [古谷]はい、あと、前回定義したテストケースが活かせているので、とても気持ちがいいです。これまで私がやってきたことは、無駄じゃなかったなって実感しています。
- [高梨]それは良かったです。次も前回のテストケースを利用します。連続ストライクですが覚えていますか?
- [古谷]はい、「test_連続ストライクすなわちダブル」ですよね?
- 第1投: 10ピン倒す(ストライク)
- 第2投: 10ピン倒す(ストライク)
- 第3投: 3ピン倒す
- 第4投: 1ピン倒す
- 残り14投 全てガタ—
- [古谷]確かこれで、トータルの点数は41点だったはずです。
- [高梨]そうです。今回はこのテストケースに、第1、第2フレームの点数を追加してください。
- [古谷]はい、ああ、やっぱり第2フレームは10点のままですね。
- [高梨]そうですね、では、先程のspareと同じ要領で実装してみてください。
- [古谷]はい、これでどうでしょう?!
連載バックナンバー
Think ITメルマガ会員登録受付中
Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。