完成形となったテスト駆動開発によるボウリングスコア計算プログラム

2015年3月4日(水)
吉谷 愛
  1. [高梨]大丈夫です!!
  2. [古谷]こ、これでもうボーナスに関しての責務の分担は終わりましたか?!
  3. [高梨]あっすみません。
    本当はこれで終わりなのですが、「ボーナスの必要性を判定する機能」も、Frameに持たせましょう。
  4. [古谷]ボーナスの必要性判定?
  5. [高梨]はい、たとえば第1フレームでスペアを取った場合、1回ボーナスを付与する必要があります。ですが、1度ボーナスを付与したらそれ以上そのフレームにボーナスを付与する必要はありません。つまりFrameに、スペアやストライクを取ったフレームに既にボーナスを追加したか否かを判定させる機能を持たせるわけです。メソッド名はneed_bonus?なんてどうでしょう?
  6. [古谷]ということは、ストライクでもスペアでもない場合は、need_bonus?は常にfalseを返すのでしょうか?
  7. [高梨]その通りです!
    ちなみに「ストライクでもスペアでもないフレーム」のことは、オープンフレームと言います。まずはFrameのテストクラスにオープンフレームの場合のテストケースを実装して、エラーが出たらFrameクラスにメソッドを実装してください。
  8. [古谷]これで大丈夫ですか?
ボーナスが必要かどうかもFrameで判定する

図7:ボーナスが必要かどうかもFrameで判定する(クリックで拡大)

  1. [高梨]大丈夫です!
    では次にスペアに対応させていきましょう。スペアの場合のテストケースをお願いします。
  1. 第1投: 5ピン倒す
  2. 第2投: 5ピン倒す(スペア)
  3. ボーナス付与前のneed_bonusがtrueになっている
  4. ボーナスを付与する。
  5. ボーナス付与前のneed_bonusがfalseになっている
  1. [古谷]はい…… テストケースだけだと、3番目で falseが返ってエラーになります。
  2. [高梨]では、Frameの実装を進めていきましょう。まずボーナスカウントを格納する変数 @bonus_countを定義します。これは、ボーナスが加算されるたびにインクリメントされる変数です。スペアの時は@bonus_countが1より小さければtrueを、大きければfalseを返せば良いでしょう。
  3. [古谷]はい、えーとRubyは最終実行行の結果を返すから…… こんな感じで大丈夫でしょうか?
ボーナスの必要性を判定するneed_bonus?をスペアに対応させる

図8:ボーナスの必要性を判定するneed_bonus?をスペアに対応させる(クリックで拡大)

  1. [高梨]大丈夫です!
    それでは最後に、ストライクの場合のテストケースをお願いします。
  1. 第1投: 10ピン倒す
  2. ボーナスを5点付与する。
  3. ボーナス付与前のneed_bonus?がtrueになっている
  4. ボーナスを再度5点付与する。
  5. ボーナス付与前のneed_bonus?がfalseになっている
  1. [古谷]はい…… これも3番目でfalseが返ってエラーになります。
  2. [高梨]この後の実装は…… もう大丈夫ですか?
  3. [古谷]はい! これでどうでしょう?
need_bonus?をストライクに対応させる

図9:need_bonus?をストライクに対応させる(クリックで拡大)

  1. [高梨]素晴らしい!いよいよ大詰めですね。
    それでは最後に、得点計算の「記録した点数を返す」部分をFrameに委譲し、BowlingGameにFrameの点数を合計する機能を実装しましょう。まず点数はすべてFrameが保持していますので、@score変数は削除します。代わりにscoreメソッドでは、配列内のFrameの点数をすべて合計して返すようにしましょう。
  2. [古谷]えーと、こんな感じでいいですか?
フレームの点数を合計して得点を計算するように変更

図10:フレームの点数を合計して得点を計算するように変更(クリックで拡大)

続・フレームの点数を合計して得点を計算するように変更

図11:続・フレームの点数を合計して得点を計算するように変更(クリックで拡大)

  1. [高梨]そうですね、scoreメソッドのeach文はあまり好きではありませんが、後で修正しましょう。
  2. [古谷]す、すみません…
  3. [高梨]次に@spareを削除しましょう。
    ボーナスの必要性はneed_bonus?を使って判定するようにしてください。
  4. [古谷]えーとneed_bonus?の判定は、spare_frameがnilの時だとエラーになるから……
    これでどうでしょう?
スペアの計算をFrameに一元化

図12:スペアの計算をFrameに一元化(クリックで拡大)

  1. [高梨]大丈夫です!
    では、このまま、@strike_bonus_countと@double_bonus_countも廃止して代わりに@strike_frameと@double_frameのneed_bonus?で管理するようにして下さい。
  2. [古谷]うっ、これはちょっと難しいですね。あ、でも、ストライクとダブルの判定はneed_bonus?でできるか…… これでどうでしょう?
ストライクの計算もFrameに一元化

図13:ストライクの計算もFrameに一元化(クリックで拡大)

  1. [高梨]素晴らしい!
    @strike_frameがnilもしくは@strike_frameのneed_bonus?がfalseの時は通常のストライクで、それ以外はダブルと判断するわけですね?素敵ですが……こうしてみると、「ストライク」と「ダブル」と2つあるのは無駄な気がしますね。DRYの法則に反しています。
  2. [古谷]そうですね、修正してみます。実は私もこれには最初から、もにょっていたので……
リファクタリングして、ダブルの処理をストライクに統合

図14:リファクタリングして、ダブルの処理をストライクに統合(クリックで拡大)

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

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

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

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