TEAM BUILDING 39
リーダブルコード
2017.11
為安 圭介
DustinBoswell,TrevorFoucher (著)@オライリージャパン『リーダブルコード』より
私のチームの
目次
1
1. 良いコードとは
2. 名前
3. 整形
4. コメント
5. 制御フローとロジック
6. 変数
7. 標準API
はじめに
2
このスライドを読んで役に立つ人
良いコードの指針を
作りたいチームリーダ
良いコードを書きたい
ITエンジニア
どちらかに当てはまるなら、
これを読めば役に立つ!
あなたはどちら?
3
・・・かもしれない!
4
わたしは
紹介
為安 圭介(ためやす けいすけ)
札幌市在住のITエンジニア
仕事 企業向けWebシステムやスマホAP開発
資格 認定スクラムマスターとかプロマネとか
著書
『強いチームを作るためのたった66の方法』『1200日の朝会で学んだたったひとつのこと』
『自分のことを朕と呼んだらチームが変わった』『ブレイブ・プロジェクトマネジメント』等多数
・・・だと良いな https://www.facebook.com/keisuke.tameyasu
本スライドは、Dustin Boswell, Trevor Foucher (著) @オライリージャパン『リーダブルコード』のまとめです!
本題
5
1
良いコードとは
7
大原則
8
どっちが良いコード?
return exponent >=0 ? mantissa * (1 << exponent) : mantissa / (1 << -exponent);
if (exponent >= 0) {
return mantissa * (1 << exponent);
} else {
return mantissa / (1 << -exponent);
}
(A)
(B)
9
どっちが良いコード?
return exponent >=0 ? mantissa * (1 << exponent) : mantissa / (1 << -exponent);
if (exponent >= 0) {
return mantissa * (1 << exponent);
} else {
return mantissa / (1 << -exponent);
}
(A)
(B) 私のチームでは、こっちを「良いコード」と定義します。
10
鍵となる考え
2
わかりやすい名前をつける
12
どっちが良いコード?
int tmp = 0;
tmp = v[i] * v[i];
int sumSquares = 0;
sumSquares = v[i] * v[i];
(A)
(B)
ある数字の二乗を計算する値
13
どっちが良いコード?
int tmp = 0;
tmp = v[i] * v[i];
int sumSquares = 0;
sumSquares = v[i] * v[i];
(A)
(B) 私のチームでは、こっちを「良いコード」と定義します。
具体的な名前をつける
ある数字の二乗を計算する値
14
名前がバグを教えてくれる
int tmp = 0;
tmp = v[i];
int sumSquares = 0;
sumSquares = v[i];
(A)
(B)
←これはバグ?
15
名前がバグを教えてくれる
int tmp = 0;
tmp = v[i];
int sumSquares = 0;
sumSquares = v[i];
(A)
(B)
←これはバグ!
←これはバグ?
具体的な名前をつける
私のチームでは、こっちを「良いコード」と定義します。
16
どっちが良いコード?
Page getPage();
Page loadPage();
(A)
(B)
ページを外部から読み込み、情報を取得する
17
どっちが良いコード?
Page getPage();
Page loadPage();
(A)
(B) 私のチームでは、こっちを「良いコード」と定義します。
ページを外部から読み込み、情報を取得する
具体的な名前をつける
18
どっちが良いコード?
List<Person> getByName(String name);
List<Person> findByName(String name);
(A)
(B)
名前を使って、ユーザのリストを検索する
19
どっちが良いコード?
List<Person> getByName(String name);
List<Person> findByName(String name);
(A)
(B) 私のチームでは、こっちを「良いコード」と定義します。
名前を使って、ユーザのリストを検索する
具体的な名前をつける
20
どっちが良いコード?
List<Integer> result = new ArrayList<>();
...
return result;
List<Integer> matchedIndexList = new ArrayList<>();
...
return matchedIndexList;
(A)
(B)
文字列のリストの中から、一致したindexのリストを返却する
21
どっちが良いコード?
List<Integer> result = new ArrayList<>();
...
return result;
List<Integer> matchedIndexList = new ArrayList<>();
...
return matchedIndexList;
(A)
(B) 私のチームでは、こっちを「良いコード」と定義します。
文字列のリストの中から、一致したindexのリストを返却する
具体的な名前をつける
22
どっちが良いコード?
class LearningDate {}
class LearningStartDate{}
(A)
(B)
ある期間の開始日(例:学習期間)
23
どっちが良いコード?
class LearningDate {}
class LearningStartDate{}
(A)
(B) 私のチームでは、こっちを「良いコード」と定義します。
ある期間の開始日(例:学習期間)
具体的な名前をつける
24
良い名前とは、誤解されない名前である
明快な単語を選ぶ
 size → height, length, bytes, memoryBytes...
 find → search,locate...
