フューチャー技術ブログ

「リーダブルコード」を読んだ感想

はじめに

こんにちは、2022年4月新卒入社、流通製造グループ所属の藤戸四恩です。
秋のブログ週間2023の16本目は「リーダブルコード」を読んでみた感想です。

最近ソースコードを書くことが多く、可読性の高いコードを書くことを意識していますが、いまいち正解がわからず、積読してあった「リーダブルコード」を読んでみたので、印象に残った箇所を紹介したいと思います。

本書について

美しいコードを見ると感動する。優れたコードは見た瞬間に何をしているかが伝わってくる。そういうコードは使うのが楽しいし、自分のコードもそうあるべきだと思わせてくれる。本書の目的は、君のコードを良くすることだ。(本書「はじめに」より)
コードは理解しやすくなければならない。本書はこの原則を日々のコーディングの様々な場面に当てはめる方法を紹介します。名前の付け方、コメントの書き方など表面上の改善について。コードを動かすための制御フロー、論理式、変数などループとロジックについて。またコードを再構成するための方法。さらにテストの書き方などについて、楽しいイラストと共に説明しています。日本語版ではRubyやgroongaのコミッタとしても著名な須藤功平氏による解説を収録。
引用元 - O’REILLY

コードを書く上で命名の考え方や適切なコメントの書き方などが説明されており、総ページ数も200ページ少しと、ちょうどよい分量です。下記の通り15章から構成されています。

第1章 理解しやすいコード
第2章 名前に情報を詰め込む
第3章 誤解されない名前
第4章 美しさ
第5章 コメントすべきことを知る
第6章 コメントは正確で簡潔に
第7章 制御フローを読みやすくする
第8章 巨大な式を分割する
第9章 変数と読みやすさ
第10章 無関係な下位問題を抽出する
第11章 一度に1つのことを
第12章 コードに思いを込める
第13章 短いコードを書く
第14章 テストと読みやすさ
第15章 「分/時間カウンタ」を設計・実装する

印象に残った箇所

不要な単語を捨てる

名前に含まれる単語を削除しても情報が全く損なわれないこともある。例えば、ConvertToString()を短くしてToString()にしても、必要な情報は何も損なわれない。同様にDoServeLoop()をServeLoop()に変えても明確さは同じだ。

個人的には、型変換のメソッドを作成する際に、メソッド名にConvertをつけることが多かったため、命名するときに不要な情報を削除することも必要だと思いました。

一貫性のある簡潔な改行位置

以下は、Javaで書かれた任意の速度のネットワークに接続したときに、プログラムがどのように動くかを評価するコードのサンプルです。 また横幅80文字までというコーディング規約があるという想定です。
※書籍P.44参考

public class PerformanceTester {
public static final TcpConnectionSimulator wifi = new TcpConnectionSimulator(
500, //接続速度(Kbps)
80, //平均遅延時間(ms)
200, //遅延時間(ms)
1 //パケットロス率(%)
);
public static final TcpConnectionSimulator t3_fiber =
new TcpConnectionSimulator(
4500, //接続速度(Kbps)
10, //平均遅延時間(ms)
0, //遅延時間(ms)
0 //パケットロス率(%)
);
public static final TcpConnectionSimulator cell = new TcpConnectionSimulator(
100, //接続速度(Kbps)
400, //平均遅延時間(ms)
250, //遅延時間(ms)
5 //パケットロス率(%)
);
};

上記の実装だと、コーデンディング規約により、余計な改行が入っているかつ同じコメントがくり返されているため、下記のようにコメントを最上部に移動して、仮引数を一行で書くように書籍では提案されています。

public class PerformanceTester {
// TcpConnectionSimulator(throughput, latency, jitter, packet_loss)
// [kbps] [ms] [ms] [percent]
public static final TcpConnectionSimulator wifi =
new TcpConnectionSimulator(500, 80, 200, 1);

public static final TcpConnectionSimulator t3_fiber =
new TcpConnectionSimulator(4500, 10, 0, 0);

public static final TcpConnectionSimulator cell =
new TcpConnectionSimulator(100, 400, 250, 5);
};

個人的には、見やすくなったと感じたが、あまり見ないコメントの記載方法(自分が知らないだけかもしれないですが)だっため、

同じコメントが続くようなときは上記を参考にコメントを記載したいと思いました。

制御フローを読みやすくする

下記の二つの実装はどちらがよみやすいでしょうか?

// No.1
if (length >= 10)
// No.2
if (10  <= length)

書籍では、No.1の方が読みやすいと答える人が多いのではないかと言及されてます。
私も最初に読んだ際に直感的にNo.1の方が読みやすいなと感じました。
あくまで指針ですが、書籍では下記のように述べられていました。

条件式の左側には、「調査対象」の式。変化する。
条件式の右側には、「比較対象」の式。あまり変化しない。

実際に自分が実装する際には、無意識で上記のように実装していましたが、今後条件式を書く際には、上記のことを意識して実装しようと思いました。

ド・モルガンの法則を使う

論理式を等価な式に置き換える方法がある。

先日レビュアーから受けた指摘事項で、ド・モルガンの法則を使って修正したことがありました。

具体的には、型がbooleanである変数 isXXXがfalseかつとisYYYがfalseの時にのみ特定の処理を行いたく、下記のように実装していました。

var isXXX bool = false
var isYYY bool = false
if !(isXXX && isYYY) {
...
}

ド・モルガンの法則を使って、下の実装ように修正しました。

var isXXX bool = false
var isYYY bool = false
if !isXXX || !isYYY {
...
}

一般的には下の実装のほうが理解しやすいと言われています。

なぜ下の実装の方がわかりやすくなるのか考えてみました。

上の実装は、「isXXX と isYYY の両方が真でない場合に実行」となりisXXXとisYYYが複合的な論理演算なため理解しにくいですが、下の実装は、「isXXX が偽、または isYYY が偽の場合に実行」となり、isXXXとisYYYを分けて考えていることで単純な論理演算になるため分かやすくなるのではないかと思いました。

テストに優しい開発

コードにはテストしやすいものとそうでないものがある。テストしやすいコードには、明確なインタフェースがある。状態や「セットアップ」がない。検査するデータが隠されていない。あとでテストを書くとおもしろいことが起きる。テストしやすいようにコードを設計するのだ!このようにコードを書いていけば、いいコードが書けるようになる!

テストを意識して実装することでメリットはいろいろあるとは思いますが、個人的には、何を目的とするかをより明確な状態で実装できることが要因の一つではないかと思いました。

また、テスト駆動開発と呼ばれる、テストケースを先に実装することで、より堅牢でバグの少ないコードを生み出すことを目的としている手法をまだ体験したことがないので個人開発などする時に試してみたいと思いました。

解決策を言葉で説明する

書籍では、コメントを数行書いての説明でしたが、私自身は、コードを書いている際に詰まったり悩んだりした際に、上長の方に質問をしている途中で解決策が思いうかぶことが多々あり言語化することは大切だと思っております。

そのため質問をする前に仮想の相手を用いて質問をすることを実践しているのですが、このことは、「ラバーダッキング」や「ラバーダック・デバッグ」1と呼ばれる技法で、名称の由来は、プログラマーがラバーダック(ゴム製のアヒル)を持ち歩き、そのダックに対してコードを一行ずつ説明しながらデバッグを行いことが由来だそうです。

さいごに

自分が実装をする上でなんとなくやっていることが言語化されており、改めて実装をする上で意識すべきことを考えさせられる良い書籍でした。

次回は、最終回で、島ノ江励さんの「人を選ぶ技術」です。