リーダブルコードLT

-- Views

December 23, 25

スライド概要

エンジニア1年目の時にリーダブルコード輪読会で作成したスライド

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

ダウンロード

関連スライド

各ページのテキスト
1.

リーダブルコード The Art of Readable Code

2.

リーダブルコードとは ・O’REILLY発行、2012年に発売された本 ・エンジニアにとってバイブル的な良書 ・より良いコードを書くためのノウハウが書かれている ・プログラミングを始めたばかりの人が読んでも価値があるし、 ある程度経験を積んだ人が読んでも価値がある(と言われている) 全体像 ・一部:表面上の改善(関数・変数・クラス名→命名の改善) ・二部:ループロジックの単純化(制御フロー・式の分割・変数のスコープ→クラス単位での改善) ・三部:コードの再構成(オブジェクト指向、全体のロジック、API→全体の改善) ※後半にいくに連れてより上流の話になっていく ・四部:テストなど1-3部では扱いきれなかったもの(このスライドでは省略)

3.

「読みやすいコード」 に特化したテーマの本 読みやすい→理解しやすい→ 内部品質/外部品質ともに向上→ ”良い”コードへ

4.

第Ⅰ部 表面上の改善

5.

第1章 理解しやすいコード

6.

いちばん大切な原則 「理解しやすい」コードを書く ポイント 「理解しやすい」とは 他の人が最短時間で理解できる 理解:変更加える/バグ発見できるレベル 理解にかかる時間 > 短いコード 理解にかかる時間を短く→コードを短く の優先順位

7.

第2章 名前に情報を詰め込む

8.

名前をつける上で重要なこと① 「名前に情報を詰め込む」 →名前を見ただけで情報を読み取れる

9.

ポイント ①明確な単語を選ぶ ②汎用的な名前を使わない ③具体的な名前を使う ④接尾語や接頭辞を使って情報を追加する ⑤名前の長さを決める ⑥名前のフォーマットで情報を伝える

10.

①情報に名前を詰め込む ②汎用的な名前を使わない 1-1. 明確な単語を選ぶ 2-1. 汎用的な名前を避ける 明確で正確な言い回し 何にでも使える名前は避ける NG例) NG例) get → fetch、download retval → 目的や値を表すものへ size → height、memoryBytes tmp → 一時的な情報の保存の stop → kill、pause の場合は使って良い 2-2. ループイテレータ 複数あるときは説明的な名前に ループイテレータ:i、j、kなど 説明的な名前: i → user_i、member_i、など 参考:プログラミングでよく使う英単語のまとめ【随時更新】

11.

③具体的な名前を使う ④名前に情報を追加する 3-1. 具体的な名前を使う 名前と目的を一致させる 日本語でいう「〇〇が」「〇〇 を」の部分を名前で表す 例) disallow_evil_constructor →disallow_copy_and_assign 4-1. 値の単位の追加 変数名に単位を入れる 例) start → start_ms size → size_mb limit → limit_kbps delay → delay_secs angle → degrees_cw 4-2. 重要な属性の追加 意味を間違えたとき深刻な被害 が出るところに危険や注意を喚 起する情報の追加 例) データが安全でない → unsafe〇 〇、untrusted〇〇 暗号化すべき → plaintext_〇〇

12.

⑤名前の長さを決める 5-1. 名前の長さを決める 5-2. 短い名前でもいい いい名前 = 「長い名前を避ける」 スコープが小さければ短い名前 という暗黙の制約 でもいい →どこで折り合いをつけるべきか →名前が適用されているコード のガイドライン の行数が小さければ、コード理 解に必要な情報がすぐそばにあ るため 5-3. 単語補完機能を使う エディタの単語補完機能を使う ことで正確な単語が入力できる 例) InteliJ IDEA → Alt-/

13.

⑤名前の長さを決める ⑥フォーマットで情報を伝える 5-4. 省略形 5-5. 不要単語の切り捨て スラングな省略形以外は使わない 削除しても問題ない単語は切り 例) 捨てる evaluation → evail 例) document → doc DoServeLoop → ServeLoop NG例) ConvertToString → ToString BackEnd → BE 6-1. フォーマットで伝える フォーマットの違いで情報を伝 える 例) クラス名 → キャメルケース 変数名 → スネークケース HTML id → スネークケース class → ケバブケース

14.

第3章 誤解されない名前

15.

名前をつける上で重要なこと② 「誤解の生まれない名前」 →積極的に誤解を探して名前をつける

16.

ポイント ①例)filter()、Clip() ②限界値、範囲、排他的範囲、ブール値 ③ユーザーの期待に合わせる ④複数の名前の検討

17.