汎用的な名前を避ける
 tmp, retval → 意味のある名前に
 i, j → memberIndex, userIndex, kaishaIndex, soshikiIndex ,ki,si
25
良い名前とは、誤解されない名前である
限界値などの名前は規則を決める
 上下の限界値 min, max
 包含的 first, last
 包含/排他的範囲 begin, end
誤解を招く短い名前より、わかりやすい長い名前を使う
 長い名前の入力は問題ではない ※IDEやエディタが補完してくれる
 stringをstr、documentをdocと省略するのは問題ないが、チーム独自の省略規則は
やめる
3
コードは見やすく整形する
27
どっちが良いコード?
public class PerformanceTester
{
public statc final TcpConnectionSimulator wifi = new TcpConnectionSimulator(
500, /*Kbps*/
80, /*msec latency*/
200,/*jitter*/
1/*packet loss %*/);
public static final TcpConnectionSimulator t3_fibar = new TcpConnectionSimulator(
45000,
10,
0,
0);
public static final TcpConnectionSimulator cell = new TcpConnectionSimulator(
100,
400,
250,
5);
}
(A)
public class PerformanceTester
{
// TcpConnectionSimulator(throughput, latency, jitter, packet_loss)
// [kbps] [ms] [ms] [%]
public statc final TcpConnectionSimulator wifi =
new TcpConnectionSimulator(500, 80, 200,1);
public static final TcpConnectionSimulator t3_fibar =
new TcpConnectionSimulator(45000, 10, 0, 0);
public static final TcpConnectionSimulator cell =
new TcpConnectionSimulator(100, 400, 250, 5);
}
(B)
4つの引数を持つTcpConnectionSimulatorクラスを使って、3つのインスタンスを作る。
1.接続速度(kbps) 2.平均遅延時間(msec) 3.遅延時間(msec) 4.パケットロス率(percent,%)
28
どっちが良いコード?
public class PerformanceTester
{
public statc final TcpConnectionSimulator wifi = new TcpConnectionSimulator(
500, /*Kbps*/
80, /*msec latency*/
200,/*jitter*/
1/*packet loss %*/);
public static final TcpConnectionSimulator t3_fibar = new TcpConnectionSimulator(
45000,
10,
0,
0);
public static final TcpConnectionSimulator cell = new TcpConnectionSimulator(
100,
400,
250,
5);
}
(A)
public class PerformanceTester
{
// TcpConnectionSimulator(throughput, latency, jitter, packet_loss)
// [kbps] [ms] [ms] [%]
public statc final TcpConnectionSimulator wifi =
new TcpConnectionSimulator(500, 80, 200,1);
public static final TcpConnectionSimulator t3_fibar =
new TcpConnectionSimulator(45000, 10, 0, 0);
public static final TcpConnectionSimulator cell =
new TcpConnectionSimulator(100, 400, 250, 5);
}
(B)
4つの引数を持つTcpConnectionSimulatorクラスを使って、3つのインスタンスを作る。
1.接続速度(kbps) 2.平均遅延時間(msec) 3.遅延時間(msec) 4.パケットロス率(percent,%)
シルエットを意識する
29
どっちが良いコード?
CheckFullName("doug Adams","Mr.Doglas Adams","");
CheckFullName("Jake Brown","Mr. Jake Brown III","");
CheckFullName("No such Guy","","no match found");
CheckFullName("John","","more than one result");
CheckFullName("doug Adams" ,"Mr.Doglas Adams" ,"");
CheckFullName("Jake Brown" ,"Mr. Jake Brown III" ,"");
CheckFullName("No such Guy" ,"" ,"no match found");
CheckFullName("John" ,"" ,"more than one result");
(A)
(B)
30
どっちが良いコード?
CheckFullName("doug Adams","Mr.Doglas Adams","");
CheckFullName("Jake Brown","Mr. Jake Brown III","");
CheckFullName("No such Guy","","no match found");
CheckFullName("John","","more than one result");
CheckFullName("doug Adams" ,"Mr.Doglas Adams" ,"");
CheckFullName("Jake Brown" ,"Mr. Jake Brown III" ,"");
CheckFullName("No such Guy" ,"" ,"no match found");
CheckFullName("John" ,"" ,"more than one result");
(A)
(B) 私のチームでは、こっちを「良いコード」と定義・・・できるかどうか微妙。
シルエットを意識する
31
一貫性と意味のあるルールでコードを整形する
複数のブロックで似たようなことをしていたら、
シルエットを揃える
 コードの「列」を揃える
