PR

もうすぐ完成! テスト駆動開発によるボウリングのスコア計算プログラム

2015年2月19日(木)
吉谷 愛

はじめに

前回は、「静的設計のブレイクスルー」をテーマに、「設計の洗練」とそれに伴うリファクタリングについて初学者の「古谷」がTDDの達人である「高梨先輩」に教えを乞う形で進めてまいりました。今回は前回「責務の分担」を行ったクラスに実装を行う過程についてご説明します。なお、この連載ではプログラミング言語RubyでTDDを実現しておりますが、必ずしもRuby経験者が対象ではありません。Ruby未経験者でもプログラミング経験者であれば、ある程度理解できるように考慮して進めてまいります。

______________________________________

  1. [古谷]古谷です。前回は、ありがとうございました。正直、前回学んだ範囲について、ちゃんと理解できているとはいいがたい状態です。設計のスキルも、MECE(Mutually Exclusive and Collectively Exhaustive:重複なく、漏れなく)で今までの設計を俯瞰するスキルも、どちらも私はまだまだです……
  2. [高梨]高梨です。前回は盛りだくさんで大変でしたからね。ですが、私が伝えたかったのは、実は以下の2点だけなのです。
  1. TDDを進めていると、今回のように「手詰まり感」が出てくることがあります。そのタイミングで、一度静的な設計を見直しましょう。
  2. TDDを行う際には、「動的設計」と「静的設計」を交互に行き来することで、全体的な設計を洗練していきます。
  1. [高梨]今回は、前回手掛けたBowlingGameクラスとFrameクラスの「責務の分担」の実装を進めることで、古谷さんの不安を一掃していきましょう。大丈夫です、今の不安を自信に変えて行くのがTDDですから。
  2. [古谷]不安を一掃……
    なんとなく倍増しそうな気がしなくもないですが、わかりました。頑張ります!
  3. [高梨]大丈夫です。今回も今まで通りインクリメンタルに進めましょうね。前回どこまで進んだか覚えていますか?
  4. [古谷]前回はここまでの設計を行いました。そして、投球を実際に記録するところまでは一応実装しています。
前回までの成果(クリックで拡大)

図1:前回までの成果(クリックで拡大)

  1. [高梨]ありがとうございます!
    それでは前回に引き続き今回もFrameの実装を続けましょう。まずは「Frameの完了を判定する」このテストケースを作ってみてください。
  2. [古谷]フレームの完了?
    えっと、投球が2回完了しているかどうかの判断だけで大丈夫ですか?
  3. [高梨]まずはそれからで大丈夫ですよ。メソッド名はfinished?でどうでしょう?
    今回のテストケースでは、refuteメソッドを使用してください。refuteは、結果がfalseである場合に成功します。これまで使っていたassertは、結果がtrueである場合に成功を返すメソッドなので、正反対ですね。
  4. [古谷]はい、テストケースを作りました。まだfinished?メソッドをFrameクラスに組み込んでいないので、エラーになりますが。
  5. [高梨]大丈夫です!
    それではFrameクラスの実装を行いましょう。
  6. [古谷]しつこいようですが、今は2投=フレーム完了だけ考えていたら良いですよね?
  7. [高梨]大丈夫です!
  8. [古谷]それでいいのであれば……
  • initializeメソッドに@shot_count = 0を定義
  • record_shotメソッドで@shot_countのインクリメント
  • finished?メソッドの実装
  1. [古谷]上記で良いでしょうか?
  2. [高梨]やってみてください。
  3. [古谷]できました!
フレームの完了を判定する。2回投げたら終了(クリックで拡大)

図2:フレームの完了を判定する。2回投げたら終了(クリックで拡大)

  1. [高梨]大丈夫です!判定もできていますね!
    では次に、ストライクへの対応も行ってください。
  2. [古谷]ストライクストライク、うーん……
  3. [高梨]単純に「10ピン倒したらフレーム終了」と判断して大丈夫ですよ。
  4. [古谷]あれ、そんなに単純で良いのかな?
    なら、finished?メソッドの条件も「@scoreが10以上だったら完了」で大丈夫ですか?
  5. [高梨]大丈夫です、実装してみてください。
  6. [古谷]こんな感じになりました!
フレームの完了を判定する。ストライクなら1投目でも完了(クリックで拡大)

