リーダブルコードLT

276 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行程度 →設計・デバッグ・修正・テストを生き延びるコ ードの数は多くない ⇔成熟したライブラリのコードはこの試練を生き 延びたコード →ライブラリを使おう!!