並び順を揃える
 ある場所で a, b ,c と並んでいたものは、別の場所でも a, b ,c と並べる
ブロックにまとめる
 空行とコメントでブロックを「段落」に分ける
書き手の意図がわかるように
コメントをつける
4
33
どっちが良いコード?
// 文字列を;で分割する
String[] splitStr = str.split(“;”, 0);
String[] splitStr = str.split(“;”, 0);
(A)
(B)
34
どっちが良いコード?
// 文字列を;で分割する
String[] splitStr = str.split(“;”, 0);
String[] splitStr = str.split(“;”, 0);
(A)
(B) 私のチームでは、こっちを「良いコード」と定義します。
見ればわかるものをコメントしない
35
どっちが良いコード?
NUM_THREADS = 8;
NUM_THREADS = 8; // 値は「num_processorsの2倍程度で十分」
(A)
(B)
36
どっちが良いコード?
NUM_THREADS = 8;
NUM_THREADS = 8; // 値は「num_processorsの2倍程度で十分」
(A)
(B) 私のチームでは、こっちを「良いコード」と定義します。
自分の考えを記しておく
37
コメント
コメントすべきではないこと
 コードを見ればすぐにわかるようなこと
 ひどいコードを補うコメント(コードを直接修正する)
 誤解を与える表現(代名詞)
自分の考えを記録する
 なぜこの書き方なのか
 どうしてこの定数はこの値なのか
読み手の立場になる
 コードの読み手が驚くような処理はコメントしておく
 ブロック単位でコメントをつける