図3:フレームの完了を判定する。ストライクなら1投目でも完了(クリックで拡大)

  1. [高梨]素晴らしい!
    それでは、またFrameをBowlingGameクラスに組み込んでいきましょう。
  2. [古谷]ええっ!?
    すみません、今の私にはどうやって組み込んだら良いのか見当もつきません!
  3. [高梨]落ち着いてください。方針はもう決まっています。
  • 現在のフレームを知っている
  • 投球を適切なFrameに記録させる
  1. [高梨]今回の組み込みの際に意識するのは、この2点だけです。
  2. [古谷]そ、そういわれても……
  3. [高梨]ボーリングは10フレームあります。つまり10個分のフレームオブジェクトをこのBowlingGameで管理するわけですが、古谷さんは今回どういう方法で管理するつもりですか?
  4. [古谷]えっと、そうですね、配列が良いかなと思っていますが……ダメですか?
  5. [高梨]大丈夫です!
    では配列で管理する場合、「現在のフレーム」は配列のどこに格納されますか?
  6. [古谷]どこ?どこって……うーんフラグを……
  7. [高梨]「現在のフレーム」は常に「最新のフレーム」です。ですから、投球完了後に次の投球を入力するFrameオブジェクトを生成して配列の最後尾にセットしていたら、最後尾のフレーム=現在のフレームと考えて大丈夫です。まあ、まずはBowlingGameのテストクラスにテストケースを実装してみましょう。そうですね、「全ての投球を1にしてすべてのフレームの点数が2になるか」というテストケースでお願いします。
  8. [古谷]はい、期待値は2なのですが、今のところBowlingGameクラスはFrame単位で得点を管理してないので、20が返ってきてエラーになります。
  9. [高梨]はい、それではいよいよ、BowlingGameクラスへの組み込みを進めていきましょう。まずは今まで通り、initializeメソッドにフレームを管理する変数を定義し、初期化します。
  10. [古谷]はい、それでは10フレーム分管理する変数を、@framesと言う名前で定義します。ただ……
    すみません、Rubyでの配列初期化の仕方がわかりません!
  11. [高梨]@frames = [ Frame.new ]としてください。ちなみに、@framesの最後の要素を取得する場合はlastメソッドを使用します。また配列の要素を追加する場合は、@frames
  12. [古谷]そこまで教えてもらったら、できそうな気がします。
    えーと……はい、これでどうでしょう?
FrameクラスをBowlingGameクラスに連携させる(クリックで拡大)

図4:FrameクラスをBowlingGameクラスに連携させる(クリックで拡大)

  1. [高梨]大丈夫です!よく頑張りました、ここまでの成果を反映したクラス図を見てみましょう。
    古谷さん、ここまで実装できましたよ!
ここまでの成果(クリックで拡大)

図5:ここまでの成果(クリックで拡大)

  1. [古谷]ありがとうございます。ひとえに高梨先輩がいらっしゃるからなのですが、やってみたら何とかなるものですね。この勢いのまま頑張ります!
    えーと、次は「投球結果を判定する」ですね?
  2. [高梨]おっ、やる気スイッチが入ったようですね!
    そうですね、次は投球結果の判定をFrameに実装します。まずはスペアから始めましょう。finished?と同じ要領で、spare?メソッドを実装してください。今までと同じように、テストケースからお願いします。テストケース名は「test_2投目で10ピン倒すとスペア」でお願いします。
  3. [古谷]あ、これはfinished?と同じように作れます!
スペアを判定するspare? メソッドの追加(クリックで拡大)

図6:スペアを判定するspare? メソッドの追加(クリックで拡大)

  1. [高梨]素晴らしい!それでは全く同じ要領で、ストライクの判定も実装しましょう。テストケース名は「test_1投目で10ピンを倒すとストライク」でどうでしょう?
  2. [古谷]スペアと違って、1投球目だったらストライクですよね……
    はい、できました!
ストライクを判定するstrike?メソッドの追加(クリックで拡大)

図7:ストライクを判定するstrike?メソッドの追加(クリックで拡大)

フロイデ株式会社 代表取締役

「最新のアーキテクチャを追及し続ける技術者集団」を目指す、フロイデ株式会社代表取締役社長。現在は、自身のCOBOLからRailsまでの非常に幅広い開発経験や、学生や未経験社員への技術指導経験を糧に、技術講師としてソフトウエアエンジニアの育成に注力している。2013年06月より、初心者向けの「はじめようRuby on Rails開発!」シリーズを考案。“技術者の立場にたった、技術者の心に火をつける”熱い講義をモットーとしている。

連載バックナンバー

Think IT会員サービス無料登録受付中

Think ITでは、より付加価値の高いコンテンツを会員サービスとして提供しています。会員登録を済ませてThink ITのWebサイトにログインすることでさまざまな限定特典を入手できるようになります。

Think IT会員サービスの概要とメリットをチェック

他にもこの記事が読まれています