ソフトウェアの品質 可読性
可読性の高いコードとは何か。
時々耳にするのがフォーマットを共通にするという考え方である。
C言語ではその昔、次のようなことが議論されたことがある。
if
文の {
の前で改行するか否か
if
文の (
の前に空白は必要か否か
else if
は else
の後で改行するか否か
return 0;
か return(0);
か
etc etc
皆が同じ書き方にして統一する方が誰が見ても理解しやすいだろうということがその根底にあるのだろうが、可読性というものは、所謂、そういったような書式の話ではない。
1行に複数の文を書いたっていいじゃないか
私は次のようなコードを書いたことがある。
#include <signal.h>
char* str_sig(int sig)
{
char* str = "";
switch (sig)
{
//case SIGABND: str = "SIGABND"; break;
case SIGABRT: str = "SIGABRT"; break;
case SIGALRM: str = "SIGALRM"; break;
case SIGBUS: str = "SIGBUS"; break;
case SIGFPE: str = "SIGFPE"; break;
case SIGHUP: str = "SIGHUP"; break;
case SIGILL: str = "SIGILL"; break;
case SIGINT: str = "SIGINT"; break;
case SIGKILL: str = "SIGKILL"; break;
case SIGPIPE: str = "SIGPIPE"; break;
case SIGPOLL: str = "SIGPOLL"; break;
case SIGPROF: str = "SIGPROF"; break;
case SIGQUIT: str = "SIGQUIT"; break;
case SIGSEGV: str = "SIGSEGV"; break;
case SIGSYS: str = "SIGSYS"; break;
case SIGTERM: str = "SIGTERM"; break;
case SIGTRAP: str = "SIGTRAP"; break;
case SIGURG: str = "SIGURG"; break;
case SIGUSR1: str = "SIGUSR1"; break;
case SIGUSR2: str = "SIGUSR2"; break;
case SIGVTALRM: str = "SIGVTALRM"; break;
case SIGXCPU: str = "SIGXCPU"; break;
case SIGXFSZ: str = "SIGXFSZ"; break;
case SIGCHLD: str = "SIGCHLD"; break;
//case SIGIO: str = "SIGIO"; break;
//case SIGIOERR: str = "SIGIOERR"; break;
case SIGWINCH: str = "SIGWINCH"; break;
case SIGSTOP: str = "SIGSTOP"; break;
case SIGTSTP: str = "SIGTSTP"; break;
//case SIGTSTP: str = "SIGTSTP"; break;
case SIGTTIN: str = "SIGTTIN"; break;
case SIGTTOU: str = "SIGTTOU"; break;
case SIGCONT: str = "SIGCONT"; break;
}
return str;
}
C言語のシグナルを文字列に変換する関数だ。
普通ならこう書くのかもしれない。
#include <signal.h>
char* str_sig(int sig)
{
char* str = "";
switch (sig)
{
case SIGABRT:
str = "SIGABRT";
break;
case SIGALRM:
str = "SIGALRM";
break;
〈中略〉
case SIGCONT:
str = "SIGCONT";
break;
}
return str;
}
普通はこのようにcase
文を3行に渡って記載することが多いのではないだろうか。
私はこれを1行で書いたわけである。
各行のカラム位置を揃えれば、ちょうど表のように見える。
そういう視覚効果を狙って書いたものだ。
どうだろう。どちらがわかりやすいだろうか。どちらが直感的に把握しやすいだろうか。
わかりやすいということは間違いも入り込みにくい。
チェックもしやすい。
その結果、品質、生産性に寄与することも少なくないはずだ。
関数の途中でリターンしたっていいじゃないか
関数の途中でリターンするのはよくない、という原則。
これを破った方がむしろわかりやすいというケースがある。
例えば、こんな風に。
bool stack_push(SCB* _scb, int data)
{
if (_scb == NULL)
{
return false;
}
if (_scb->sp < 0)
{
return false;
}
_scb->stack[_scb->sp] = data;
(_scb->sp)--;
return true;
}
エラーケースを最初に除去してしまう方法である。
途中リターンを許容しないとこんな感じになる。
bool stack_push(SCB* _scb, int data)
{
bool b_ok = false;
if (_scb != NULL)
{
if (0 <= _scb->sp)
{
_scb->stack[_scb->sp] = data;
(_scb->sp)--;
b_ok = true;
}
}
return b_ok;
}
個人的には if
文がネストすると、それでもう萎えそうになる。
プログラムというのは条件と処理を決めていく作業だ。文章で書けば箇条書きににしたり、表にしたりということが向いている。すなわち、コードも箇条書きや表のようなイメージで書くとわかりやすいということだ。
エラーケースをはじいてゆくような書き方は箇条書きに似ている。一方、条件文のネストは箇条書きとは相容れない。
不等号は右側に大きい数値を書こうよ
引数「a」の範囲が「7~13」であるとして、範囲チェックの条件文をこんな風に書く人がいる。
void boo(int a)
{
if ((a >= 7) && (a <= 13))
{
printf("a = %d\n", a);
}
}
比較したい数値を必ず左側に書くやり方だ。
でも、これってわかりにくくね?
「a」の範囲が「7~13」であるということを数式で書くと、こうなるはずだ。
数式の場合は、大きい数値を右側に書く。
「a」は「7」より大きくて「13」より小さいわけだから、「7」「a」「13」の順で書くことになる。
条件文もこれにならって、次のように書いた方がわかりやすいはずだ。
void boo(int a)
{
if ((7 <= a) && (a <= 13))
{
printf("a = %d\n", a);
}
}
これで、「a」が「7~13」の範囲であると理解しやすくなる。
プログラムもまた言語である。
だから、1行1行を「読む」ものでもある。
とは言うものの、小説とは違って行間を読む必要はない。
できれば、行間を読む労力はなくしたい。
一文字一文字を追いかけて読まずとも、視覚的に理解できるのであれば、それに越したことはない。
今回は以上の3つを挙げたが、他にもあるのかもしれない。
異論、反論、あるいは他の方法など、意見歓迎である。
Discussion