条件分岐を状態遷移として解釈し、多段 if 文からプログラマーを救う

 PHP  条件分岐を状態遷移として解釈し、多段 if 文からプログラマーを救う はコメントを受け付けていません
3月 272012
 

多段 if 文はプログラムの可読性を損いバグの温床となるため、多くのプログラマーがそれによって悶え苦しみ、日夜命を落としています。
今日は、そんな憎き多段 if 文をやっつける方法を考えたいと思います。

たとえば、次のような条件分岐があるとします。
flowchart

これを、素直に次のようにコーディングします。

code 1

// $condition1~5 には条件式が入るとします。つまり boolean が入ります。
// $goal に数値を入れているだけですが、実際にはここに具体的な処理を置きます。

$goal = 0;

if ($condition1) {
	if ($condition2) {
		$goal = 1;
	} else {
		if ($condition4) {
			if ($condition5) {
				$goal = 1;
			} else {
				$goal = 2;
			}
		} else {
			$goal = 3;
		}
	}
} else {
	if ($condition3) {
		$goal = 4;
	} else {
		if ($condition4) {
			if ($condition5) {
				$goal = 1;
			} else {
				$goal = 2;
			}
		} else {
			$goal = 3;
		}
	}
}

多段 if 文の下地ができました。これを応用してネストを深くしていくとプログラマーを殺すことができます。
さらにコードの 7 ~ 15 行目と 21 ~ 29 行目が重複しています。
もし、条件4と条件5の間に新しい条件6が挿入される、つまり
・条件4が真のときに条件6の判定を行う
・条件6が真のときに条件5の判定を行う
という仕様変更が発生すると2カ所にまったく同じ修正が必要になり、プログラマーは死にます。

条件分岐を見るとついこのように書きたくなりますが、一寸ここでフローチャートを眺めてみると、だんだんと次のように見えてきます。
state transition diagram

なんと、状態遷移図っぽくなりましたね!
while 文の中で状態を遷移させていき、それぞれの状態を switch-case 文で記述するような感じで、この図の処理を次のようにコーディングしてみましょう。

code 2

$state = 1;
$goal = -1;
while ($goal == -1) {
	switch ($state) {
		case 1:
			if ($condition1) {
				$state = 2;
			} else {
				$state = 3;
			}
			break;
		case 2:
			if ($condition2) {
				$goal = 1;
			} else {
				$state = 4;
			}
			break;
		case 3:
			if ($condition3) {
				$goal = 4;
			} else {
				$state = 4;
			}
			break;
		case 4:
			if ($condition4) {
				$state = 5;
			} else {
				$goal = 3;
			}
			break;
		case 5:
			if ($condition5) {
				$goal = 1;
			} else {
				$goal = 2;
			}
			break;
		default:
			$goal = 0;
			break;
	}
}

それぞれの状態で条件の判定を行い、次に移るべき状態がある場合は $state に対応する値を代入し、ゴールに到達すれば $goal に値を代入してループを抜けます。
行数は増えましたが、シンプルな case 文によって構成されたコードになりネストが浅くなりました。また、条件4の重複もなくなりました。
条件4と条件5の間に条件6が挿入される場合は、$condition4 が true のときに $state に条件6を差す別の値を代入し、それに対応する case を書いてやればOKです。図でいうと矢印の行き先を変更するイメージです。

常にこの方法が適用できるわけではないかと思いますが、上手くいくこともあるんじゃないか、ということで。

 Posted by at 1:56 AM