2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

リーダブルコード

数年前(プログラミング初心者の時)に読んだので、もう一度振り返りでサクッとまとめてみました。

1. 理解しやすいコード

コードは理解しやすくなければならない。

読みやすさの定理

コードは他の人が最短時間で理解できるように書かなければならない。

2. 名前に情報を詰め込む

名前に情報を詰め込む

tmp, size, getなどはわかりにくいので❌

明確な単語を選ぶ

e.g.
getPage() → downloadPage()
size() → height(), numNodes, memoryBytes()
stop() → kill()(取り消しができないほど重い操作), Pause()(後から再開できる場合)

単語 代替案
send deliver, dispatch, announce, distribute, route
find search,extract, locate, recover
start launc, create, begin, open
make create, set up, build, generate, compose, add, new

tempやretvalなどの汎用的な名前を避ける

retvalは「これは戻り値です」以外の情報はない。

ループイテレータは、汎用的だが、clubs[i].members[k] == users[j]clubs[ci].members[mi] == users[ui] のようにバグが分かりやすいようにする。

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

ServerCanStart() → CanListenOnPort()
DISALLOW_EVIL_CONSTRUCTORS → DISALLOW_COPY_AND_ASSIGN
--run_locally() → --extra_logging() / --use_local_database

名前に情報を追加する

  • 単位
    string id = "af84ef845cd8"string hex_id = "af84ef845cd8"
関数の仮引数 単位を追加した仮引数
start(int delay) start(int delay_secs)
CreateCache(int size) CreateCache(int size_mb)
ThrottkeDownload(float limit) ThrottkeDownload(float max_kbps)
Rotate(float angle) Rotate(float degrees_cw)
  • その他の重要な属性
関数の仮引数 単位を追加した仮引数
プレインテキストのpassword plaintext_password
文字コードutf-8のhtml htmx_utf8
入力されたdataをURLエンコードした data_url_enc
エスケープされていないコメント  unescaped_comment

ハンガリアン記法 : 変数名の接頭辞にをつける(e.g. pLast, psqBuffer, cch, mpcopx)

名前の長さを決める

  • 長い名前を避ける(ダメではない)
  • スコープが小さければ短くてok
  • 頭文字と省略形を避ける(特にプロジェクト固有の省略形はダメ!BEManagerなど新メンバは理解できない)
  • 不要な単語を捨てる(e.g. ConvertToString()→ToString())

