プログラミング

『リーダブルコード』第Ⅱ部の要点と実例:ループとロジックの単純化

この記事は約5分で読めます。

こんにちは。

今回の記事では、『リーダブルコード』第Ⅱ部の要点と実例を紹介します。

第Ⅰ部の表面的な改善についてはこちらで紹介しています。

第Ⅱ部ではループとロジックの単純化について詳しく解説されています。具体的には、制御フローや論理式、変数などの改善方法が取り上げられています。

ループとロジックを単純化することで、頭に置いておかないといけない「精神的な荷物」を減らすことができます。ぜひ参考にしてみてください。

制御フローを読みやすくする(7章)

条件やループなどの制御フローは「自然」にする
読み手が立ち止まったり、読み返したりしないように書く

条件式の引数の並び順(7.1節)

条件式は「調査対象」を左に「比較対象」を右に書きましょう。

これは「あなたの身長が170cm以上ならば」が自然であるのに対して、「170cmがあなたの身長以下ならば」が不自然に感じられるのと同じことです。

Java
if (profile.height > 170) // 自然   「あなたの身長が170cm以上ならば」
if (170 < profile.height) // 不自然 「170cmがあなたの身長以下ならば」
左側右側
「調査対象」の式。変化する。「比較対象」の式。あまり変化しない。

条件式は肯定形を使う(7.2節)

「人は否定形を理解できない」と聞いたことがありませんか。
シロクマ効果/シロクマ実験と呼ばれますが、「シロクマのことを考えないでください」と言われるとシロクマについて意識してしまうというものです。

同じことが条件式にも言えます。
例えば以下の二つのコードは同じ処理ですが後者の方が理解しやすいと思います。

Java
// 否定形を使った場合
if (!isCompleted) {
  // タスクを実行する
}else {
  // タスクを終了する
}
Java
// 肯定形を使った場合
if (isCompleted) {
  // タスクを終了する
}else {
  // タスクを実行する
}

ネストを浅くする(7.7節)

ネスト(≒階層)が深いコードは理解しづらいものです。

本書ではネストが増えるごとに「精神的スタック」に条件をプッシュしなければならないと表現しています。
自分がどの条件ブロックにいるのかを常に確認しなければならないことが読みづらさの原因なのです。

ネストを浅くするためには「この関数の処理はここまでですよ」と示すガード節を使用して関数から早く返すことが効果的です。

以下はガード節を使用した場合と使用しない場合のサンプルコードです。

Java
// 割引後の価格を返すメソッド(ガード節を使用)
public double calculateDiscount(double price, double discountRate) {
    if (price <= 0) {
        return 0; // 価格が0円以下なら割引できないため返す(以下を実行しない)
    }
    
    if (discountRate <= 0) {
        return price; // 割引率が0以下なら割引できないため返す(以下を実行しない)
    }
    
    double discountedPrice = price - (price * discountRate);
    return discountedPrice;
}

すべてのifブロックがreturn文で終わっているため、精神的スタックからポップできる

Java
// 割引後の価格を返すメソッド(ガード節を使用しない)
public double calculateDiscount(double price, double discountRate) {
    if (price > 0) {
        if (discountRate > 0) {
            double discountedPrice = price - (price * discountRate);
            return discountedPrice; // ネストが深くなり可読性が落ちる
        }
    }
    
    return price;
}

「値段が0円以上」で「割引率が0以上」の時であることを覚えていないといけない。

巨大な式を分割する(8章)

巨大な式は飲み込みやすい(=理解しやすい)大きさに分割する

説明変数を使う(8.1節)

リーダブルコード第Ⅰ部の解説で説明したように、名前は短いコメントです。

複雑な処理を変数に入れることで、処理の目的が理解しやすくなります。

Java
if (line.split(":")[0].trim().equals("root")) {
    // ...
}
Java
// 説明変数を導入
String username = line.split(":")[0].trim();
if ("root".equals(username)) {
    // ...
}

要約変数を使う(8.2節)

複雑でない場合でも、長いコードはそれだけで面積を占有します。

大きなコードを小さな名前に置き換えて、管理や把握を容易にしましょう。

Java
if (request.getUser().getId() == document.getOwnerId()) {
    // 文章を編集できる
}

if (request.getUser().getId() != document.getOwnerId()) {
    // 文章は読み取り専用
}
Java
// 要約変数を導入
boolean ownDocument = request.getUser().getId() == document.getOwnerId();

if (ownDocument) {
    // 文章を編集できる
}

if (!ownDocument) {
    // 文章は読み取り専用
}

まとめ

今回は、精神的な荷物を減らすループとロジックの単純化についてまとめました。

具体的な例を挙げることで実践的な内容となっていますので、コーディングの際に参考にしてみてください。

コメント