読者です 読者をやめる 読者になる 読者になる

/home/by-natures/dev*

ソフトウェア開発者として働く人の技術的なメモ

Ruby でペアプログラミングをしてみた

先日、東京Ruby会議10 のスタッフである @Shindo200 さんに誘っていただき、はじめてペアプログラミングペアプロ)をしてきました。その時感じたペアプロの面白さを、行った課題内容も含めてお伝えできればと思います。

ペアプロとは?

ペアプログラミングについて、Wikipedia の定義では次のようになっています:

ペアプログラミング(英: pair programming)は、2人のプログラマが1台のワークステーションを使って共同でソフトウェア開発を行う手法である。一方が単体テストを打ち込んでいるときに、もう一方がそのテストを通るクラスについて考えるといったように、相補的な作業をする。 実際にキーボードを操作してコードを書く人を「ドライバー」、もう1人を「ナビゲーター」と呼ぶ。30分ごとか、単体テストを1つ完成させる度に役割を交替するのがよいとされる。また、1日に一度の割合でパートナーを変えるのがよいともされている。

今回はここまでカッチリ行った訳ではなく、適当なタイミングで交代しながら1台のPCでプログラムを作り上げていきました。

ペアプロのメリットは色々考えられますが、Wikipedia で最も最初に挙がっているのは「規範意識の増大。ペアプログラミングでは、個人の作業よりもサボりにくく、ちゃんと作業を進める可能性が高い。」というものです。私も今回初めてやってみて、この利点は大きいと感じました。取り組んだ課題に対して途中で投げ出すこと無く、詳細まで考えてプログラムが作成できたからです。

一方でペアプロを実際に業務に取り入れた場合のデメリットも Wikipedia では多く挙げられていますが、仕事以外でも友人・知人とペアプロを行うことでモチベーションを上げることが出来たり、お互いの知らない知識を教え合うことができました。

では、実際に行ったペアプロの内容をご紹介します。

今回行ったペアプロの内容

今回は TDD(テスト駆動開発)を行いました。最初にテストを書いてから、これをパスするためのプログラムを作るというスタイルです。@Shindo200 さんは東京Ruby会議10 で知り合った方でしたので、もちろん言語は Ruby です。テストは RSpec というテストツールを使って作成しました。テストもいきなりすべて書くのではなく、簡単なテストをパスするためにプログラムを作り、次第にテストを増やしてプログラムの完成度を上げていきました。

課題1: Fizz Buzz

まずは有名な Fizz Buzz を実装しました。Ruby で一からクラスを書くのは久しぶりだったので、Ruby 自体の文法も調べつつ RSpec のテストコードを書いたり、リファクタリングや変数名・クラス名なども修正し、30分弱で完成しました。

class Fizz
  def print(n)
    retval = ""
    retval = "fizz" if n % 3 == 0
    retval += "buzz" if n % 5 == 0
    retval = n if retval == ""
    retval
  end
  def solve(n_from,n_to)
    (n_from..n_to).map { |n| print(n) }
  end
end

print は値を返すべきじゃないかとか、各変数名が安易なのでちゃんと命名しないと…と言いつつも、RSpec のテストも通っているので、Fizz Buzz はここで切り上げて次の課題に取り組みました。

課題2: ボウリングのスコア計算

