Kikuchy's Second Memory

技術のこととか、技術以外のこととか、思ったことを書き留めています。

読みやすいコードとワーキングメモリ

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

3年ばかり前に『リーダブルコード』という本を図書館で借りて読んだ(ので詳細な内容を忘れてしまった)のですが、
こういった本が出版されるほどに、エンジニアは読みやすいコードを求めているのだと感じます。

コードの品質については様々な要素があります。その中に、可読性というものも入るでしょう。

可読性の高さは、コードの理解を容易にします。
コードの理解が容易であれば、変更も加えやすく、拡張もしやすくなる可能性があります。

その可読性も、いくつかの要素から成り立っているようです。

  • トークン単位 ... 命名は妥当か。値やメソッド名、クラス名から挙動などの想像がつくか。
  • 式単位 ... プリミティブな命令の単位。どのような命令なのか、見てわかるか。
  • 制御文、スコープ、メソッド単位 ... この文やスコープの意味するところは何なのかを、想像できるか。
  • クラス、モジュール、ファイル単位 ... 理解しようという気になるか。


これらすべての根底には、読者にかかる認知的負荷を低くできるかどうか、という問題が横たわっています。
認知的負荷というのは、一時記憶(ワーキングメモリ)の圧迫と言い換えられるでしょう。
要は、覚えておけるかどうか、です。

人間の一時記憶は 7±2 個のことがら(チャンク)しか貯蔵できないと言われています(マジカルナンバー・セブン)。
コードを書いている本人ならともかく、読む人はたくさんのことがらを記憶できません。

しかし、記憶できることがら(チャンク)は、意味のある一かたまりであれば、ある程度記憶がしやすくなるのです。

例えば数字列。

097153754865

これを覚えろ、と言われてもなかなか難しいかもしれません。
数字一つひとつを記憶しようとしても、12個あるわけですから、7±2 を明らかに逸脱しています。

では、こちらは?

0971 - 5375 - 4865

まだ覚えやすいですね。
例えばこれが紙に書かれた電話番号だったとして、番号を打ち込むのが少しは楽になりそうです。
一つの塊は4桁ですから、塊ごとに記憶するのは容易。
その塊も3つですから、数字を一つずつ覚えこもうとするよりかははるかに楽になります。

コードについても同じことが言えます。
なるべく、単純化・抽象化した塊で捉えることで理解が楽になるわけです。

// 昔メンテナンスしていたプロダクトでこんな感じのコードを見たことがあります
boolean ret;
if (isSucceeded(results)) {
    if (results.targets.size() > 0) {
        ret = true;
    } else {
        ret = false;
    }
} else {
    ret = false;
}
return ret;



// 変数が減れば、頭に入れておくべきことが減ります。
// 行数や制御構文が減れば、「このまとまりが何をしているのか」を少ない労力で考えられます。
return isSucceeded(results) && (results.targets.size() > 0);

ある特定の操作をメソッドに抽出する、というのも有効な手段です。

// ナイーブに実装すると
int index = -1;
for (int i = 0; i < elements.size(); i++) {
    Element e = elements.get(i);
    if (e.id != target.id && e.name.equals(target.name)) {
        index = i;
        break;
    }
}




// 特定の操作をメソッドに置き換える -> 抽象化する ということです
int index = indexOf(elements, () -> e.id != target.id && e.name.equals(target.name));
/* (ラムダ式部は、以下の省略形です)
new Predicate<Element> {
    boolean test(Element e) {
        return e.id != target.id && e.name.equals(target.name);
    }
}
*/

// 最終的な行数は増えますが、こちらのメソッドの実装は頭に入れなくても良いでしょう
// 「特定の条件を満たす要素が格納されているインデックスを探し出す」という役割だけを覚えておけばいいのですから
public boolean <T>  indexOf(List<T> list, Predicate<T> predicate) {
    for (int i = 0; i < elements.size(); i++) {
        T t = list.get(i);
        if (predicate.test(t)) {
            return i;
        }
    return -1;
}


単純なことを複雑に実現することは楽なのです。
なぜなら「実現する」という最低限がすでにできているのに、「単純化する」という+αに頭を使わなくて済むから。
書いた行数が増えれば、なんだか仕事した感じにもなりますし。

単純なことを単純に実現する
できないことではないのですが、疲れるタスクです。
書いた行数が成果とされるような(Web系でそんな職場は存在しないと信じたいですが)ところだと、マイナスに働くかも。
ですが、書いた自分がやっておけば、今後のこされたコードに触る人全員の労力を削減できます。
また、それだけ考えることができる人だと周囲も認識してくれますし、それが自信をつけることにも繋がるでしょう。

読む人に苦労をかけないコードを残していきたいものですね。