プログラム悪戦苦闘日記

はてなダイアリーからの移行(遺物)

続 糞プログラムをやっつけろ!

 前回の続き。このすさまじいif - then -elseチェーン。なぜこんなことになってしまったのだろうか。
 
 一言で言えば、モジュール分割方向がまちがっているということだ。たとえば、こんなWebアプリケーションがあったとしよう。ある商品の取引で、画面からは「買い」「売り」「取消」の3つが入力できる。株の売買みたいなのを想像してくれれば良い。さて、このユーザー入力の「買い」「売り」「取消」の選択に対して、プログラムはどのように設計するだろうか。
 自分だったら、おそらく最初に「買い」「売り」「取消」の3つの処理に振り分け、それぞれ「買い」なら買いの処理特化した処理にすると思う。オブジェクト指向で言えば、ObjectFactory作って、「買い」「売り」「取消」共通のインターフェイスクラスを生成して…、といった感じか。ここでいいたいのは、処理の設計はユーザーの立場---クライアント側の立場で作られているということだ。あたりまえと思うかもしれない。なぜならクライアント側の処理だから、クライアント側の立場でプログラム設計したほうが、きっとスムーズにつくれるだろう。
  
 しかし、だ。ヘタレプログラムはこういう風にならない。先ほどの処理で、ユーザー入力にたいして注文テーブル(RDBを使っていれば)に1件追加するとした場合、注文テーブルに対する処理をサブルーチン化する。ユーザー入力だから入力チェックも必要だろう。入力チェック処理も、ひとつのサブルーチンにする。もちろんレスポンス画面用の文言を生成する部分も、1つのサブルーチンだろう。
 これの何が悪いのか? 個々のサブルーチンは「買い」「売り」「取消」に限らず同じサブルーチンが走る。だからこれらのブルーチンは次のようになっているだろう。

void init(int kbn) {
    switch(kbn) {
    case 1: // 買いの場合
        …買いの初期化
    case 2: // 売りの場合
        …売りの初期化
    case 3: // 取消の場合
        …取消の初期化
    }
}

void updateData(int kbn) {
    switch(kbn) {
    case 1: // 買いの場合
        …買いのテーブル更新
    case 2: // 売りの場合
        …売りのテーブル更新
    case 3: // 取消の場合
        …取消のテーブル更新
    }
}

…以下同様

 もちろん、これを呼び出すmain側も同様だ。

int main() {
    string msg;

    if( kbn == 1 ) {
        msg = "買い処理";
    }else if(kbn == 2) {
        msg = "売り処理";
    }else if(kbn == 3) {
        msg = "取消処理";
    }

    init(kbn);
    updateData(kbn);
    …
}

 これでめでたく、すべてのサブルーチンに同じ条件のif-elseが入る訳だ。これがよく見かける if-then-elseチェーンの正体である。
 なぜこのような分割をするのか。それは、サービスを提供する側からの視点でモジュール分割をしているからだ。注文テーブル更新があるなら、「注文テーブル更新」というサブルーチンを作る。レスポンスの文言生成なら、レスポンス文言サブルーチンを作る。その後で買い」「売り」「取消」の処理振り分けをするから、ほとんどすべてのサブルーチンで、同じif条件が入るわけだ。この考え方はまさしくホスト系の考え方であろう。

 こんな風に、クライアント側の立場で処理を分割しないから、ちょっと区分やコードが増えるだけで、簡単にifネストが深くなる。今の、いわゆる売買区分のほかに、午前/午後のような時間区分、すぐ処理するか予約かの処理区分みたいに、区分が3つになろうものなら、この地点でifは3重ネストである。いや、エラー処理もあるからifの4重ネストになる。これでさらにforループも加われば、インデントは5マスになる。あまりにインデントが深くなるので、インデントが半角空白1文字とか、ふざけたプログラムができるのもこんな背景があるからだろう。

 しかし、こんな考え方をするプログラマ(?)は、再教育できないようだ。機能ごとに処理を分ける(だって分けてるじゃん)とか、グローバル変数を減らせ(だって関数の引数がふえるじゃん)とか、なぜダメかを説明しても理解しない/しようとしない/できないのだ。ヤレヤレ、だからソコ Javaで数値をStringで比較するのヤメれ