①例)filter()、Clip() 1-1. 例1)filter() あいまいな言葉 -選択か除外か分からない 例) 選択 → select() 除外 → exclude() 1-2. 例2)clip() あいまいな言葉 -動作が特定できない 例) 最後の文字から削除 → remove 最大文字まで切り詰め → truncate

18.

②限界値/範囲/排他的範囲/ブール値 2-1. 限界値 限界値を含めるとき(~以上、~ 以下) →minとmaxを使う 例) max_〇〇 min_〇〇 2-2. 範囲 範囲を示すもの →firstとlastを使う (min、maxでも良い) 例) set.PrintKeys(first="a", last="e") print int_rang(min=2, max=4) 2-3. 包含/排他的範囲 最初は含むが最後の要素は含ま ないもの →beginとendを使う 例) 何時から何時まで開催など

19.

②限界値/範囲/排他的範囲/ブール値 ③ユーザーの期待に合わせる ④複数の名前の検討 2-4. ブール値 ブール値を返す関数 →trueとfalseの意味が通るよう な名前にする 例) is、has、can、shouldなどを つけて分かりやすく 3-1. ユーザーの期待 4-1. 複数の名前の検討 エンジニア業界のスラングを理解して 類義語を調べて比較する 名前に反映させる → 適切な名前を選ぶ 例) get() 、〇〇.list()、〇〇size() → 軽量なメソッド generate〇〇()、〇〇.countSize() → 重量なメソッド

20.

第4章 美しさ

21.

コードの「美しさ」の改善 優れたソースコードは「目に優しい」 →ほとんどの時間はコードを読む時間

22.

つの原則 ①読み手が慣れてるパターン 一貫性のあるレイアウト ②似ているコードは 似ているように見せる ③関連するコードを まとめてブロックにする →一貫性のあるスタイルは 「正しい」スタイルより大切 3 ポイント

23.

①一貫性のあるレイアウト 1-1. 一貫性のある改行位置 1-2. 一貫性のある並び 改行位置はコード全体を通して 一連のコードでは同じ並び順に 同じ位置にする する →全体のシルエット的に見やす いコードにする 1-3. シルエットの統一 複数のコードブロックで同じよ うなことをしていたら、シルエ ットも同じようなものにする

24.

②列の整理 2-1. メソッドを使った整列 2-2. 縦の線をまっすぐに メソッドに処理をまとめること 縦の線を揃えることでタイプミ で、コードが見やすくなる場合 スを見つけやすい、楽に流し読 がある みできる

25.

③ブロック分け 3-1. 宣言をブロックにまとめる 単位を作ってブロックにまとめる 例) // ユーザーの友達取得 friends = user.friends() メールアドレスのインポート // contacts = import_contacts(user.emai) 3-2. 段落に分割する コードを段落分けする →空行を使って、ブロックを論 理的な段落に分ける

26.

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

27.

コメントの目的 →コードの意図を,読み手にわかりやすく,で きるだけ短時間で伝えること

28.

コメント すべきで ないこと ①コードからすぐにわかること ②ひどい命名やコードを補うためのコメント →コメントで補うのではなく,コードを直 せ!

29.

コメント すべきこと ①実装中の自分の考えや気付き ②読み手の理解を促進するためのメモ

30.

①自分の考えや気付き 1-1. 採用理由 他にも実装方法やライブラリが ある中,なぜ他の方法ではな く,この手法を採用しているの か 1-2. 開発中の気付き 1-3. 定数の設定理由 開発中に気付いた欠陥などを, 定数や値の設定理由 「Todo:」,「Fix:」を使っ てメモしておく(チームで事前 ex)なぜこのリストは1000まで に統一しておく) 入るようにしているのか,など

31.

②読み手の理解を促進するためのメモ 2-1. 詰まりそうな箇所 読み手が「えっ?」っとなりそ うなところをあらかじめ予想し て,コメントで書いておく 2-2. プログラムの概要 2-3. ロジックの説明 ファイルやクラスには全体像を ロジックは改行を入れるなどし コメント てブロックに分け,各ブロック にはコメントを付けて概要を補 ex)これは〇〇をするためのクラ 足 スです,使い方は△です //ユーザーID取得 //IDを用いてDBからデータ取得

32.

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

33.

伝えたい内容が齟齬なく, 的確に伝わるようなコメントを書け

34.

ポイント ①コメントは領域に対する情報の比率が高く なければならない ②情報密度の高い言葉を使う(専門用語、コ ンピュータ用語)

35.