名前のフォーマットで情報を伝える

  • 定数 CONSTANT_NAME→kConstantName (#defineマクロと区別するため)
  • メンバ変数 val_(アンダースコアをつける)
  • jQueryのライブライ関数を代入した変数 $val ($をつける)

3. 誤解されない名前

名前が他の意味と間違えられることはないだろうか?と何度も自問自答する。

filter

results = data.filter("age <= 20")とすると、resultsは20歳以下のオブジェクトか20歳以下ではないオブジェクトかわからない。→ select() にする。除外の場合は exclude() にする。

Clip(text, lenght)

段落の内容を切り抜く関数clip(text, lenght)は以下2通りの意味で捉えられる。

  • 最後からlength文字を削除する → remove() にする
  • 最大lenght文字まで切り詰める → truncate() にする

また、length → max_length → max_charsにする。

限界値の定義

CART_TOO_BIG_LIMITだと未満か以下か不明 → MAX_ITEMS_IN_CARTにする

ブール値の名前

bool read_password = trueは以下2つの解釈がある。

  • パスワードをこれから読み取る必要がある
  • パスワードをすでに読み取っている
    → need_password, user_is_authenticated

boolはis, has, can, shouldなどの接頭辞をつけると分かりやすい(e.g. spaceLeft()→ hasSpaceLeft())
また、名前は否定形より肯定形の方がいい(e.g. disable_ssl=false→ use_ssl=true)

ユーザの機体に合わせる

一般的にgetやsizeは軽量なイメージ

  • get()はjavaでは、メンバの値を返すだけの軽量アクセサ。コストが多い場合等はcomputeXXX()を使う
  • list.size()→O(1)ではない場合などcoutSize()やcoutElementsにする。size()はO(1)

複数の名前を検討する

e.g. 再利用したい実験のIDの名前

  • template → 曖昧。これはテンプレートか、このテンプレートを使っているわからない。
  • resuse → 100回再利用できると誤認識の可能性
  • copy → 100回コピーできると誤認識の可能性、100回目のコピー
  • inherit → 継承

4. 美しさ

  • 読み手が慣れているパターンと一貫性のあるレイアウトを用いる
  • 似ているコードは似ているように見せる
  • 関連するコードをまとめてブロックにする

最近は、フォーマッターなどあるので不要。

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

コメントの目的は、書き手の糸を読み手に知らせることである

コメントするべきではないこと

  • コードからすぐにわかることをコメントしない。

  • コメントのためのコメントをしない

  • 酷い名前はコメントをつけずに名前を変える : コメントに頼らない。関数名などを修正する

通常は、補助的なコメント(コードの読みにくさを補うコメント)は不要である。
優れたコード > 酷いコード + 優れたコメント

自分の考えを記録する

  • 監督のコメンタリーを入れる : e.g.// このデータだとハッシュテーブルよりバイナリツリーの方が40%速かったなど。
  • コードの欠陥にコメントをつける : // TODO: もっと高速なアルゴリズムを使うなど
  • 定数にコメントをつける : e.g. const int MAX_RSS_SUBSCRIPTIONS = 1000; // 合理的な限界値。人間はこんなに読めない
アノテーション 意味
TODO あとで追加、修正するべき機能がある。
FIXME 既知の不具合があるコード。修正が必要。
HACK あまりきれいじゃないコード。リファクタリングが必要。
XXX 危険!動くけどなぜうごくかわからない。
REVIEW 意図した通りに動くか、見直す必要がある。
OPTIMIZE 無駄が多く、ボトルネックになっている。
CHANGED コードをどのように変更したか。
NOTE なぜ、こうなったという情報を残す。
WARNING 注意が必要。

読み手の立場になって考える

  • 質問されそうなことを想像する
  • ハマりそうな罠を告知する
  • 全体像のコメント
  • 要約コメント

コメントには、WHAT・HOWではなく、WHYをかくようにすることが多い。ただし、コードを理解するのに役立てば、どれを書いてもいい。

ライターズブロックを乗り越える

適切な言い回しにする。(e.g. やばい → 注意 )

6. コメントは正確で簡潔に

コメントは領域に対する情報の比率が高くなければいけない

  • コメントを簡潔にしておく
  • 曖昧な代名詞を避ける
  • 歯切れの悪い文章を磨く
  • 関数の動作を正確に記述する
  • 入出力のコーナーケースに実例を使う
  • コードの意図を書く : 詳細レベルではなく、高レベルで記述する
  • 名前付き引数コメント : よく話からに引数にはインラインコメントを使う
  • 情報密度の高い言葉を使う : 正規化、ナイーブソリューション、ヒューリスティックなど

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

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

条件式の引数の並び順

  • 左側 : 調査対象の式。変化する。
  • 右側 : 比較対象の式。あまり変化しない。

if/elseブロックの並び順

  • 条件は否定型よりも肯定型を使う。⭕️ if(debug) ❌ if(!debug)
  • 単純な条件を先に書く。
  • 関心を引く条件や目立つ条件を先に書く。

ただし、これは状況によって変える。

三項演算子

基本的にはif/elseを使う。
三項演算子は簡潔になるときだけ使う。

do/whileループを避ける

do-statementはエラーや混乱の原因になることが多いので、避けた方が良い。条件は前もって書いた方が良い。

関数から早く返す

関数で複数のreturnを使い早く返すことは良いこと。

悪名高きgoto

基本的にgotoは使わない方がいい。

goto exit;だけ例外。

ネストを浅くする

  • 早めに返してネストを削除する(ガード節)
  • ループ内部のネストを削除する

実行の流れを追う

構成要素 高レベルの流れが不明瞭になる理由
スレッド どのコードがいつ実行されるかわからない
シグナル・割り込みハンドラ 他のコードが実行される可能性がある
例外 いろんな関数呼び出しが終了しようとする
関数ポインタと無名関数 コンパイル時に判別ができないので、どのコードが実行されるか不明
仮想メソッド object.virtualMethod()は未知のサブクラスのコードを呼び出す可能性がある

これらの構成要素を使うと、コードを追うのが難しくなる。
ただし、これらを使うことで、コードが読みやすくなったり、冗長性が低くなくなったりする可能性もあるので、うまく使う。

8.巨大な式を分割する

巨大な式は飲み込みやすい大きさに分割する。

説明変数

説明変数を使って式を分割する。

要約変数

要約変数 : 大きなコードの塊を小さな名前に置き換えて、管理や把握を簡単にする変数。

if (request.user.id == document.owner_id){
}
if (request.user.id != document.owner_id){
}

これを要約変数を使うと以下のようになる。

final boolean user_owns_document = (request.user.id == document.owner_id);
if (user_owns_document){
}
if (!user_owns_document){
}

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

not (a or b or c) ↔️ (not a) and (not b) and (not c)
not (a and b and c) ↔️ (not a) or (not b) or (not c)

短絡評価の悪用

頭がいいコードに気をつける。あとで他の人がコードを読みときに分かりにくくなる。

巨大な文を分割する

DRY原則など

指揮を簡潔にするもう1つの創造的な方法

マクロを利用すると良いときがある。

9.変数と読みやすさ

  • 変数が多いと変数を追跡するのが難しくなる
  • 変数のスコープが大きいとスコープを把握する時間が長くなる
  • 変数が頻繁に変更されると現在の値を把握するのが難しくなる

変数を削除する

  • 役に立たない一時変数
  • 中間結果を削除する
  • 制御フロー変数を削除する

変数のスコープを縮める

変数のことが見えるコード行数をできるだけ減らす

  • 特にグローバル変数は避ける。
  • メソッドをできるだけstaticにする
  • 大きなクラスを小さなクラスに分割する
  • 定義の位置を下げる

変数は一度だけ書き込む

変数を操作する場所が増えると、現在値の判断が難しくなる。

constfinalを使い、イミュータブルにする。

10.無関係の下位問題を抽出する

無関係の下位問題を積極的に見つけて抽出する。

  • 関数やコードブロックを見て、このコードの高レベルの目標は何か?と自問する
  • コードの各行に対して、高レベルの目標に直接的に効果があるのか?あるいは、無関係の下位問題を解決しているのか?と自問する
  • 無関係の下位問題を解決しているコードが相当量あれば、それらを抽出して別の問題にする

Amazon.co.jp: リファクタリング(第2版): 既存のコードを安全に改善する (OBJECT TECHNOLOGY SERIES) : Martin Fowler 著, 児玉 公信, 友野 晶夫, 平澤 章, 梅澤 真史: 本

Amazon.co.jp: ケント・ベックのSmalltalkベストプラクティス・パター: シンプル・デザインへの宝石集 : ケント ベック, Beck,Kent, 真史, 梅沢, 誠, 皆川, 直樹, 小黒, みどり, 森島: 本

11. 一度に1つのことを

コードは1つずつタスクを行うようにしなければならない

  • タスクは小さくする
  • オブジェクトから値を抽出する
  • 一度に1つのタスクを適用する

12. コードに思いを込める

おばあちゃんがわかるように説明できなければ、本当に理解したとは言えない by アルバート・アインシュタイン

  1. コードの動作を簡単な言葉で同僚にもわかるように説明する
  2. その説明の中で使っているキーワードやフレーズに注目する
  3. その説明に合わせてコードを書く
  • ロジックを明確に説明する
  • ライブラリを知る
  • この手法を大きな問題に適用する
  • 解決策を言葉で説明する
  • 手法を再起的に適用する

ラバーダッキング : 問題を声に出して説明するだけで、解決策が見つかること

13. 短いコードを書く

最も読みやすいコードは何も書かれていないコードだ。

  • 必要ない機能の実装について悩まない
  • 質問と要求の分割
  • コードを小さく保つ
    • 汎用的なユーティリティコードを作って重複コードを削除する
    • 未使用のコードや無用の機能を削除する
    • プロジェクトをサブプロジェクトに分割する
    • コードの重量を意識する。軽量で機敏にしておく
  • 身近なライブラリに親しむ
  • unixツールボックスを使う

14. テストと読みやすさ

  • テストを読みやすくて保守しやすいものにする
  • テストを読みやすくする : 大切ではない詳細はユーザから隠し、大切な詳細は目立つようにする
  • 最小のテストを作る
  • 独自のミニ言語を実装する
  • エラーメッセージを読みやすくする
  • もっといいassert()を使う
  • エラーメッセージはできるだけ役に立つようにする
  • テストの適切な入力値を選択する
  • 入力値を単純化する
  • 1つの機能に複数のテスト
  • テストの機能に名前をつける
  • テスト駆動開発(TDD)
2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?