制御フローを読みやすくする
5
39
どっちが良いコード?
if (10 < length) {
if (length > 10) {
(A)
(B)
「もし10がその長さより小さいなら・・・」
「もしその長さが10より大きいなら・・・」
40
どっちが良いコード?
if (10 < length) {
if (length > 10) {
(A)
(B) 私のチームでは、こっちを「良いコード」と定義します。
「もし10がその長さより小さいなら・・・」
「もしその長さが10より大きいなら・・・」
変化する値を左に、固定値を右に
41
どっちが良いコード?
while( 1024 > receivedBytes){
while(receivedBytes < 1024 ){
(A)
(B)
42
どっちが良いコード?
while( 1024 > receivedBytes){
while(receivedBytes < 1024 ){
(A)
(B) 私のチームでは、こっちを「良いコード」と定義します。
変化する値を左に、固定値を右に
43
どっちが良いコード?
if (!user.hasAuthority()) {
// 権限なしの処理
} else {
// 権限ありの処理
}
if (user.hasAuthority()){
// 権限ありの処理
} else {
// 権限なしの処理
}
(A)
(B)
44
どっちが良いコード?
if (!user.hasAuthority()) {
// 権限なしの処理
} else {
// 権限ありの処理
}
if (user.hasAuthority()){
// 権限ありの処理
} else {
// 権限なしの処理
}
(A)
(B) 私のチームでは、こっちを「良いコード」と定義します。
if文の条件はできるだけ素直に
45
どっちが良いコード?
if(userResult == true)
{
if(permissionResult != true)
{
reply.writeErrors("permissions error");
reply.done();
return;
}
}
reply.writeErrors("");
reply.done();
(A)
if (!userResult) {
reply.writeErrors(“”);
reply.done();
return;
}
if (!permissionResult) {
reply.writeErrors(“permissions error”);
reply.done();
return;
}
reply.writeErrors("");
reply.done();
(B)
46
どっちが良いコード?
if(userResult == true)
{
if(permissionResult != true)
{
reply.writeErrors("permissions error");
reply.done();
return;
}
}
reply.writeErrors("");
reply.done();
(A)
if (!userResult) {
reply.writeErrors(“”);
reply.done();
return;
}
if (!permissionResult) {
reply.writeErrors(“permissions error”);
reply.done();
return;
}
reply.writeErrors("");
reply.done();
(B)
私のチームでは、こっちを
「良いコード」と定義します。
ガード節を使う
47
制御フローを読みやすくする
比較を書くときは、変化する値を左に、安定した値を右に
 if (length >= 10) よりも if (10 <= length)
処理の順番
 肯定形で書く
 単純な条件を先に書く
ネストを浅く
 ガード節を使う
 特別なケースはすぐにreturn
6
変数を扱いやすくする
49
どっちが良いコード?
Date now = daytime.now();
System.out.println(now);
System.out.println(daytime.now());
(A)
(B)
50
どっちが良いコード?
Date now = daytime.now();
System.out.println(now);
System.out.println(daytime.now());
(A)
(B) 私のチームでは、こっちを「良いコード」と定義します。
より明確にならない変数は定義しない
51
どっちが良いコード?
class LargeClass{
String str;
void method1(){
str = ...;
method2();
}
void method2(){
System.println(str) ;
}
void methodOther(){
・・・
・・・
}
}
(A)
class LargeClass {
void method1() {
String str = ...;
method2(str);
}
void method2(String str) {
System.println(str) ;
}
void methodOther() {
・・・
}
}
(B)
←strを使う
←strを使う
←以降、strを使わない記述が続く
←strを使う
←strを使う
←以降のメソッドは、strが見えない
52
どっちが良いコード?
class LargeClass{
String str;
void method1(){
str = ...;
method2();
}
void method2(){
System.println(str) ;
}
void methodOther(){
・・・
・・・
}
}
(A)
class LargeClass {
void method1() {
String str = ...;
method2(str);
}
void method2(String str) {
System.println(str) ;
}
void methodOther() {
・・・
}
}
(B)
私のチームでは、こっちを
「良いコード」と定義します。
←strを使う
←strを使う
←以降、strを使わない記述が続く
←strを使う
←strを使う
←以降のメソッドは、strが見えない
変数のスコープを縮める
53
どっちが良いコード?
<script>
var f = function(){
for( i = 0; i < 10 ; i += 1 )...
};
f();
</script>
<script>
alert(i);
</script>
(A)
<script>
var f = function(){
for( var i = 0; i < 10 ; i += 1 )...
};
f();
</script>
<script>
alert(i);
</script>
(B)
←10が表示される ←undefined
54
どっちが良いコード?
<script>
var f = function(){
for( i = 0; i < 10 ; i += 1 )...
};
f();
</script>
<script>
alert(i);
</script>
(A)
<script>
var f = function(){
for( var i = 0; i < 10 ; i += 1 )...
};
f();
</script>
<script>
alert(i);
</script>
(B)
私のチームでは、こっちを
「良いコード」と定義します。
変数のスコープを縮める
←10が表示される ←undefined
55
変数
役に立たない変数は削除する
 複雑な式を分割していない変数は使わない
 より明確にならない変数は使わない
変数のスコープを縮める
 ローカル→インスタンス→グローバル
 変数のことが見えるコード行数はできるだけ減らす
7
コーディング量を減らす
57
鍵となる考え方
58
定期的にAPIを読んで、標準ライブラリに慣れておく
標準APIを使う
汎用的なユーティリティコードを作って重複コードを
削除する
広く使われている外部ライブラリ(Apache Commons
など)にあるような機能をオレオレ実装しない
おわり
TEAM BUILDING 39
SpecialThanks To
S.T, A.K, E.O, S.S, T.U
Y.Y
60
参考文献
• Dustin Boswell, Trevor Foucher (著) @オライリージャパン「リーダブルコード」
• 上田勲 (著)@秀和システム「プリンシプル オブ プログラミング」
• こざき亜衣@小学館「あさひなぐ」15巻
• こざき亜衣@小学館「あさひなぐ」16巻
• こざき亜衣@小学館「あさひなぐ」17巻
• こざき亜衣@小学館「あさひなぐ」18巻

私のチームのリーダブルコード