Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

68
Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

description

2014/05/15日に行ったリーダブルコードの勉強会で使用したスライドです。

Transcript of Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

Page 1: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

Readable Code 勉強会 in 仙台

Part 1: 名前を改善する

Page 2: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

自己紹介{ name: “河野 康隆”,

company: “株式会社セカイネット”,

facebook: “kouno.yasutaka”,

favorite: [ “GAE/J”, “Spock”, “D3.js”, ],

}

Page 3: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

AGENDA

1.なぜ、読めるコードを書くべきなのか?

2.この勉強会の方針

3.まずは、名前を改善しよう

Page 4: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

1.なぜ、読めるコードを書くべきなのか?

Page 5: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

価値のあるコード

実際に動くコード=

Page 6: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

稼働しているけれど…•ドキュメントとコードに整合性がない

•機能追加やバグの修正が困難

•運用が困難

•そもそも、

なぜ動いているのか分からない\(^o^)/

Page 7: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

ゆえに

Page 8: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

コスト>パフォーマンス

Page 9: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

動くから価値があるとはいえない

Page 10: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

価値のあるコードとは• 正しく動作する

• 機能を追加・変更しやすい

• 容易にメンテナンスできる

Page 11: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

価値のあるコードを書くために

デザインパターン

テスト駆動開発

アルゴリズム

フレームワークアジャイル開発

Page 12: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

ぼっちに優しくない

Page 13: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

明日から誰でもすぐに実践できて、

上流・下流・上級者・初心者を問わず、

しかも効果が高い方法

Page 14: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

リーダブルコード

Page 15: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

読みやすいコードは 価値のあるコードの第一歩

• 間違えにくいし、間違いを見つけやすい。 • 無駄なコードやコメント、ドキュメントに

時間・思考を奪われない。 • 優れた設計やテストのしやすさにつながる • ◯◯さんじゃないとわからないコードが無くなる

Page 16: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

おすすめはPDF版  タブレットで読みやすいB5本 どの現場にも持ち出せる !

オライリーのEbook Storeで販売中

https://www.oreilly.co.jp/books/9784873115658/

これを読もう!

Page 17: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

2.この勉強会の方針

Page 18: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

この勉強会の方針• 未読者の予習・既読者の復習とする

• なるべくJava言語を使って説明していく

• 自分のコードの改善点を知る

• たまに指しますので、快くお応えください

Page 19: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

3.理解しやすいコード

Page 20: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

どちらが優れている?// パターン1int i = 0;while (list.size() > i) { Object element = list.get(i); System.out.println(element); i++;}!// パターン2for (Object element : list) { System.out.println(element);}

Page 21: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

どちらが優れている?// exponent 指数, mantissa 仮数// パターン1if (exponent >= 0) { return mantissa * (1 << exponent);} else { return mantissa / (1 << -exponent);}!// パターン2return exponent >= 0 ? mantissa * (1 << exponent) : mantissa / (1 << -exponent);

Page 22: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

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

POINT

Page 23: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

コードが短いことは良いことだ。 でも、絶対に良いとは限らない

Page 24: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

どちらが優れている?// パターン1int i = 0;while (list.size() > i) { Object element = list.get(i); System.out.println(element); i++;}!// パターン2for (Object element : list) { System.out.println(element);}

Page 25: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

どちらが優れている?// exponent 指数, mantissa 仮数// パターン1if (exponent >= 0) { return mantissa * (1 << exponent);} else { return mantissa / (1 << -exponent);}!// パターン2return exponent >= 0 ? mantissa * (1 << exponent) : mantissa / (1 << -exponent);

Page 26: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

短ければいいわけじゃない

// これは何やってるのかよくわからないassert (((bucket = findBucket(key)) != null) || !bucket.isOccupied());!!// でも、これならわかるbucket = findBucket(key);if (bucket != null) assert (!bucket.isOccupied());

Page 27: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

コメントがあるおかげで理解できる

//"hash = (65599 * hash) + c" の高速版hash = (hash << 6) + (hash << 16) - hash + c;

Page 28: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

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

大事なことなので二回言いました

Page 29: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

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

Page 30: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

書いてみよう!次の仕様のメソッド定義を書いてみてください。 コメントはいりません。 !

• 指定されたURLにあるファイルを取得する • 取得ファイルサイズの上限が指定できる。 • タイムアウト時間を指定できる。

!• 質問があれば受け付けます。

Page 31: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

無難な名前を濫用してませんか?

getset

bufresult

tmp size

str

list

map

hoge

