増7.TDD考
●今回の発端
筆者は、COBOLでのバッチ処理も、RDBでのバッチ処理も実際にやったことが有ります。
その経験からすると、どう考えても昔の方が(TDDの観点からすると)「作法に叶っていた」ように思います。
もちろん、「インターフェイス」というような機能は見当たらず(なかったと断言できないのがきつい!)、その代わりに名称をテストされる側のプログラムと揃えたテストドライバを作りました。そのテストドライバはずっと取って置き、必要に応じて流し直したりしていました。
いったいどうなってしまったのでしょうか?
●COBOLでのバッチ処理
COBOLでのバッチ処理について簡単に説明します。
- COBOLでのバッチ処理は、複数のCOBOLプログラム&ソートツール&echoみたいなツール(ジェナーとか言いました)(1つ1つをジョブステップと言います)をスクリプト言語で繋いで順々に実行することで成り立っています(全体でジョブと言います)
- それらの間は中間ファイル(CSVやTSVみたいなものだが、セパレータがなく「何バイト目から何バイトはなになに項目」とする形式でした)でレコード情報を渡します
- スクリプト言語はJCLといい、マクロもあり、実行当初に与えたパラメータでマクロを実行するしないを決定するIF文みたいなのも有りますが、繰り返しはありませんでした
そして、そのスクリプト言語を何らかの“ストリーム(の様な)”に流し込むとプログラムが実行され、その実行されたプログラムが中間ファイルに結果を書いてそれを利用します。その“ストリーム(の様な)”は(まるで.netのTextReaderとStringReaderの様に)何段階ものフィルターの様になっていて、適した口に流し込むと、TSSになったり、時にはCOBOLコンパイラになったり、EDITORになったりしました。
●テストドライバ
冒頭でいいましたテストドライバですが、JCLではジョブステップ単位のテストとなります。スクリプトから直にプログラムを呼ばず、スクリプトがテストドライバを呼び、テスト結果を(SYSOUTやSYSDBOUTから出力された)中間ファイルに書き、それを見て結果を判断します。
●なぜCOBOLはTDDの観点からすると作法にかなっているのか
それは、ジョブステップで出来ることが、RDBの各DMLでできることとかなり違う事が有ると思います。
- 入力中間ファイルを読み、出力中間ファイルに書く。中間ファイルの更新は基本的にしない
- 基本的に1つのジョブステップでは1回の読み書きのみをする。(複数読み書きを1つのジョブステップにしてもかまわないが、(リアルタイムオンラインと異なり)バッチ処理ではそれほど名前空間が逼迫していなかったので、そこまではしなかった)
- 「抽出+並べ替えのキー作成」で1つ、(必要なら)「キーブレークの最後を示すフラグ付加」で1つ、「集計」で1つ、(必要なら)「帳票作成」で1つのジョブステップとなった。RDBのJOINに相当する「マッチング」が必要な場合それも1つのジョブステップとなる
- キーブレイクやマッチングをする前には、ソートのジョブステップが必要だった。ソートはワークの中間ファイルを使うため、入力中間ファイルと出力中間ファイルを同じにしても動いた
というように、RDBでいうと実行計画の1つ1つが、それぞれ独立したジョブステップとなる格好です。要するに、COBOLは細かいのです。そしてそれぞれに(現在で言うインターフェースとは異なりますが)中間ファイルを持ち、それらをいつでも検証できます。
しかし、COBOLには「何々を満たすすべての」を表す機能がありませんでした。それは、中間ファイルの何バイト目から何バイトが何の項目なのかについては、個々のコンパイルされたプログラムが独自に判定し、そのプログラムがどう中間ファイルを判定するのかはコンパイルした後では全く分からないからです(リフレクションの機能がありませんでした)。インターフェースの役回りである中間ファイルの解釈は個々のジョブステップで独自に行われ、しかもその解釈内容は外部から観測できないのです。
RDBは「何々を満たすすべての」を表す機能があります。(もっとも身近なDMLである“select文”だって、省略しないで書くと“select all文”に相当します)そして、全ての項目(列)は定義され登録されています。
自分はRDBを推進する役回りでなく、現行に拘泥する抵抗勢力の役回りにされていたので、なぜ「細かさと検証のしやすさ」を退け、「全てを表す機能」を求めたのか本当の理由は知りませんが(それこそ、RDBを推進した方々に公表してもらいたいことですが)、歴史的にはRDBが勝利者です。(「全て」を表せないと、ロックがきちんと出来ず、そのため、COBOLのバッチ処理は他のすべての処理を排除して行う必要がありましたので、それも一因かと思います)
●TDDの相性の悪い相手
RDBはもちろん、セキュリティソフトウェア、GUIなどのイベント駆動を原理とするものとTDDの相性は悪いと言われていますが(一部、Wikipediaの「テスト駆動開発」より引用)、それらすべてに「何々を満たすすべての」を表す機能があります。
つまり、TDDそのものが悪いのではなく、TDDのテストを表現する言語に「何々を満たす全ての」を表す機能がないのが問題かもしれません。もっとも、TDDは専用インターフェイスを使い、ハリウッドの原則にしたがって動作する部品を扱うので、「すべて」を表そうとすると矛盾を生じてしまうのかもしれません。
●「謎を解け。生きる力を示せ」
「すべて」を表現できると、宣言的な表現が可能になります。Prolog言語も宣言的な言語で、実用には至らず早世してしまいましたが、その言わんとする所を現実世界(現実プログラム)に接地させて見ると、少しは得るところがあるのかもしれません。(このコラムの(自転車云々を除く)元ネタは、よく考えたらこれだったのかもしれません)。
まぁ、今回はこんなところです。
●コラムのコメント欄の方針
コメントに対し、当意即妙の回答を、それなりのタイミングでする自信がありません。
ですので、このコラムで、
・わたしは基本的にコメントに答えない
・コメントを書く人は、回答がない前提で議論を進めていただく
とさせていただきます。
コメント
「~を満たすすべて」の数が圧倒的に異なりそうです。
select ... where ~ で得られる要素数は高々全レコードの数。
一方「0以上であるdoubleすべて」で得られるdoubleの数は...
K.Oumi
COBOLバッチ処理 と RDB(DML)の処理 という対比は面白いですね。
メモリDUMPを取って、HEXコードを読み、うわ~バウンダリずれてんじゃん(TT)とかやってた時分が懐かしく思われました。
QuestionDriven
(COBOLで)RDBを利用しないバッチ処理と、RDBを利用したバッチ処理とを対比した場合、後者は、DMLが意図した仕様を満たしているのかどうか、テスト駆動で検証しにくいという、主張だと理解しました。普通の手続き的に書くプログラムに比べて、DBが絡む部分はTDDで開発しにくいというのは、同意しますが、DMLの仕様を検証するためのテストデータを作ることで、問題なくTDDを適用できます。テストデータを作る部分の手間はかかりますが、それはRDBを使うか使わないかには依存していない要素ではないかと思います。