kazasiki's blog

プログラミングとかVRゲームとか

この分岐をどうやって書く?

1.今回の問題

複雑な分岐の書き方で迷ったことはありますか?
今回皆さんに試しに書いていただきたい分岐の処理はこちら。簡単だけどちょっと面倒くさいものです。
f:id:kazasiki:20130615100507j:plain
2,3分で書いたので少し汚いけど、許して(^_^;)

文章で説明すると、変数flagの値によって処理を分岐させます。
flagの値が1の場合は、judge()関数を動かし、結果がtrueであれば、doSomething()を実行します。
flagの値が2の場合は、judge()関数を動かさず、ただdoSomething()を実行します。
flagの値が上記以外の場合はなにもしません。
簡単な処理です。

2.実際に書いてみましょう

さて、では以降の文章を読む前に、皆さん各自実際にプログラムとしてこの処理を書いてみましょう。
チラシの裏にペンで書いてもいいし、テキストエディタに書いてもいいです。
簡単でしょ? 1分もかからないかと思います。
書いた?
ちゃんと書いた?
書いた人は下に読み進めて行きましょう。

3.答え合わせ

さて、どんな感じになりましたかね?

ちなみにこんなかんじの回答パターンを用意してみました。

private void hoge(int flag) {
    // flag = 1 or 2 or 3

    // Pattern1
    if (flag == 1) {
        if (judge()) {
            doSomething();
        }
    } else if (flag == 2) {
        doSomething();
    }

    // Pattern2
    boolean temp = false;
    if (flag == 1) {
        temp = judge();
    }
    if (temp || flag == 2) {
        doSomething();
    }

    // Pattern3
    if ((flag == 1 && judge()) || flag == 2) {
        doSomething();
    }

    // Pattern4
    switch (flag) {
    case 1:
        if (!judge()){
            break;
        }
    case 2:
        doSomething();
    default:
        break;
    }
}

細かいバリエーションは抜かして思いつく限りのパターンを書いてみたのですが、皆さんの回答はどれかに当てはまってでしょうか?
動作確認はしたので、正しくは動くかと思います。

4.どれが好き?

上に挙げた4つのパターンはそれぞれ長所と短所のようなものがあります。
読みやすいかどうかとか、正規化されている(処理が冗長でない)かとか、汎用性があるかとか。

まず、パターン1はそれぞれのflagの値による動作が追いやすいです。
プログラムの中でflagが処理制御用の変数であるならば、それ毎に処理が追えたほうが良いという考え方もあります。
ただし、見て分かる通りdoSomething()が2回書かれており、無駄があります。あと「要するにflagが2の時はjudge()しなくいい」という見方はしにくいですね。

次に、パターン2は処理に無駄はありませんが、パッと見た感じがわかりにくいかなと思います。何より一度判定結果をboolean変数に代入しているので、直感的でなくなってしまっています。

パターン3も処理に無駄はありませんし、行数も少なく、見た感じは一番スマートですね。
ただ、一行に固めて書いた影響で、flagとなっている変数名が長い名前に変更になった場合などに見難くなる可能性が高いです。judge()に関しても、本当はもっと長い関数名にするでしょうし、実際にはflagはintではなくStringでequals()を使った判定をするかもしれません。例えば、

// Pattern3
if ((myMostFavoriteArtist.equals("ももいろクローバーZ") && judgeDoSomethingRunning()) || (myMostFavoriteArtist.equals("私立恵比寿中学") ) {
    doSomething();
}

みたいな。

パターン4はswitch文を使った用法で、直感的に理解しやすいかもしれません。
ただ、このプログラムを理解するには「javaのswitch文がラベルジャンプ的に動作していて、breakしない場合はどんどん下の行を実行していく」ということを知っている必要があります。switch文は一般的(?)には、case毎に処理を記述してbreakするような用法が多く、このことを理解していない人は意外に多いです。プログラミング言語によってはこういうふうになってないものもあった気がします。
また、javaのswitch文はswitch条件に入れられる変数の型が決まっています。私の記憶だとenumとString以外の参照変数は入れれません。実数も入れれません。条件も必ず一致条件で、比較演算子は使えません。今回のケースは「int型の一致判定」だったので、switch文を使えましたが、言語的にswitch文は案外制約が多いのです。

5.まとめ

分岐処理はいろんな書き方があって、それぞれ長所短所があって、適切に使いわけなければいけません。
コードは皆の共同所有物なので、他の人のレベルに合わせる必要もあります。
設計上の思想や業務上の要件によって、どう書いたほうが見やすいか、変更に耐えうるかも変わります。
万能の回答はありませんが、各自気をつけて書いて行きましょう。
この記事が何かのヒントになれば幸いです。