前編では,テスト駆動開発(TDD)の概要と,従来型の実装設計の手法を説明し,TDDを適用することによって最初に説明した開発手順が「どのように変わるか」を説明した。後編では,TDDの適用が向いているのはどのような開発なのかについて考えてみよう。

 まず,TDDではあらかじめ実装仕様を決定しないという点から,


  • オープンシステム
  • オブジェクト指向プログラミング

に向いているということが言える。

 オープンシステムの場合,プログラムが直接利用するAPIはもとより,そのAPIを提供するミドルウエア,フレームワーク,OS,データベースといったプラットフォームに至るまで,開発者が問題発生時に制御する手段がほとんどない。利用するプラットフォームがオープンソース・プロダクトであれば制御手段がないということはありえないが,オープンソース・プロダクトの修正リスクまで事前に見積もっているプロジェクトは通常はないだろう。

 このことは,開発そのものがブラックボックスに依存した,非常に脆弱な足場しか持たないことを意味する。したがって,実際にプログラムを作成し動かすということが非常に重要になる。そこで問題が早期に発見できれば回避して別の手段を取るといったことが可能となるからだ。

 したがって,あらかじめモジュールの詳細な動作を決定することは,プラットフォーム自体を自分たちで構築するのでない限りリスクが大き過ぎる。オープンシステムを採用する理由は,メインフレームと比べた場合の価格だけではなく,利用可能な開発者の数,利用可能なミドルウエアなどの数,といったものが含まれているはずである。それを有効に活用するためには,まずプログラムを動かさなければならないのだ。それを逆にリスクと考えるのであれば,オープンシステムの利用は差し控えるべきである。

 TDDを採用することは実行動作を確認しながら完成を目指すことでもある。まさに,オープンシステム上での実装に適合している手法であることがわかる。

 次に,オブジェクト指向開発においては,オブジェクト指向設計が(当然のことだが)重要となる。なぜならば,プログラミング言語やツールが,それを補助することを目的としているからだ。逆に言えば,オブジェクト設計をせずにオブジェクト指向言語を利用することはインピーダンス・ミスマッチを起こし,開発効率を非常に低下させる。まさに,連載第1回の冒頭で例に出した,エンジン付きのトラックを人力車であるかのように人間が引っ張るような誤りに相当する。

 また,正しい方向で利用しないと,いわゆる開発者ベースの大きさによるメリット――単に人員の採用だけではなく,利用可能なツールの種類,量,参考可能な資料の種類,量など――を享受できなくなるという点も指摘しておく。

