ExcelでDSLしてみよう
それでは実際にExcelを使った外部DSLっぽいミニフレームワークを作成してみましょう。通常のプログラムやXMLと比較して、DSLの表現方法としてのExcelには次のメリットとデメリットがあります。
メリット
Excelは表ですので、表形式のデータを見たまま表現できるのが一番のメリットです。また、セルに色をつけたりコメントも自由自在に書けるなど視覚的な表現力も高いといえます。もとになる仕様書などがそもそもExcelだったりしますので、加工したりコピー&ペーストして利用することも簡単です。
デメリット
デメリットとしては、変更履歴の差分(diff)が取りにくい点が挙げられます。また、テキストエディタで編集できない、セルに構文を含んだ長い文字を書くのは辛いなどの問題点もあります。
今回のお題
今回のお題は「固定長電文解析処理」です。メインフレームの時代より「固定長フォーマット」の電文が利用され続けてきました。現在でも「固定長電文」はデータ交換のメジャーなフォーマットです。Javaには固定長電文を解析する標準APIはありませんので、自作する必要があります。そこで今回は、固定長電文の解析にExcelによるDSLを適用します。
例で使用する「固定長電文」は図1のようなものです。読みやすいように改行を入れていますが、実際はすべてつながって1行になります。
表1が電文のフォーマットやルールを記述した「電文仕様書」です。電文仕様書には、先頭から8バイトが送信日、9バイト目からの10バイトがユーザ名といった情報や、空白除去、データの型といった変換ルールの仕様などが記載されています。
この仕様書をもとに固定長電文を解析してから、目的の処理(たとえば「DBにデータ追加する」など)を行います。
表1 電文仕様書
No | データ名称 | 開始 | 長さ | 変換ルール |
1 | 送信日 | 1 | 8 | Date型に変換 (yyyyMMdd) |
2 | ユーザ名 | 9 | 10 | 両端空白除去(trim) |
3 | メールアドレス | 19 | 20 | 両端空白除去(trim) |
4 | ポイント | 39 | 5 | 両端空白除去(trim)、 Integer型に変換 |
Step1:ベタなコードで書いてみる
まずはDSLなどを使わずにべたにコードを書くとリスト1のようになります。
まず①で固定長電文のファイルを読み込み、バイト配列に変換しています。バイト配列はそのままでは「配列の一部を取り出す操作」などが面倒です。ですので、②③で「ByteArray」という自作ユーティリティクラスを使用して、バイト配列を扱いやすくラップしています。
parseメソッドはデータの読み込み処理です。データがなくなるまで読み込み処理を繰り返します。④でそれぞれのデータ項目のサイズだけ順番にデータを読み込んでいます。同時にDate型への変換(toDate)や両端文字列の除去(trim)、Integer型への変換(toInteger)を行っています。⑤で1件分のデータを標準出力へ出力しています。実際に実行してみると図2の出力が得られます。
考察1:MessageParserがクラスになっているのはなぜ?
MessageParserクラスは連載の前回でもお勧めしたインナークラスとして実装されています。これはバイト配列の現在の読み込み位置である変数indexをフィールドとして保持したいからです。
もしクラス化せずにローカル変数で保持しようとすると、以下のようなコードになるはずです。
また、別の方法としては自作のByteArrayクラス自体に「現在の読み取り位置」を保持する機構を持たせるという手もあります。その場合、呼び出し元は「何バイト目まで読み込んだのか?」というのは知る必要がなく、「次の8バイトを読み込む」「まだ読み込めるか?」などを問い合わせするだけでよくなり、よりカプセル化が進んだ状態となります。その際クラス名は「ByteArray」ではなく「ByteReader」に、メソッド名「getString」は「readAsString」にするなど、より適切な名前にリファクタリングしたほうがよいでしょう(名前付けに関しては本誌Vol.45の本連載第2回を参照してください)。コードは次のようになるでしょう。
考察2:これからどうする?
リスト1はベタに書かれていて、シンプルでわかりやすいコードになっていますね。今回指定された仕様ぐらいであれば、このコードは十分に「良いコード」です。めでたしめでたし……だと話が終わってしまうので、もう少しいろいろな状況を考えてみましょう。
今回は電文内のデータ項目は4種類(送信日、ユーザ名、メールアドレス、ポイント)ですが、データ項目が100を超えるような電文はざらにあります。そのような場合、④のデータを読み取る処理が100行以上続くことになり、可読性がかなり悪くなりそうです。仕様書を見ながらプログラムへ反映するのも大変でしょう。ですのでStep2では、「Excelの仕様書」を読み込んで、その仕様書に定義されたとおりに処理が実行されるように書き換えてみましょう。