「やる夫で学ぶTDD」九日目 スコア計算 #ytdd

某SIerで if(true == false) のような神コードのインスペクションで廃人化しているやらない夫こと太田先生の「やる夫で学ぶTDD」九日目です。そして感動のクライマックスへ。
2
Kenichiro Ota @oota_ken

やる夫で学ぶTDD 第9日目 スコア計算 始めます。 #ytdd

2011-02-15 23:58:32
Kenichiro Ota @oota_ken

やらない夫:やる夫、昨日は劇眠くて、芸材のフレームの計算までで止めてしまったが、実は現在のフレームまで求めていると、スコアは一瞬だったりするんだな。すでに課題2で別のデータ構造でスコア計算しちゃっているしな。 #ytdd

2011-02-16 00:00:01
Kenichiro Ota @oota_ken

やる夫:クラスScoreが昨日 score.add(pin); だけしていたけど、あれを使うのかお? #ytdd

2011-02-16 00:00:44
Kenichiro Ota @oota_ken

やらない夫:さらにテストケースもフレームのものをそのまま拡張して、スコア用のassert分を付け加えていくだけでできるので、まあ、やってみよう。 #ytdd

2011-02-16 00:01:18
Kenichiro Ota @oota_ken

やらない夫:もともと、 bowling.bowl(4); assertEquals(1, bowling.getCurrentFrame()); やって現在のフレームだけ求めていたものに、 #ytdd

2011-02-16 00:02:05
Kenichiro Ota @oota_ken

やらない夫: assertEquals(4, bowling.getFrameScore(1)); というようにフレームの番号を指定したものと、 assertEquals(4, bowling.socre()); 現在のスコアを取得するものを追加する。 #ytdd

2011-02-16 00:03:09
Kenichiro Ota @oota_ken

やる夫:最初の assertEquals(4, bowling.getFrameScore(1)); は仕様にはないけど、getCurrentFrameと同じく、ヘルパーメソッドかお? #ytdd

2011-02-16 00:03:46
Kenichiro Ota @oota_ken

やらない夫:そうだ。最初から、scoreだけpublicにして、テストするとちとしんどいので、ヘルパーメソッドもpublicにして、テストしていくようにした。最終的にはpackage privateにしてもいいかな。 #ytdd

2011-02-16 00:04:54
Kenichiro Ota @oota_ken

やらない夫:同じようにして、通常の投球、つまり、ストライクでもスペアでもないテストケースについて、assertをこんな風に追加していく。 #ytdd http://twitpic.com/404ort

2011-02-16 00:08:10
拡大
Kenichiro Ota @oota_ken

やる夫:もちろん、最初は固定値を返すところからやるんだお。テストケースを付け足しながら、汎用的な実装に変えていくんだお。 #ytdd

2011-02-16 00:09:21
Kenichiro Ota @oota_ken

やる夫:ストライクもスペアも考慮しなくていいなら、クラスScoreは簡単だお! private int[] pins = new int[21]; 投球ごとのピンの数を格納する配列を用意してあげて、これはMax21投球で固定にするお。 #ytdd

2011-02-16 00:13:21
Kenichiro Ota @oota_ken

やる夫:現在のピンの位置を表す private int pinIndex = 0; を用意して、public void add(int pin) { pins[pinIndex++] = pin; } を作って投げるごとに、Bowlingが呼び出すようにするお! #ytdd

2011-02-16 00:14:46
Kenichiro Ota @oota_ken

やる夫:そして、public int getFrameScore(int frameIndex) { で、指定したフレームのスコアを求めるようにするお。 #ytdd

2011-02-16 00:15:34
Kenichiro Ota @oota_ken

やる夫:このコードは課題2のコードを流用して for (int currentPin = 0, currentFrame = 1; currentFrame <= frameIndex; currentFrame++) { とぐるぐるフレームごとにループを回すようにして#ytdd

2011-02-16 00:16:48
Kenichiro Ota @oota_ken

やる夫:ループ内は frameScore += pins[currentPin] + pins[currentPin + 1]; currentPin += 2; とすればさっきのテストには全合格だお! #ytdd

2011-02-16 00:18:16
Kenichiro Ota @oota_ken

やらない夫:そして、その後、ストライクとスペアのケースについても、scoreのassert文を追加していって、合格するような実装をScoreクラスにテストを追加しながら書いていく。 #ytdd http://twitpic.com/404t3f

2011-02-16 00:21:00
拡大
Kenichiro Ota @oota_ken

やる夫:これまたすでに課題2でほとんど作っているから、ロジックを流用できるお。ただし、課題2では、ストライクの場合、 10 0のようにデータを与えていたけど、課題3では powl(10); powl(5);のようにストライクの場合は明示的に0を入れないようにするから #ytdd

2011-02-16 00:22:46
Kenichiro Ota @oota_ken

やる夫:そのデータ構造の違いには注意する必要があるお。逆に、10 0と課題3のデータ構造で成る場合は、ストライクを取ったフレームの後のフレームでガーターになってしまった時を意味するお! #ytdd

2011-02-16 00:23:51
Kenichiro Ota @oota_ken

やる夫:データ構造と仕様をシンプルにしたから、実装ももっとシンプルになってこんな感じになったお。これまた全ケース合格だお。 #ytdd http://twitpic.com/404uks

2011-02-16 00:25:22
拡大
Kenichiro Ota @oota_ken

やらない夫:テストケースを見てもらえばわかるとおり、現在のスコアについては、10フレーム投げ終わった場合以外は、その後連続してガーター(つまり0)が続くという最悪パターンでの値を返すようにしているので、ここが実装がシンプルにできた理由だな。 #ytdd

2011-02-16 00:26:44
Kenichiro Ota @oota_ken

やらない夫:待て!境界値として危ない10フレーム目のテストケースについても、スコアに対するaasertを追加して、それでテストケースが全合格したら、無事実装成功だといえる。今回の実装では、特に10フレーム目を特殊扱いしなくても、#ytdd

2011-02-16 00:28:25
Kenichiro Ota @oota_ken

やらない夫:問題なく動作すると思うが、念のためにテストケースを拡張する。10フレーム目の考慮はBowlingクラスだけがするようにしている。 #ytdd

2011-02-16 00:30:12
Kenichiro Ota @oota_ken

やる夫:もちろん全合格だおー!完成だおー! #ytdd

2011-02-16 00:34:36
Kenichiro Ota @oota_ken

やらない夫:実は境界値とエラー推測の観点からはテストケースと実装の両方がまだ不足しているのだが、それはデブサミ当日にぜひ参加者の皆さんと作っていきたいんだな。 #ytdd

2011-02-16 00:35:40