obj1, obj2

Page 32: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

/** Webサイトのインターフェース. */interface WebSite { Page getPage();}

/** Webサイトのインターフェース(改善案). */interface WebSite { Page fetchPage();}

どこから取ってくるの?

Page 33: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

/** 二分木クラス. */class BinaryTree { int size();}

/** 二分木クラス(改善案). */class BinaryTree { int height(); int numNodes();}

なんのサイズ?高さ?

ノードの数?

Page 34: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

/** スレッドクラス. */class Thread { void stop();}

/** スレッドクラス(改善案). */class Thread { void kill(); void pause(); // resume()で処理を再開}

強制終了? 一時停止?

Page 35: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

目的に即した 明瞭な単語を選ぼう

POINT

Page 36: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

シソーラス(類語辞典)を使うhttp://thesaurus.com/

Page 37: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

やり過ぎに注意しよう

class String { // たとえば、Stringにこんなメソッドがあったら混乱してしまう。 public String[] split(String regex); public String[] divide(String regex); public String[] partition(String regex); // "boo:and:foo".split(":") > ["boo", "and", "foo"] // "boo:and:foo".divide(":") > ["boo", ":", "and", ":", "foo"] // "boo:and:foo".partition(":") > ["boo", ":and", ":foo"]}

Page 38: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

空虚な名前を避ける

Page 39: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

2点間の距離を求めるコードを書く

a

bc 考え方:

ピタゴラスの定理 c2 = a2 + b2

座標1

座標2

Page 40: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

// n次元上の二点間の距離を求める。例:2次元の場合 pointA = [5, 3]public static double distance(double[] pointA, double[] pointB) { // 二点の次元が異なる場合は非数を返す if (pointA.length != pointB.length) return Double.NaN; double result = 0.0; for (int i = 0; i < pointA.length; i++) { result += Math.pow(pointA[i] - pointB[i], 2); } return result;}

2点間の距離を求めるコード

平方(C2)のまま!

Page 41: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

//n次元上の二点間の距離を求める。(修正版)public static double distance(double[] pointA, double[] pointB) { // 二点の次元が異なる場合は非数を返す if (pointA.length != pointB.length) return Double.NaN; double squareDistance = 0.0; // 二点間の距離の平方 for (int i = 0; i < pointA.length; i++) { squareDistance += Math.pow(pointA[i] - pointB[i], 2); } return Math.sqrt(squareDistance); // 二点間の距離の平方根を返す}

2点間の距離を求めるコード(修正版)

平方のままならバグとわかる

Page 42: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

resultのような 空虚な名前を避ける

POINT

Page 43: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

このtmpは問題ない

// 左右の値を入れ替えるif (right < left) { tmp = right; right = left; left = tmp;}

「一時的な値の置き場」という意図が明確

Page 44: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

このtmpはダメ// ユーザー情報を文字列化String tmp = user.name();tmp += " " + user.phone_number();tmp += " " + user.email();...template.put("user_info", tmp);

tmpではなく、userInfo等にすべき

もし”file_info”等に 間違えても気づかない

Page 45: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

ループイテレーターi, j, k, iterは、短いけどよく意味を表している。

for (int i = 0; i < clubs.size(); i++) for (int j = 0; j < clubs[i].members.size(); j++) for (int k = 0; k < users.size(); k++) if (clubs[i].members[k] == users[j]) System.out.println("user[" + j + "] is in club[" + i + "]");

バグ:kとjが逆

でも、次のようなケースではどうだろう

Page 46: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

ループイテレーター改善:なんのイテレーターか明確にしよう

// ci = clubs_iter, mi = members_iter, ui = user_iterfor (int ci = 0; ci < clubs.size(); ci++) for (int mi = 0; mi < clubs[ci].members.size(); mi++) for (int ui = 0; ui < users.size(); ui++) if (clubs[ci].members[ui] == users[mi]) System.out.println("user[" + mi + "] is in club[" + ci + "]");

バグがすぐに解る

Page 47: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

tmp, it, resultのような汎用的な名前を使うときは、相応の理由を用意する。

POINT

Page 48: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

具体的な名前をつかう

Page 49: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

/** 任意のTCP/IPポートをサーバがリッスンできるか確認する. */public boolean serverCanStart() { ... }

機能に対し、メソッド名が抽象的

/** 任意のTCP/IPポートをサーバがリッスンできるか確認する.(修正版) */public boolean canListenOnPort() { ... }

Page 50: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

—run_locally オプション

自作プログラムのオプション。

追加のデバッグ情報を出力するが、動作が遅くなる。

新しいメンバーが理解できない

リモートでもデバッグ情報がほしい時は?

ローカルでパフォーマンステストする時は?

—extra_loggingなど、もっと直接的な名前にしよう

Page 51: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

名前に情報を追加する

Page 52: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

// 処理時間の計測long start = new Date().getTime();...long elapsed = new Date().getTime() - start;System.out.println("読み込み時間:" + elapsed + "秒");

ミリ秒を保持しているため、バグ

//処理時間の計測(修正版)long start_ms = new Date().getTime();...long elapsed_ms = new Date().getTime() - start_ms;System.out.println("読み込み時間:" + (elapsed_ms / 1000L) + "秒");

Page 53: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

メソッドの仮引数 単位を追加した仮引数start(int delay) delaySecs, delay_secs

createCache(int size) sizeMb, size_mb

throttleDownload(float limit) maxKbps, max_kbps

rotate(float angle) degreesCw, degrees_cw

単位を追加した例

Page 54: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

変数名 改善後の変数名password plaintextPassword

comment unescapedComment

html htmlUtf8

data dataUrlEnc

重要な属性を追加した例

バグになりそうなところだけ使うのがポイント

Page 55: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

単位や重要な属性は 変数名に追加して 常に意識できるようにする。

POINT

Page 56: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

長い名前を避ける

Page 57: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

newNavigationControllerWrappingViewControllerForDataSourceOfClass()

覚えにくいし、画面を占領しすぎている

こんな名前は嫌だ

Page 58: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

POINT変数の使い方によって、名前の長さを変える。

Page 59: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

// スコープが小さければ短い名前でもいいif (debug) { Map<String, Integer> m = new HashMap<>(); lookUpNamesNumbers(m); System.out.println(m);}!!// もし、メンバ変数やグローバル変数だとmの目的が分からないlookUpNamesNumbers(m); System.out.println(m);

Page 60: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

長い名前を入力するのはかんたんEclipseを例に挙げると

• 大文字による省略記法 ✦ “LHM”と入力してコード補完すると、”LinkedHashMap”が入力される。

✦ “LinH”と入力してコード補完すると、

”LinkedHashMap”と”LinkedHahsSet”が候補に挙がる。

!• コードテンプレートの使用

✦ “sysout”と入力してコード補完すると、

”System.out.println()”が入力される。

Page 61: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

省略の仕方に気をつける// この省略は、新しいメンバーが理解できるだろうか?class BEManager // BackEndManagerの略

プロジェクト固有のものは省略しないほうが良い プログラマにとって一般的な省略はしても良い

// この省略は理解できるeval // evaluationdoc // documentstr // string

Page 62: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

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

Page 63: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

基本だけど、おさらいしよう// クラス・インターフェースは大文字で始めるpublic class TaskQueue { // 定数(static final)は大文字とアンダースコア public static final int LIMIT_TASK_NUM = 100; // 変数やメソッドは小文字で始める private Queue<Task> tasks; public void addQueue(Task task) { ... } public Object executeQueue() { ... }}

Page 64: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

今日のまとめ

Page 65: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

名前に情報を詰め込むには• 明確な単語を選ぶ

✴ 状況に応じてgetではなくfetchやdownloadを使う

• 汎用的な名前を避ける ✴ 明確な理由がなければ、tmpやresultを使わない

• 具体的な名前で、詳細に説明する ✴ serverCanStart()よりcanListenOnPort()のほうが明確

• 大切な情報を追加する ✴ ミリ秒を表す_msを付ける。生パスワードをplaintextPasswordとする。

• スコープに合った名前を付ける ✴ 短いスコープなら短い名前で良い。長いスコープなら長くても明確に。

• 大文字やアンダースコアなどに意味を込める ✴ 例えば、一目で定数と解るような名前(LIMIT_TASK_NUM)をつける。

Page 66: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

解答例

public InputStream downloadFileAsStream( String httpUrl, int limitSizeMb, int timeoutSecs);

Page 67: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

ご清聴、ありがとうございました

Page 68: Readable Code 勉強会 in 仙台 Part 1: 名前を改善する

写真素材についてこの資料は、ぱくたそ(http://pakutaso.com)の写真素材を一部利用しています。この写真を継続して利用する場合は、ぱくたそ公式サイトからご自身でダウンロードしていただくか、ぱくたそのご利用規約(http://pakutaso.com)に同意していただく必要があります。同意しない場合は写真のご利用はできませんのでご注意ください。