次はボウリングのスコア計算プログラム。フレームと倒したピンの数が与えられ、そこから最終的なスコアを計算するというものです。ボーリングの点数計算ってどうなっているんだっけ?という確認をしつつだったので、リファクタリングも入れて2時間かかりました(^_^; 最初に「すべてガーターだった場合」や「すべて1ピンだった場合」など簡単なテストケースを考え、ストライクを増やしたりパーフェクトゲームをテスト対象に加えつつ、次第にアルゴリズムを複雑にしていきました。

アルゴリズムは何度も修正を加えました。たとえば、10フレーム目から再帰的に作れば上手く動くかなと思っていたんですが、ストライクは2フレーム先までしか見ないというルールを見落としていたため、300点になるはずのパーフェクトゲームが1500点ほどになってしまうなど、意外な盲点が。。頭で考えて満足してしまう僕の悪い癖です。結局1フレーム目からループを回すアルゴリズムに修正しました。

このボーリングのスコア計算プログラムが完成したときは、その場しのぎの条件分岐が多く、一応動くけれどプログラムとしてはお互い満足のいくものではありませんでした。結構時間を食ってしまったのでリファクタリングはしないで終わらせようとも思ったのですが、せっかくだからという思いもあり、リファクタリングも行いました。無駄な変数・メソッドの排除、番兵を利用した無駄な 条件分岐の排除などを行った取り組んだ結果、お互いが驚くほど簡潔なプログラムになりました。規範意識の増大という、ペアプロの利点が生かされた瞬間だったと思います。

class Bowling
  def get_total_score(frames)
    frames.push([0,0])
    scores = []
    frames.each_with_index { |frame, i|
      scores.push(frame.inject(:+))
      if i < 9 && is_strike?(frame)
          scores[i] += frames[i+1][0] + frames[i+1][1]
          scores[i] += frames[i+2][0] if is_strike?(frames[i+1])
      end
      scores[i] += frames[i+1][0] if is_spare?(frame)
    }
    scores.inject(:+)
  end

  def is_strike?(frame)
    frame[0] == 10
  end

  def is_spare?(frame)
    !is_strike?(frame) && frame[0]+frame[1] == 10
  end
end

このプログラムに対する RSpec のテストコードは次のコードを作成しました。テストから先に作成する TDD では、その時その時で目標がハッキリと定まっているため、プログラムしやすいと感じました。

今回は少しずつテストを増やすことでアルゴリズムを完全なものに近付けていったのですが、テストの作成手順によっては回り道をして実装してしまうことがあったため、どのようにテストを増やしていくかも重要だと感じました。

# coding: utf-8
$LOAD_PATH << File.dirname(__FILE__)
require "bowling"

describe Bowling do
  describe "#get_total_score" do
    it "全部ガーターだった場合、スコア0を返すこと" do
      bowling = Bowling.new
      ary = Array.new(10,[0,0])
      bowling.get_total_score(ary).should eq 0
    end
    it "パーフェクトゲームの場合、スコア300を返すこと" do
      bowling = Bowling.new
      ary = Array.new(9, [10,0]).push([10,10,10])
      bowling.get_total_score(ary).should eq 300
    end
  end
end

実行結果はこのようになっています。オプションを付ければ結果が色で分かるため、一目瞭然です。 [caption id="attachment_2190" align="aligncenter" width="459"]RSpecの実行結果 ボーリングの点数計算プログラムに対し、RSpec で作成したテストコードを実行した結果です。[/caption]

感想

初めてのペアプロでしたが、予想以上に面白かったです!

まず、自分の知らない知識を学べたこと。TDD も初めてだったし、RSpec を利用したのも初めてでしたので、とても勉強になりました。TDD 未経験の僕には、テストからプログラムを作るというのは斬新な経験でした。

休憩時間を設けて、その間にどんな言語でプログラムを作ったことがあるかを見せ合ったりして、少し Haskell の話をしたり、僕は Scheme の話をしたりするなど、情報交換としても面白い場でした。まったく異なった境遇や職種にいる人とペアプロすることで、このように新たな発見もありました。

ペアプロ自体の利点も知れました。ボウリングの特定計算プログラムは、ルールも細かくて考えることが多かったため、一人であれば途中で「あとはこんな感じだろう」と辞めてしまっていた気がします。これをペアプロ形式にすることで最後までプログラムを仕上げることができ、細部の罠にも気付くことができました。リファクタリングを行うことでプログラムを綺麗にすることもできて、ペアプロはプログラムの質を上げるのにも役立つことが分かりました。

ペアプロのお題は世の中に沢山あるようです。@shinyaa31 さんのブログエントリ「[ペアプロ][TDD]ペアプロ・TDDの『お題』をまとめてみた #tddbc #coderetreat #pp_con」はとても参考になりそうです。みなさんも是非、TDD を用いたペアプロを行ってみてはいかがでしょうか。僕へのお誘いも待ってます(^_^