オブジェクト指向設計にも難しさがあるが・・・

 オブジェクト指向設計に問題があるとすれば,それは,理想的なオブジェクトの粒度,インタフェース,関連といったものの発見が困難だということである。そのために,デザインパターンのように,ある問題を解決するためにはどうするべきか,というような考え方が生まれるのである。しかし,デザインパターンはあくまでも一般論であって,個々の実装のヒントにはなるが,そのまま利用できる実装ではない。それを実装に近づけたものがフレームワークであるが,フレームワークは汎用的な処理については作成可能であるが,個々のアプリケーションの実装の面倒を見ることはできない。そのため,個々の実装は開発者が自分で考えなければならない。しかし机上で考えた実装が正しく動作すると前提して実装の全工程を考えることは既に書いたようにオープンシステムでは危険である。また,モジュール間のインタフェースは,実装中期以降に判明してくる例外的な状況に対応させるために,当初の予測から変化するものである。しかも,一般論としてはモジュール間のインタフェースは後から拡張するように設計しなければならない。最初は狭く,徐々に広く作るのは,インタフェース設計のセオリーなのだ。したがって,考えながら実装して動作させて確認したほうが良い。そのほうが問題が出た場合の手戻りがはるかに少なくて済むからである。ここでセオリーを曲げてあらかじめ何でもありとして設計するというのは,既に誤りの始まりであることは指摘しておく。セオリーを逆転させてうまくいくことはないからセオリーなのだ。

 それでは,オープンシステムやJ2EE上のオブジェクト指向開発を利用するメリットは何なのか? それをここで書く必要はないだろう。今まで書いてきたオープンシステム上での開発やオブジェクト指向設計での問題点は,あくまでも従来型開発手法の視点から見た場合に問題点と捉えることができるということに過ぎない。従来型開発手法は長考に次ぐ長考という手法によって問題を発生させないように工程を進める。しかし,オープンシステムとオブジェクト指向開発はどちらもトライ&エラーを要求する。したがって問題だと見えるということなのだ。

 まずは視点を変えて同じ土俵に上らなければ正当な評価を下すことは不可能だ。100回トライ&エラーを実行してもそれが従来型開発手法での1回の長考よりも短時間で完了するのであれば,それは効率の向上を意味する。したがってあらかじめ,オープンシステムやオブジェクト指向設計というものがトライ&エラーを要求するものだと割り切って行動しなければならない。その上で,判断をくだすべきことなのだ。それをせっかく長考したのにエラーになって時間切れとなるのが真の問題なのである。

 オープンなシステムなのだから,システム自体の実装も常に流動的である。その代わり変化に素早く対応でき,時間が進むに連れて実現可能な領域が大きくなる。オブジェクト指向なので,余分な工程を必要とせずに正確なプログラムが開発できる。この恩恵を受けるためにはこちらの土俵に乗らなければならない。それだけのことだ。

 なお,事前に実装設計書が記述できないのであれば,開発後に記述したらどうか?  という考え方があるのは知っている。筆者の経験からの結論は,NOだ。書けるのは機能の詳細とインタフェースの詳細までであって,実装の詳細記述は事実上意味がない。

 なぜならば,オブジェクト設計により,外部に対する1つの機能は実装内部では細かい複数のオブジェクトによって実現されるからだ。そのため,仕様自体が細かいオブジェクト間に分散されるため文書化しても読むことができないからだ。当然,書くことも困難である。また,その細粒度の設計が再利用可能かと言うと非常に怪しい。
しかし,ソース・ファイルは異なる。エディターを使えばどこで何が参照しているかを調べることは文書に比べればはるかに効率良くできる。また,TDDの理由にソースの可読性を高めることが含まれる点を忘れてはいけない。これは本当に重要な点で,リファクタリングしながら開発可能な点が,TDDの良さだからだ。

 また,Javaがなぜ,CやC++の文法を元にしたかという理由も考慮すべきだ。これらの言語は,コンピュータを学ぶものにとっては基本常識のようなものだからだ。すなわち,読める人間は多数いる(余談だが,読める人間が多数いるからこそ,オープンソースという文化が成立していると言っても良いだろう)。

 ここで,従来型の手法ではなぜ実装設計が必要だったかを思い起こしていた だきたい。開発時点では設計者と実装者の分業のためで,その理由としてプログ ラム作成時の入力デバイスおよび言語仕様に起因する記述の困難さを挙げた。これは現在,存在しない理由である。一方,開発終了後には,実装設計はソースコードと分離されたもう1つの実装として意味があった。だが,現在,ソースコードと分離した実装としての文書にどれだけの意味があるだろうか? 10年後にJavaが無くなり,誰もソースを読めなくなる可能性はどのくらいあるだろうか? もしそうだとして,その開発された実装が手付かずに10年後もそのままの形で動いているのだろうか? そもそもJ2EEで開発するということは,システムマイグレーション時の移行コストの抑制にあるのではないだろうか? J2EEであれば,多少の問題はあるものの,基本的にはそのまま移行できる。であれば,ソース・ファイル以上の実装には意味がないではないか。しかも,実装設計はオブジェクト指向設計でJavaに依存したオブジェクトの粒度での記述となるのだ。それは構造化設計の詳細ダイアグラムとは全く異なるビューである。すなわち再利用できない。その意味では,むしろ機能定義の充実を図るほうが良いと考えられる。