コメントの情報密度を 高めるために 代名詞は使わない 1-1. 1-2. 動作を正確に記述 1-3. 専門用語を用いる データをキャッシュに入れる.ただ //このファイルに含まれる行数 抽象度を下げるために,コンピ し,先に”その”サイズをチェックす を返す ュータの専門用語を用いる る ↓ //このファイルに含まれる改行 ・ヒューリスティック →データ?キャッシュ? 文字('/n')を数える ・ブルートフォース ・ナイーブソリューション

36.

第Ⅱ部 ループとロジックの単純化

37.

第7章 制御フローを読みやすく

38.

制御フローを読みやすく 制御フロー = 条件式やループ →制御フローがないor自然なコードは読みやすい

39.

ポイント ①条件式の引数の並び順 ②if/elseのルール ③三項演算子、do/whileループ、goto ④ネストを浅くする ⑤早めに返す ⑥実行の流れを追いやすく

40.

①条件式の引数の並び順 ②if/elseのルール 1-1. 条件式の引数 左:調査対象(~はの部分) 右:比較対象 例) if(length >=10) → ◯ if(10 <= length) → × のルール 2-1. if/else ①肯定形 > 非定形 - !はなるべく使わない ②単純な条件を先へ -ifとelseが同じ画面に表示される ③目立つ条件を先へ

41.

③三項演算子、do/whileループ、goto 3-1. 三項演算子 三項演算子は基本使わない (条件 ? a : b) →1行で書いた方が読みやすい 場合のみ使う ループ 3-2. do/while 3-3. goto do/while while goto → ループは基本使わない ループで書く は基本使わない →普通のループに置き換える

42.

④ネストを浅くする ⑤早めに返す 4-1. ネストを浅くする 4-2. ループ内部のネストの削除 ネストの深いコードは読みにくい ループ内部でネストしているコー →1つのネスト内に書くコードは ドは読みにくい なるべく簡潔になるようにする。 →書き方変える/ループを分ける。 5-1. 早めに返す 関数で早めに返してあげると、 ネストを削除したりコードをク リーンにしたりできる。

43.

⑥実行の流れを追いやすく 6-1. 実行の流れを追えるか 実行の流れを追いやすいコード を書く →追うのが難しくなる構成要素 をなるべく少なくする。 例) スレッド、シグナル/組み込み ハンドラ、無名関数、仮想メソ ッド 6-2. 新鮮な目で見る 変更を加えるときはコードを新 鮮な目で見る →書いている人にとっては簡潔 な変更でも初めて見た人には分 かりづらいときがある。

44.

第8章 巨大な式を分割する

45.

巨大な式の分割 コードの式が大きいと理解が難しい →コードの「塊」は読みやすい大きさに分割する

46.

ポイント ①説明変数 ②要約変数 ③ド・モルガンの法則 ④短絡評価

47.

①説明変数 ②要約変数 1-1. 説明変数 式の意味を説明する変数 例) if line.split[0].strip() == "root": ↓ username = line.split[0].strip() if username == "root": 1-2. 要約変数 式を代入しておく変数 →大きなコードの塊は変数に代 入すると読みやすくなる 例) if(request.user.id == document.owner_id) ↓ user_owns_document = ...

48.

③ド・モルガン法則 ④短絡評価 3-1. ド・モルガン法則 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) を分配してand/orを反転させる not 4-1. 短略評価 短略評価: 論理演算子(&&, ||)は左辺を評 価した時点で式全体の真偽値が 決定し右辺を評価する必要がな いもの →長すぎる式の場合は、変数を 使って分割する

49.

第9章 変数と読みやすさ

50.

変数は必要な場合のみ定義する 変数のスコープはできる限り小さく する

51.

ポイント ①変数を削除する ②変数のスコープを小さくする ③変数は一度だけ書き込む

52.

①変数を削除する 1-1. 役に立たない一時変数 now = datetime.datetime.now() root_message.last_view_time = now ←nowっていう一時変数いらない 直接代入しちゃえばいい 1-2. 制御フロー変数の削除 boolean done= false; 条件 while( && !done){ ... if(...) { done=true; continue; } } 制御フロー変数doneの削除 → while(条件 ){ ... if(...) { break; } }

53.

②変数のスコープを小さく する(変数はできる限り近 くで宣言する) 2-1. グローバル変数 極力使用を避ける 色々なところから値が変更され てプログラムが複雑になり、処 理の流れを追いづらくなる 問題があったときに原因が何な のか特定しづらくなる 想定外のところで値が変更され てしまう 2-2. インスタンス変数 2-3. ローカル変数 関数の引数で足りるデータ 必要になったときに,使う直前で初 をインスタンス変数にしな めて定義して、スコープを最小にす い るよう心がける。 加工した値をインスタンス 変数に保持しない 説明変数は積極的に使って良い プログラムで取得できる情 報をインスタンス変数に保 持しない 参考:qiita「良いコードの書き方」

