仙台-3 仙台-2...仙台駅 仙台塩釜港 仙台南I.C 泉I.C 仙台宮城I.C 48 4 45 東北道 仙台東I.C 仙台-1 仙台-2 仙台-3 仙台-4 仙台-5 仙台-6
Readable Code 勉強会 in 仙台 Part 1: 名前を改善する
-
Upload
- -
Category
Technology
-
view
1.051 -
download
1
description
Transcript of Readable Code 勉強会 in 仙台 Part 1: 名前を改善する
Readable Code 勉強会 in 仙台
Part 1: 名前を改善する
自己紹介{ name: “河野 康隆”,
company: “株式会社セカイネット”,
facebook: “kouno.yasutaka”,
favorite: [ “GAE/J”, “Spock”, “D3.js”, ],
}
AGENDA
1.なぜ、読めるコードを書くべきなのか?
2.この勉強会の方針
3.まずは、名前を改善しよう
1.なぜ、読めるコードを書くべきなのか?
価値のあるコード
実際に動くコード=
?
稼働しているけれど…•ドキュメントとコードに整合性がない
•機能追加やバグの修正が困難
•運用が困難
•そもそも、
なぜ動いているのか分からない\(^o^)/
ゆえに
コスト>パフォーマンス
動くから価値があるとはいえない
価値のあるコードとは• 正しく動作する
• 機能を追加・変更しやすい
• 容易にメンテナンスできる
価値のあるコードを書くために
デザインパターン
テスト駆動開発
アルゴリズム
フレームワークアジャイル開発
ぼっちに優しくない
明日から誰でもすぐに実践できて、
上流・下流・上級者・初心者を問わず、
しかも効果が高い方法
リーダブルコード
読みやすいコードは 価値のあるコードの第一歩
• 間違えにくいし、間違いを見つけやすい。 • 無駄なコードやコメント、ドキュメントに
時間・思考を奪われない。 • 優れた設計やテストのしやすさにつながる • ◯◯さんじゃないとわからないコードが無くなる
おすすめはPDF版 タブレットで読みやすいB5本 どの現場にも持ち出せる !
オライリーのEbook Storeで販売中
https://www.oreilly.co.jp/books/9784873115658/
これを読もう!
2.この勉強会の方針
この勉強会の方針• 未読者の予習・既読者の復習とする
• なるべくJava言語を使って説明していく
• 自分のコードの改善点を知る
• たまに指しますので、快くお応えください
3.理解しやすいコード
どちらが優れている?// パターン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);}
どちらが優れている?// exponent 指数, mantissa 仮数// パターン1if (exponent >= 0) { return mantissa * (1 << exponent);} else { return mantissa / (1 << -exponent);}!// パターン2return exponent >= 0 ? mantissa * (1 << exponent) : mantissa / (1 << -exponent);
コードは他の人が 最短時間で理解できるように書かなければいけない。
POINT
コードが短いことは良いことだ。 でも、絶対に良いとは限らない
どちらが優れている?// パターン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);}
どちらが優れている?// exponent 指数, mantissa 仮数// パターン1if (exponent >= 0) { return mantissa * (1 << exponent);} else { return mantissa / (1 << -exponent);}!// パターン2return exponent >= 0 ? mantissa * (1 << exponent) : mantissa / (1 << -exponent);
短ければいいわけじゃない
// これは何やってるのかよくわからないassert (((bucket = findBucket(key)) != null) || !bucket.isOccupied());!!// でも、これならわかるbucket = findBucket(key);if (bucket != null) assert (!bucket.isOccupied());
コメントがあるおかげで理解できる
//"hash = (65599 * hash) + c" の高速版hash = (hash << 6) + (hash << 16) - hash + c;
まとめコードは他の人が 最短時間で理解できるように書かなければいけない。 !
大事なことなので二回言いました
4.名前に情報を詰め込む
書いてみよう!次の仕様のメソッド定義を書いてみてください。 コメントはいりません。 !
• 指定されたURLにあるファイルを取得する • 取得ファイルサイズの上限が指定できる。 • タイムアウト時間を指定できる。
!• 質問があれば受け付けます。
無難な名前を濫用してませんか?
getset
bufresult
tmp size
str
list
map
hoge
obj1, obj2
/** Webサイトのインターフェース. */interface WebSite { Page getPage();}
/** Webサイトのインターフェース(改善案). */interface WebSite { Page fetchPage();}
どこから取ってくるの?
/** 二分木クラス. */class BinaryTree { int size();}
/** 二分木クラス(改善案). */class BinaryTree { int height(); int numNodes();}
なんのサイズ?高さ?
ノードの数?
/** スレッドクラス. */class Thread { void stop();}
/** スレッドクラス(改善案). */class Thread { void kill(); void pause(); // resume()で処理を再開}
強制終了? 一時停止?
目的に即した 明瞭な単語を選ぼう
POINT
シソーラス(類語辞典)を使うhttp://thesaurus.com/
やり過ぎに注意しよう
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"]}
空虚な名前を避ける
2点間の距離を求めるコードを書く
a
bc 考え方:
ピタゴラスの定理 c2 = a2 + b2
座標1
座標2
// 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)のまま!
//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点間の距離を求めるコード(修正版)
平方のままならバグとわかる
resultのような 空虚な名前を避ける
POINT
このtmpは問題ない
// 左右の値を入れ替えるif (right < left) { tmp = right; right = left; left = tmp;}
「一時的な値の置き場」という意図が明確
このtmpはダメ// ユーザー情報を文字列化String tmp = user.name();tmp += " " + user.phone_number();tmp += " " + user.email();...template.put("user_info", tmp);
tmpではなく、userInfo等にすべき
もし”file_info”等に 間違えても気づかない
ループイテレーター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が逆
でも、次のようなケースではどうだろう
ループイテレーター改善:なんのイテレーターか明確にしよう
// 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 + "]");
バグがすぐに解る
tmp, it, resultのような汎用的な名前を使うときは、相応の理由を用意する。
POINT
具体的な名前をつかう
/** 任意のTCP/IPポートをサーバがリッスンできるか確認する. */public boolean serverCanStart() { ... }
機能に対し、メソッド名が抽象的
/** 任意のTCP/IPポートをサーバがリッスンできるか確認する.(修正版) */public boolean canListenOnPort() { ... }
—run_locally オプション
自作プログラムのオプション。
追加のデバッグ情報を出力するが、動作が遅くなる。
新しいメンバーが理解できない
リモートでもデバッグ情報がほしい時は?
ローカルでパフォーマンステストする時は?
—extra_loggingなど、もっと直接的な名前にしよう
名前に情報を追加する
// 処理時間の計測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) + "秒");
メソッドの仮引数 単位を追加した仮引数start(int delay) delaySecs, delay_secs
createCache(int size) sizeMb, size_mb
throttleDownload(float limit) maxKbps, max_kbps
rotate(float angle) degreesCw, degrees_cw
単位を追加した例
変数名 改善後の変数名password plaintextPassword
comment unescapedComment
html htmlUtf8
data dataUrlEnc
重要な属性を追加した例
バグになりそうなところだけ使うのがポイント
単位や重要な属性は 変数名に追加して 常に意識できるようにする。
POINT
長い名前を避ける
newNavigationControllerWrappingViewControllerForDataSourceOfClass()
覚えにくいし、画面を占領しすぎている
こんな名前は嫌だ
POINT変数の使い方によって、名前の長さを変える。
// スコープが小さければ短い名前でもいいif (debug) { Map<String, Integer> m = new HashMap<>(); lookUpNamesNumbers(m); System.out.println(m);}!!// もし、メンバ変数やグローバル変数だとmの目的が分からないlookUpNamesNumbers(m); System.out.println(m);
長い名前を入力するのはかんたんEclipseを例に挙げると
• 大文字による省略記法 ✦ “LHM”と入力してコード補完すると、”LinkedHashMap”が入力される。
✦ “LinH”と入力してコード補完すると、
”LinkedHashMap”と”LinkedHahsSet”が候補に挙がる。
!• コードテンプレートの使用
✦ “sysout”と入力してコード補完すると、
”System.out.println()”が入力される。
省略の仕方に気をつける// この省略は、新しいメンバーが理解できるだろうか?class BEManager // BackEndManagerの略
プロジェクト固有のものは省略しないほうが良い プログラマにとって一般的な省略はしても良い
// この省略は理解できるeval // evaluationdoc // documentstr // string
名前のフォーマットで 情報を伝える
基本だけど、おさらいしよう// クラス・インターフェースは大文字で始める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() { ... }}
今日のまとめ
名前に情報を詰め込むには• 明確な単語を選ぶ
✴ 状況に応じてgetではなくfetchやdownloadを使う
• 汎用的な名前を避ける ✴ 明確な理由がなければ、tmpやresultを使わない
• 具体的な名前で、詳細に説明する ✴ serverCanStart()よりcanListenOnPort()のほうが明確
• 大切な情報を追加する ✴ ミリ秒を表す_msを付ける。生パスワードをplaintextPasswordとする。
• スコープに合った名前を付ける ✴ 短いスコープなら短い名前で良い。長いスコープなら長くても明確に。
• 大文字やアンダースコアなどに意味を込める ✴ 例えば、一目で定数と解るような名前(LIMIT_TASK_NUM)をつける。
解答例
public InputStream downloadFileAsStream( String httpUrl, int limitSizeMb, int timeoutSecs);
ご清聴、ありがとうございました
写真素材についてこの資料は、ぱくたそ(http://pakutaso.com)の写真素材を一部利用しています。この写真を継続して利用する場合は、ぱくたそ公式サイトからご自身でダウンロードしていただくか、ぱくたそのご利用規約(http://pakutaso.com)に同意していただく必要があります。同意しない場合は写真のご利用はできませんのでご注意ください。