プログラマの役割と責任が変化する

 今回のまとめは次のものだ。

 従来の開発の実装工程は,1970年代のCOBOLをプログラミング言語として採用している時には意味があり,正当であった。しかし,オープンシステム上でオブジェクト指向言語を利用した開発ではTDDが適合している。

 これはどのような変化なのか?

 プログラマは単に与えられたフローチャートや詳細仕様を右から左へ翻訳するだけでだめになったということだ。プログラマは設計者でもなければならない。ある機能をどう実現するかに責任を持つ必要がある。それは,現在の販売員が自動販売機のように金を受け取って商品を渡すだけではなく,たとえば衣料品販売であればコーディネータである必要があるのと同様に,機械任せ可能な領域が増えたための変化だ。

 管理者側は,設計から実装までの領域を全面的にプログラマの裁量に委ねなければならない。実装(=設計)開始前から全体のコード量や不具合摘出件数を見積もることはもはや不可能だ。当然,コード設計やモジュール設計に従って工業的にプログラムが開発されることを望むことはできない。可能なことは,この機能をいついつまでに実装してくれ,と依頼することだけだ。それはオープンシステムを利用することで,システムの大部分をアウトソースしてしまったことと同様だ。管理しようとすればするほどインピーダンス・ミスマッチによってリスクが増加する。リスクで済むうちはまだ良いのだが,大型トラックで丸木橋を渡れと言ったら,まともな人間なら降りるし,忠実な人間なら落ちる。どちらにしても向こう側にはたどり着けない。結局,目の前の丸木橋をあきらめて運転手に道程を任せなければならない。これは管理の放棄ではない。運転手に要求することは身の安全を確保し交通法規を遵守して決められた時間に荷物を運ぶことだ。これまでは身の安全や交通法規についてまでも口を出せたのだが,それは終わりだということなのだ。

 最後にJavaを利用してプログラムを開発すること,それが管理面でどのようなインピーダンス・ミスマッチを起こすかの一部の例を挙げることは無駄ではないだろう。

  • プログラムはパッケージと呼ばれるOSのファイルシステムのディレクトリ階層と密結合した構造で分類し「管理する」。このことは,開発後のプログラムを単一のディレクトリで管理するという考えではうまくいかないことを意味する(JARと呼ばれる形式で単一ファイル化可能だが1カ所に配備すればOKというわけではない)。
  • プログラムはソース・ファイルと無関係にクラスと呼ばれる単位でバイナリ・ファイルが生成される。
    このことは,開発成果物としてのソース・ファイルとバイナリ・ファイルを1対1で管理するという手法は取れないことを意味する。無理やり1ソース1バイナリとすると実装上の問題を惹き起こし,数時間で済む工程が非常識な手法を取る事により多大な工数を必要とし,しかもメンテナンス性を損なうことになる。
  • Javaの実装は2年単位くらいでバージョンアップされる。ある時点で未解決のバグはバージョンやリリースアップにより解決される。また,ミドルウエアについても同様にバグ修正のためのバージョンアップがある。このことは,1度納入したシステムが恒久的ではないことを意味する。特に,セキュリティ面での問題に関しては,ネットワーク到達可能な位置でアプリケーション・サーバーを稼働させていれば,できるだけ速やかにバージョンアップしてシステムへの影響を避ける必要がある。




筆者紹介 arton


謎のITエンジニア。RTOS上でのデータベースエンジンを皮切りにミドルウエアやフレームワークとそれを利用するアプリケーションの開発を行い現在に至る。専門分野がある業界に特化しているために逆にメインフレームクラスから携帯端末まで,その時の需要に応じてダウンサイジングしたりアップサイジングしたりしながらオブジェクトを連携させていくという変化に富んだ開発者人生を歩んでいる。最近はもっぱらJ2EEが主戦場。著書に『Rubyを256倍使うための本 邪道編』『Visual C# .NETによるWebプログラミング入門』共著に『J2EEプログラミング講座』(いずれもアスキー刊)などがある。