54.

③変数は一度だけ書き込む 3-1. 1度だけ書き込む と 3-2. const final 変数の値が変わると,その経過を追 可能であれば,積極的にconstや 跡しなければならない→読みにくい finalなどのイミュータブルな宣言を 行う

55.

第Ⅲ部 コードの再構成

56.

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

57.

汎用コードを分離する プロジェクト固有のコードから汎用コードを分離

58.

ポイント ①プロジェクト固有のコードから汎用コードを分離する 理由: 1.呼び出し側のコードが簡潔になる 2.再利用できる 3.改善が楽になる ②純粋なユーティリティコード -プログラムの核となる基本的なタスクは汎用コードと して分離する 例)ファイルの読み込み、ハッシュテーブルの使用

59.

第11章 一度に1つのことを

60.

つずつタスクを行うようにする 1 一度に複数のことをするコードは理解しにくい

61.

ポイント ①やり方 1.コードが行なっている「タスク」を全て列挙 2.タスクをできるだけ異なる関数/クラスへ分割する ②例 ・変数への代入 ・関数の分割(1度に1つのことを行う) ・クラスの分割 ・ディレクトリ構成

62.

第12章 コードに思いを込める

63.

コードを書く前に,プログラムの ことを簡単な言葉で説明する ソースコードはプログラムの動作を説明する 最も大切な手段 →できる限り簡潔でわかりやすく書くべき

64.

ポイント ①ロジックを明確に説明する ②その説明に合わせてコードを書く

65.

①ロジックを明確に説明する 簡潔な言葉で説明 1-1. いきなりプログラムを書き始め るのではなく,まずは自分が実 装したいロジックを,簡潔な言 葉で説明してみる (コメントで書くのもあり) 1-2. ラバーダッキング 自分が実装しようとしていること を,アヒルのおもちゃやクマの人 形に向かって説明してみる →プログラムのを言葉にすること で,ロジックが整理され,明確な 形になる

66.

②説明に合わせてコードを書く 2-3. ライブラリを知る キーワードやフレーズ 2-2. 簡潔にコードを書く に注目する ロジックの説明を実装する再,難 簡潔なコードを書くために,ライ しい/不自然なコードではなく, ブラリが何を提供してくれてるか 「権限あり なし」,「時間 銘柄 価 2-1. / / / 格」など,ロジックを構成する上で重要 となりそうなキーワードに着目する 他者が読みやすいよう,できるだ を知ることが欠かせない.ライブ ラリの内容を知る時間を作り,実 け簡潔で自然なコードを書く 際に利用する

67.

第13章 短いコードを書く

68.

冒険.興奮. ジェダイはそんなものを求めては おらん 新しいコードにはテストや保守が必要 →新しいコードは極力書かないようにすべき

69.

ポイント ①不必要な機能をプロダクトから削除する ②最も簡単に要求を満たす実装を考える ③コードを小さく保つ

70.

①不必要な機能をプロダクトから 削除する 必要機能の見積もり 1-1. プロジェクトを開始するときに は,カッコいい機能のことを考 え,そしてプロダクトに書かせ ない機能を過剰に見積もりがち 1-2. 実装労力の過小評価 プログラマは, ・実装コスト ・保守/文章化コスト を過小評価しがち 1-3. ミニマル 不必要な機能をプロダクトから削 除する.過剰な機能は持たせな い.ミニマルなプロダクトを維持 sする

71.

②最も簡単に要求を満たす実装 方法を考える 2-1. 要求の正しい評価 すべてのプログラムが,高速 で,100%正しくて,あらゆる ケースをうまく処理できる必要 はない.どの程度の要求を満た せばいいのか,実装前に適切に 評価する 2-2. 最も簡単なプログラム 要求を正しく見積もった上で,そ の要求を満たす最もシンプルで簡 単なプログラムを実装する.難し いロジックを組む必要はない

72.

③コードを小さく保つ 3-1. ユーティリティ化 3-2. コードの削除 3-3. 外部リソースの利用 重複コードを汎用的な「ユーテ 「コードをせっかく書いたから, - 言語の標準ライブラリ ィリティコード」にする 削除したくない」そんなのどうで - 外部API ≒オブジェクト指向 もいいんだよ! - Unixコード を用い,コード量を減らす 未使用のコードは無用の機能はど "たまには標準ライブラリのすべ んどん削除しよう ての関数を15分かけて読んでみ ろ!"

73.

(参考)平均的なソフトウェアエンジニアが 1日に書く”出荷用の”コードは10行程度 →設計・デバッグ・修正・テストを生き延びるコ ードの数は多くない ⇔成熟したライブラリのコードはこの試練を生き 延びたコード →ライブラリを使おう!!