見通しのよいコードを書くことも重要だ。仕様を明瞭にして漏れのないようにすることだって重要だ。どれも重要だが、しかし現実のソフトウェア開発において軽視させることが多かった。明確な仕様もなく(それどころか断片的な仕様さえ1週間も経たずにころころ中身が変わる)、ろくに単体テストもされていない、見通しの悪い○○なコードで構築されたソフトウェアの何と多いことか。TDDが巧妙なのは、ソフトウェアの実装フェーズにテスト(最低でも単体テスト)を組み込み、一体化させることで、プログラマを否応なくソフトウェア・テストの世界に引きずり込もうとしている点だ。プログラマにソフトウェア・テストを意識させることで、暗黙のうちに前述の各要素を強制させている。テストの追加を「単なる重荷」ととらえて、形式的にしかTDDしない開発者もいるだろう。そのような開発者の手にかかれば、TDDだろうと何だろうと効果はゼロだ。TDDを契機にソフトウェア・テストに積極的に取り組み、単体テストを行い、テスタビリティの観点からコードの書き方を改善し、仕様の網羅・明確化という観点で問題を洗い出すようになる開発者だっているだろう。個人的には、上記の点がTDDのメリットであり、これらのメリットが得られるのならテスト・ファーストに徹する必要は無いと考えている。私を含めて悪い意味での「怠惰なプログラマ」がサボってしまいがちなこれらの要素を、TDDは暗黙のうちに強制しようと働く。その意味でTDDは巧妙で、実に有用な興味深い手法だと言えるだろう。筆者は研究開発系のプロジェクトに関わることが多く、「そもそも、このアルゴリズムで大丈夫なのか?」という不明点が多々ある段階で設計・コーディングを行うことが大半である。試作品を何度も揉んで、ある程度「この方法で実現できそう」という感触が得られてから、関数単位での単体テストや、ライブラリ単位での統合テストに着手している。それより前に単体テストに着手すると、試作品を揉んだ結果として「データ構造を含む大規模な見直し」が発生したので単体テストも大幅に書き直す――なんてことが発生して、時間的コストがかかり過ぎるからだ。いう事情があるので、厳密なTDDは行っていない。単数単位の単体テストは実施しているし、単体テストの自動化もしている。私の環境においては「試作品を何度も揉んで、ある程度『この方法で実現できそう』という感触が得られ」るという粒度が、TDDにおける「自分のシステムにおいて期待する振る舞いを記述するテストを書き、それをパスさせる」という手法に適した大きさなのだろう。単体テストは重要な要素ではあるものの、単体テストが「おま環」でのTDDに適した粒度であるとは限らない――という点を考慮した上で、TDDについて評価すべきだろうし、評価結果を提示する時は「おま環」について併記すべきだろう。ないと議論が噛み合わないはずだ。*1:本職のテスト担当の人は、優秀な人は本当に優秀だ。このバグ、どうやって見つけたのだろう......。*2:実際には、まあ、色々とありまして......。*3:現実的に、このような状況がありうるか疑問ではある。*4:Pascalのprocedureのような感じ。Pythonここ2~3年ほどPythonで小さなコマンドライン・ツールを書く機会が何度かあったのだが、argparseでコマンドライン引数を解析する時、引数を1つだけとるオプションの扱いに難儀している。ArgumentParser.add_argument()のキーワード引数nargsに1を設定すればよいのだが、解析結果がリストで返される。「args.option_foo[0]」みたいにインデックスを指定する必要がある。APIの思想としては、この挙動は理解できる。APIを使う側としては面倒くさいのだ。面倒なのが、省略可能なオプションでかつ省略時に適当な既定値が無いケースだ。この場合、「args.option_foo」は空のリストである可能性がある(必須オプションや、キーワード引数defaultを使って適当な既定値を用意できるケースでは、リストが空になることはない)。args.option_foo:」のように空リストか否か確認するコードと、「args.option_foo[0]」で値を参照するコードを書き分ける必要がある。しないと空リストなのに「args.option_foo[0]」で値を参照しようとして例外となる。このコンテキストの切り替えが面倒なのである。大体「複数の値を取りうるオプション(結果として値が0個になる可能性もあるオプション)」の場合はイテレータを使うので、例外が発生することはまず無い。argparseのnargsの仕様からすれば、引数を1つだけとるオプションが例外的なのである。私が作るツールでは「引数を1つだけとるオプション」が大多数を占めているので、例外的状況ばかりなのである。上記挙動を嫌って、少し前まではキーワード引数nargsに'?'を指定していた。この方法なら解析結果を「args.option_foo」という書き方で参照できる。難点として、コマンドライン引数にて「引数無しでオプションを指定する」ということが許容されてしまう。value_foo」みたいに指定して欲しいところを「--option-foo」とだけ指定された時に、argparseが許容してしまうのである(というか、それが'?'の時の仕様通りの挙動なのだが)。何か「いい感じ」にできないか考えた結果、現時点ではキーワード引数nargsに1を設定した上で、ArgumentParser.parse_args()やArgumentParser.parse_known_args()の戻り値に以下の関数を適用して「いい感じ」に変換することで対応している。Options(**attrs)関数simplify_options()のキーワード引数as_listに登録された名前以外のリスト型のattributeを展開した上で、namedtuple化して返している。引数はリストのままにしておく。'bar'])リストのままにしておきたいものを明示する必要がある点が面倒と言えば面倒だ。実際のところ、コマンドライン・ツールにて「複数の引数を受け取るオプション」を設ける機会は意外と少ない。「毎度面倒な手順を踏む」という状況にはならない。私が見落としているだけで、こんな関数を用意しなくとも、標準の機能でうまい具合に扱える気がするのだが......。「C言語を覚えると組込み系の採用に有利」みたいな就職・転職向けの文言を見る度に、世の中そんなに甘くないと言いたくなるのです。組込み開発に限らず、例えばLinuxコンソールアプリ等でC言語を散々使った人も同意するのではないかと思うのだけど、Cプログラマは他のCプログラマに厳しいものだ。そもそもC言語自体が鋭い刃物に例えられる代物で、メスにもドスにもなりうる取り扱い注意な言語だ*1。Cプログラマは誰しも、C言語について一言居士となる。そんな連中からすれば、C言語の基本文法を知っていて簡単な課題を解ける程度の人は、「C言語を書ける人」には含まれない。どのレベルの人が「C言語を書ける人」に該当するのか?よくよく考えてみると、次の点をクリアしているか否かがポイントではないかと思う。C言語の各種文法の「効率的で理にかなった使い方」をある程度知っている。C言語とメモリモデル(スレッドじゃなくてハードウェア・アーキテクチャの方)にまつわるアレコレをある程度把握している。普通のCプログラマは関数の引数として構造体の実体ではなくポインタを指定するように実装するものだが、そういう癖を身に着けていているか、そうする理由を知っているか否か――ということだ。この辺は、師匠となる本や人に揉まれながら、C言語でそこそこの大きさの実用的な何かを実装した経験によって成長する部分だろう。C言語に限らない話で、他のどの言語でも同じだ。要するに『Cプログラミング専門課程』で語られているような、C言語とメモリに関する諸々をどこまで体得しているか、という話だ。プログラミング言語はハードウェアに対するある種の抽象化層として機能するのだけど、C言語はその抽象化層が薄くて且つ容易に穴をあけることができる*2。穴の中にはメモリ空間が広がっている*3。穴は簡単にあいて容易に拡がる代物であるし、Cプログラマもまたよせばいいのに好き好んで穴をあけようとする連中なもので、どうしても穴の中のメモリとの付き合い方を覚える必要がある。うまく付き合えないCプログラマは翌朝顔にひっかき傷をこさえて出勤する定めにある。そもそも組込み以外のソフトウェアであっても、ファイルやTCP/IP経由で外部とバイナリデータ*4でやり取りすることになった時点で「メモリ上のデータの形式」というメモリとのお付き合いが始まるものだ。組込みでなくともC言語を使うなら、メモリとの上手な付き合い方は必須の教養だ。抽象化層の薄さと穴のあけ易さはC言語(とC++)の特異な部分だ。他の言語はもう少し厚い抽象化層を持っているし、穴があきにくいように工夫されている。言語では抽象化層(言語機能とその有効な使い方)を学べば大半の物事が済むのだが、C言語では言語機能という抽象化層を学ぶだけでは不十分で、穴の中のメモリを知る必要がある。メモリとの付き合い方を毎年指導するのがしんどいので他人任せにしたいのだが、唯一使える本だと思っている『Cプログラミング専門課程』は入手困難なのである。見る代わりの本、何か無いかなあ?*1:この例えの元ネタは藤原博文氏の『Cプログラミング診断室』である。*2:だからこそドライバのような「ハードウェアを直接叩く」系のソフトを書くことができるのだが。*3:組込みソフトウェアの場合は物理メモリだし、WindowsやLinuxなどのアプリの場合は論理メモリだ。*4:ここでは「テキスト形式ではないデータ」ぐらいの意味で使っている。見る差し詰め本書は「お仕事でAndroidアプリを書くことになったAndroidアプリ以外のアプリ開発経験がある職業プログラマ」向けの入門書、といったところだろうか。実際に、まえがきの「本書の特色」に以下の記載がある:そのため、本書では特に業務でのアプリ開発でまず押さえておかなければならないポイントに絞って解説を行っています。ListViewを使ったアプリの例題の場合、類書だと単純にArrayAdapter<String>で文字列を表示するだけで終わることが多いのだけど、本書ではlistitemのレイアウトファイルを作成して独自のアダプタを実装するところまで説明されている。仕事では独自のアダプタを実装する構成のアプリが多い訳だ。本書について、ネット上では「構成が直感的じゃない」という指摘があって、まあ確かに一般的なAndroidアプリ開発の入門書と比較すれば直感的ではないのだけど、でも順を追って読み進めれば問題にならない。:)だから急にAndroidアプリ開発の仕事が降ってきた人が入門書を買う際には、本書を候補に入れることを推奨する。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。