デバッガブルコード デバッガでトレースしやすい書き方

IDEのデバッガには、1行づつの「ステップ」、関数の中へ入る「ステップイン」、関数から戻る「ステップオーバー」、ローカル変数やメンバー変数の値を表示する機能があります。

「ステップイン」することなく、「ステップ」だけでどこを通っているかを確認できたり、関数の戻り値を確認できると、デバッグしやすいです。

式の一部分を指定して評価する機能もありますが、その操作をする必要があります。

以下の例は、javascriptです。

1行に1関数

1行に2つ以上の関数を書くと、2つめ以降の関数にステップインしにくいです。

デバッグしにくい例

func_a(); func_b();Code language: JavaScript (javascript)

func_b内にステップインしたいとします。1回目の「ステップイン」でfunc_aにステップインします。func_a内から「ステップオーバー」でこの行に戻ります。2回目の「ステップイン」でfunc_bにステップインします。

デバッグしやすい例

func_a();
func_b();Code language: JavaScript (javascript)

func_bの行で止まり「ステップイン」すると、func_b内にステップインします。

if文の条件と本体は別の行にする

if文の条件部分と本体を1行にすると、ifの本体を通ったのかわかりにくくなります。

デバッグしにくい例

if (1 < a) { func_a(); }
Code language: JavaScript (javascript)

ステップ実行すると、if文の条件のtrue/falseに関係なく、次の行に進みます。条件が成立したのか、しなかったのか、func_aを呼んだのか、呼ばなかったのか、がわかりづらいです。

デバッグしやすい例

if (1 < a) {
    func_a(x);
}Code language: JavaScript (javascript)

ステップ実行すると、if文がtrueなら、func_a()の行で止まります。

if文の条件を関数化する

if文の条件式が複雑だと、式内のどの条件がtrue/falseなのかわかりにくくなります。

デバッグしにくい例

if (x <= func_a() && y <= func_b()) {
}Code language: JavaScript (javascript)

このif文が予想外の結果だとします。func_a()が予想外なのか、func_b()が予想外なのか、わかりません。

デバッグしやすい例

さきほどのifの条件文そのままなら、

function is_xxxx(x, y) {
    var a = func_a();
    var f1 = (x <= a);
    if (!f1) {
        return false;
    }

    var b = func_b();
    var f2 = (y <= b);
    if (!f2) {
        return false;
    }

    return true;
}

if (is_xxxx(x, y)) {
}Code language: JavaScript (javascript)

次の書き方は、if文をなくせます。ただし、微妙に意味が違います。

function is_xxxx(x, y) {
    var a = func_a();
    var f1 = (x <= a);

    var b = func_b();
    var f2 = (y <= b);

    var f = f1 && f2;
    return f;
}

if (is_xxxx(x, y)) {
}Code language: JavaScript (javascript)

1つめの書き方では、x <= aが成立しないときは、func_b()は呼ばれません。

2つめの書き方では、x <= aの結果に関係なく、毎回func_b()が呼ばれます。

func_b()が副作用のある処理をしている場合、1つめの書き方と2つめの書き方で結果が違ってくるかもしれないので、要注意です。

関数の引数で、別の関数を呼ばない

関数Aの引数で関数Bを呼ぶと、関数Bの戻り値を確認しにくくなります。

デバッグしにくい例

func_a(func_b());Code language: JavaScript (javascript)

func_bの戻り値を確認するには、func_aにステップインする必要があります。
また「1行に関数を2つ以上書くと、2つめ以降の関数にステップインしにくい」と同じように、func_bにステップインするまでの操作が多くなります。

デバッグしやすい例

var b = func_b();
func_a(b);Code language: JavaScript (javascript)

func_a(b);の行で止まると、変数のbの値が表示されます。

return式で関数を呼ばない

return式で関数Aを呼ぶと、関数Aの戻り値を確認しにくい。

デバッグしにくい例

return func_a();
Code language: JavaScript (javascript)

func_a()の戻り値を確認できません。

デバッグしやすい例

var result = func_a();
return result;Code language: JavaScript (javascript)

return result;で止まると、変数resultの値が表示されます。

多くのlintでは、ローカル変数 result は不要、と警告されますが、気にしないことにします。

1式1関数

1行1関数のバリエーションです。右辺の式内で2つ以上の関数を呼ぶと、戻り値を確認しにくいです。

デバッグしにくい例

var x = func_a() + func_b();Code language: JavaScript (javascript)

func_aやfunc_bにステップインしないと、戻り値を確認できません。

デバッグしやすい例

var a = func_a();
var b = func_b();
var x = a + b;Code language: JavaScript (javascript)

2行目まで進めば、変数aの値が表示されます。3行目まで進めば、変数bの値も表示されます。

最近のAPIの名前は長いので、1行で複数のAPIを呼ぶと、1行が長くなってしまいます。長い1行にしても、折り返したとしても、読むとき追いかけにくいです。1つづつAPIの戻り値を変数に代入すると、読みやすくなります

関数の戻り値は変数に代入する

1行1関数、1式1関数の究極バリエーションです。

デバッグしにくい例

var y = 123 + func_a();Code language: JavaScript (javascript)

こんな簡単な式でも、これ以降の処理で想定外な動きがあると、次のようにyの値を表示して確認することになります。

var y = 123 + func_a();
console.log(y);Code language: JavaScript (javascript)

yの値を表示してみたけど、予想した値ではない場合、さらにfunc_a()の値を表示して確認することになります。

var a = func_a();
console.log(a);
var y = 123 + a;
console.log(y);Code language: JavaScript (javascript)

console.logを削除したものが、次の「デバッグしやすい例」です。

デバッグしやすい例

var a = func_a();
var y = 123 + a;Code language: JavaScript (javascript)

あやしい動きのとき、各行の直後にconsole.logを書くことで、変数の値を表示できます。デバッガでステップ実行したときも値を確認しやすいです

タイトルとURLをコピーしました