
本稿では、可読性と保守性を主題として、条件式を中心にソースコード(以下コード)を読みやすくする方法についてご紹介します。
条件式にまつわる問題は、条件がしばしば複雑で構造的でなく可読性が低くなることです。たくさんの研究が示すとおり、複雑な条件式は、しばしばプログラムの問題となります。読みやすい条件式を記述することは、プログラマにもコードの正当性をレビューする人にとっても、メリットがあります。
まず、条件式とは何なんでしょう?それは、ifやwhileそしてその他の多くの構文で、何をするべきかを決定するために使用される表現です。
下記の例では、a && b という式が条件式です。これは、aとbの両方が真であることを単純にチェックします。
if (a && b)
{
printf("Both a and b were true\n");
}
それでは、可読性が低い、実際の実装を見てみましょう。このコード片は、私が何年にもわたって見てきたものと比較して、まだ読みやすい部類のものであることに注意してください。本稿の後半で、この実装を読みやすく書き換えていきます。
if (info.isNameCandidate && isType (token, TOKEN_PAREN_NAME) &&
! st->gotParenName &&
(! info.isParamList || ! st->haveQualifyingName ||
c == '(' ||
(c == '=' && st->implementation != IMP_VIRTUAL) ||
(st->declaration == DECL_NONE && isOneOf (c, ",;"))))
方法1: 演算子を使ったインデントでソースコードの可読性をあげる
最初の改善方法としては、カッコとインデントを使っての可読性の向上です。
各演算子(&&など)がしばしば行の末尾または中間に置かれることが一つ問題としてあります。こうなると、論理構造がわかりにくくなります。
一例として、可読性の低い構造を示します。
if ((FirstCondition && SecondCondition) || ThirdCondition ||
FourthCondition)
{
}
このコードを演算子が構造を示すよう下記に書き換えられます。先ほどと異なり、大きく条件が3つあること、そしてその1つは詳細な2つの条件があることが、明らかです。
if ( ( FirstCondition
&& SecondCondition)
|| ThirdCondition
|| FourthCondition)
{
}
先述の複雑な例を再度考えてみましょう。上記のテクニックを使うと、下記のように書き換えられます。
if ( info.isNameCandidate
&& isType (token, TOKEN_PAREN_NAME)
&& ! st->gotParenName
&& ( ! info.isParamList
|| ! st->haveQualifyingName
|| c == '('
|| ( c == '='
&& st->implementation != IMP_VIRTUAL)
|| ( st->declaration == DECL_NONE
&& isOneOf (c, ",;"))))
たとえ、さっと見ただけでも、条件式の大枠の構造が理解できるでしょう。トップレベルは、4つの論理積をとり、4つめの条件は論理和でさらに詳細な条件を確認しています。
常にカッコを使う
もう1つのトラブルの原因は、異なる論理演算が正しくカッコを使用されずに適用されることです。まずは例を見てみましょう。ぱっと見ると、下記は全て論理積のようですが、実は論理和が含まれます。
if (FirstCondition && SecondCondition || ThirdCondition && FourthCondition)
まずは正しくインデントをすると次のようになります。
if ( FirstCondition
&& SecondCondition
|| ThirdCondition
&& FourthCondition)
論理積と論理和をカッコなしで混在させることは、最低でも警告されるべきで、火災警報が鳴ってもおかしくないレベルのものです。 チームのエキスパートが何と言おうと、このような記述は不具合への道です!
カッコを追加し(論理積が論理和より強く結合することも覚えておいてください)、インデントもしましょう。以下が結果です。
if ( ( FirstCondition
&& SecondCondition)
|| ( ThirdCondition
&& FourthCondition))
ついに、論理構造がはっきりとしました。元々は、2つの条件の論理和を見ようとしていて、詳細な条件として論理積を使っていたのです。
このインデントのテクニックは、何も、論理和と論理積に限定されるものではありません。==などの比較演算子も、算術演算子も、同様にインデントしましょう。
if ( player_passed_the_test
&& ( high_score
>= ( index
* ( the_alpha_value
+ a_gamma_value))))
方法2: 制御用の変数を用いて保守性をあげる
状況によっては、1つの複雑な式で条件を記述するのが難しい場合もあるでしょう。そのような状況では、制御用の変数を用いるのが便利です。制御用の変数を用いると、コードは2パートに分割されます。1つ目は、制御用の変数に値を代入して特定の文を実行するかを決定するパートです。2つ目は、特定の文を実行するパートです。
下記が一例です。(C言語の場合、stdbool.hのインクルードが必要です)
/* Start by assuming that we should perform The Action. */
bool doIt = true;
if (FirstCondition)
{
/*
* When First Condition is true, we should not perform The Action
* (unless decided otherwise below).
*/
doIt = false;
}
if (SecondCondition)
{
/*
* If the Second Condition is true, we decide that The Action
* should be performed.
*/
doIt = true;
}
if (ThirdCondition)
{
/*
* Of course, The Action should not be performed if the Third
* Action is met.
*/
doIt = false;
}
if (doIt)
{
/* Perform the action. */
PerformTheAction();
}
ソースコードを読んだ人がわかるように
本稿では、条件式にまつわる可読性と保守性をあげる2つのテクニックをご紹介しました。それらのテクニックは、既存コードのリファクタリングと新規コードの実装で適用できます。量産したコードの問題を解決する最善の方法は、そのコードが何をどのように行っているのかを、ソースコードを読んだ人にわかるようにすることです。