🪓

『リーダブルコード』の要点&活用方法

2022/09/25に公開約4,000字

はじめに

今回の記事では『リーダブルコード』の要点と、実務で活用する方法を徹底解説する。この記事を読むことで、『リーダブルコード』の重要な部分と実務での活用方法を学べるだろう。

本記事で使うプログラムの言語はPythonを採用した。Pythonは文法がシンプルなので初心者でも学習コストが低く、プログラミングの入門としては最も相応しいからである。今回の記事の内容は良いプログラムを格上での重要な基本事項なので、どの言語でも役立つだろう。

1部:表面上の改善

名前に情報を詰め込む

プログラムに使われる名前は、主に次の5つの鉄則を守る必要がある。

  1. 明確な単語を選ぶ

例えば、getは具体的に何をしているのかがわからない。

def GetImage(url):
    ...

この場合だと、画像をダウンロードするのか、あるいはWebサイト上に表示されている画像のすべてを取得するのかまったくわからない。

インターネット経由で画像を取得するなら、DownloadImageのほうが明確である。変数や関数、クラスに名前を付けるならより明確かつ正確な名前にするべきだ

  1. 汎用的な名前を避ける

名前を付ける際には、その値をつける目的がはっきりわかるような名前を使ったほうがいいvalueargfooなどの空虚な名前はできるだけさけるべきである。

def func1(value):
    return value ** 2

上記のプログラムの場合はvalueの2乗の値を示しているので、関数名はfunc1ではなくsquareにするべきだ。

  1. 抽象的な名前よりも具体的な名前を使う

変数や関数はできるだけ具体的な名前を選ぶべきだ。

import socket, time

def server_can_start():
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client_socket.connect(('localhost', 5000))

上記のプログラムは、任意のTCP/IPポートをサーバがリッスンする(接続する)ための関数である。しかし、server_can_startは少し抽象的なのでlisten_on_portにするべきである。

  1. 接尾辞や接頭辞を使って情報を追加する

時間やバイト数のように計測できる値であれば変数名に単位を入れるべきである。

import time

def current_time():
    return round(time.time() * 1000)

例えば、上記のプログラムの場合は現在の時刻をミリ秒単位で取得するプログラムである。このプログラムではcurrent_timeと関数の名前しか表示されていないのでミリ秒単位で表現するためにはcurrent_milli_timeとミリ秒を表現する関数であることをきちんと示す必要がある。

あと、変数名に追加する情報は求めるべき値や危険・注意を換気する情報を追加するべきである。

from datetime import datetime

today = datetime.date.today()
date = datetime.date(2022, 12, 25)
during = date - today

print(during)

上記のプログラムは今年のクリスマスまでの日数を計算するものである。こちらの場合、dateはクリスマスの日付を指していることは理解できるだろう。しかし、特定の日数までを計算するためのプログラムにしたいならdatedate_targetにするべきだ

さらに、具体例で考えると暗号化されていないパスワードはplaintext_password表記するべき。変数名に情報や属性を追加する場合は、セキュリティのバグなどシステムに深刻な被害が出るところを中心に使うべきである。

  1. 名前の長さを決める

よい変数や関数の名前は「長い名前を避ける」という暗黙の了解がある。しかし、これを真に受けると名前がxなど1つの文字あるいは簡単な単語だけになってしまう。

識別子の「スコープ」(その名前がみえるコードの行数)が小さければ多くの情報を詰め込む必要はない。スコープ内のすべての情報(変数の型・初期値・破棄方法など)が理解できる場合は、変数の名前は短くて構わない。

if user:
    let v = user.info.name
    print(f'user name: {v}')

誤解されない名前を付ける

リストの出力結果を処理するプログラムがあるとする。

scores = [55, 70, 87, 66, 92, 80]

filtered = filter(lambda score: score >= 80, scores)

print(list(filtered))

上記のプログラムの場合、filteredがデータを取得しているのかあるいは除外しているのかわからない。filterのような複数の解釈ができる名前はできる限り避けるべきである。

上記のプログラムの場合、選択しているのでfilteredではなくselectedに名前を変更するべきだ。

コメントするべきことを知る

コメントを残すべきことは主に次の通りである。

  • コードの欠陥や改善点
  • コードに対する自分の意見

プログラマーがコメントする際によく使う記法は以下の通りである。

記法 典型的な意味
TODO: 後で着手する
FIXME: 既知の不具合があるコード
HACK: あまりきれいではない解決策
XXX: 大きな問題

2部:ループとロジックの単純化

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

条件やループなどの制御コードは、読み手が立ち止まらないように書く。

  • 条件式の引数の並び順は、左側に「調査対象」で右側に「比較対象」
if price > 1000:
    return price

if 1000 > price:
    return price
左側 右側
「調査対象」の式で変化する 「比較対象」の式であまり変化しない
  • if/elseブロックの並び順は否定形よりも肯定形
# 否定形
if not value:

# 肯定形
if value:
  • ネストを浅くする
if user_result == SUCCESS:
    if permission_result != SUCCESS:
        reply.error('reading permissions')
        reply.done()
        return False
    
    reply.error('')

else:
    reply.error('user_result')

上記のプログラムでは、最初の処理ではuser_resultpermisson_resultの値を覚える必要がある。またif()ブロックが終了する際に覚えておいた値を反対にしなければならない。

そのため、読み手には理解しづらいコードになっている。

上記のコードの場合はelseブロックを早めに返すことでネストの数を減らすことができる。

if user_result != SUCCESS:
    reply.error('user_result')
    reply.done()
    return

if permisson_result != SUCCESS:
    reply.error('reading permissions')
    reply.done()
    return False

reply.error('')
reply.done()

これで条件分岐の仕組みをより良く理解しやすくなった。

おわりに

今回の記事では、『リーダブルコード』の要点や実務での活用方法を簡潔に解説した。私自身も読み進めていく中では言語化できていない部分が数多くあった。

今回の記事では初心者向けに本書での特に重要なポイントを中心にまとめているので、本記事は『リーダブルコード』を読む時に再度更新する予定である。

良いコードを書くために必要なことは、何回も何回も練習するしかないということである。

参考文献

GitHubで編集を提案

Discussion

ログインするとコメントできます