というわけで、10/8に行われたTDD BootCamp 1.6 for C++に参加してきたので、復習も兼ねて感想などを。
TDDBCには半年ぐらい前から興味を持っていたけれど、1.5は定員オーバー、1.6は開催に気づかず、1.6 for PHPは言語の問題で無理。今回ようやく参加できたんだったりする。
TDD自体はファウラーの「リファクタリング」→XPの流れの中で知っていて、何度か実践しようとしてみたことはあった。でも当時のコードではUI部分のStateMachineが多く、初期化が難しかったり動作や確認に手作業/目視がどーしても必要で、自動テストの導入の所で挫折してたりする。
現職だとお客さんの流儀に合わせないとならないから、なおさら。
まず最初に、@kaorun55さんの講演「TDDBCへようこそ」
TDDBCと、TDD自体の説明。
印象に残ったのは、バージョン管理、タスク管理、自動化テストは開発者にとっては「躾」レベルの基本的なスキルである、という点。
この歳になると人を教える立場につくことが多いけれど、技術的なこと(そこ自体わりと問題だったりするが)の次に教えることとして、そのあたりを抑えると良さそうで、参考になった。
それからペアに分かれて、運営側から小出しに出される課題について、ペアプロでTDDを実践。
課題は鉄道路線の経路探索。課題1、2あたりは駅数も少ないので、GoogleTestの練習を兼ねてベタなコードで片付ける。が、最終的にグラフの探索を行わなければならない予感はしていて、どのタイミングでクラスを作るなどRichな構造に直すか、ペアの方と相談を始めてたりする。
次にレビュー。まず隣のペアと実装を見せあってから、全体で2~3ペアを選んでコードを発表してもらう。最初から構造を作り込んでるペア、課題を分析して簡単な条件を導き出し最低限の実装で行ってるペアなど、それぞれ特色があって面白い。
昼食の後、@goyokiさんの講演「テスト駆動開発入門 ネクストステップ」
TDDを理解した後で、どのように実践し、またどのように理解を深めていくかの指針。
まず実践にあたっては、テストコード自体の保守の重要性が強調され、そのための様々なテクニックを紹介していただけた。
今の案件でもテストコードの保守には手を焼いていて、大変参考になった。
学習についても、読まなきゃとAmazonのカートには入れたものの後回しになっている本がいくつも紹介されて、精進が必要だと改めて認識。
そして課題の続き。課題3がGreenになった所でデータ構造のクラス化に踏み切る。この時間違えたのは、新規にクラスを起こすところのコードについてTDDを行わなかったことで、案の定バグを作り込んで解決にかなり時間を使ってしまった。結局、課題4の途中で時間切れ。
バグの主因はデータの実体と参照の混同(というか分けて考えていなかった)で、ナビゲータをしながら嫌な予感はしていたのだが、介入せずにそのまま流してしまったのは反省点だった。boostを入れてあれば、せめてshared_ptr+仮想コンストラクタで実装して影響を出にくくできたのに。少なくともTR1なら入っているはずなので、その時気づいていれば。
さいごに@t_wadaさんによる総括の講演。
問題を扱いやすい粒度に分割することが重要、という点と、実際にTDDを適用した結果の工数変化が参考になった。
実際に手を動かせて、また参加者が比較的バラエティに富んでいて違う業種の方とコミュニケートでき、有意義な勉強会だった。
今後も機会があれば参加したいし、それだけではなく自社内でなり客先の人を巻き込むなりして、なんか出来ればなぁと思った。
2011年10月9日日曜日
2011年10月3日月曜日
IO#unpack!
String#unpack!の続き。
普通はStringが処理できれば問題ないんだけど、時にはIO(と言うかファイル)を直接パースしたい時がある。特にファイルだとサイズが巨大で、事前に全部読み込んでおくのが現実的でないこともあるし。
ここで問題になるのが、pack文字列の中には読んでみないとデータ長が確定しない場合があること。なので、事前にpack文字列をパースして、判るならば読み込み長を事前に計算する実装にしてみた。以下ソース。
長さがわからなったら、packして実際のデータ長を求め直してからseekするという富豪設計。ちなみにSTDINとかのtellやseekがエラーになるストリームでは使えないので注意。
本来unpackで消費するバイト数が取得できたり、unpack時に1バイト取ってくる動作をカスタマイズできたりすれば、こんなゴマカシは必要ないわけで。この辺はRubyの入出力周り、とくにIOが安易にunixのファイルに準拠した仕様になっている弊害だと思う。
ここで問題になるのが、pack文字列の中には読んでみないとデータ長が確定しない場合があること。なので、事前にpack文字列をパースして、判るならば読み込み長を事前に計算する実装にしてみた。以下ソース。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class IO | |
def unpack!(ptn) | |
ptn2 = ptn.dup | |
until ptn2.empty? | |
len += case ptn2 | |
when /\*/ | |
len = nil | |
break | |
when /^[aAcCx](\d+)/ | |
$1 ? $1.to_i : 1 | |
when /^[sSnv](\d+)/ | |
($1 ? $1.to_i : 1) * 2 | |
when /^[iIlLNVfegPp](\d+)/ | |
($1 ? $1.to_i : 1) * 4 | |
when /^[qQdEG](\d+)/ | |
($1 ? $1.to_i : 1) * 8 | |
else | |
len = nil | |
break | |
end | |
ptn.gsub(/^#{$&}/,'') | |
end | |
pos = self.tell if len.nil? | |
ret = read(len).unpack(ptn) | |
if len.nil? | |
pos += ret.pack(ptn).size | |
self.pos = pos | |
ret | |
end | |
end | |
end | |
# use like this | |
open(ARGV[0]) do |f| | |
p f.unpack!('csi') | |
p f.tell | |
end |
本来unpackで消費するバイト数が取得できたり、unpack時に1バイト取ってくる動作をカスタマイズできたりすれば、こんなゴマカシは必要ないわけで。この辺はRubyの入出力周り、とくにIOが安易にunixのファイルに準拠した仕様になっている弊害だと思う。
登録:
投稿 (Atom)