リーダブルコードをn読み解こう

: subtitle

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

: author

須藤功平

: institution

株式会社クリアコード

: content-source

schoo

: date

2015-04-03

: allotted-time

40m

: theme

clear-code

質問(1)

((‘tag:center’)) 前回の授業は…

* A:参加した
* B:参加できなかった\n
  (('note:(都合が合わなかった、知らなかったなど)'))
* C:知っていたが参加していない

質問(2)

((‘tag:center’)) プログラミングについて

* A:未経験
* B:学習中(('note:(schooや学校、独学など)'))
* C:趣味・仕事でたまに書く\n
  (('note:(趣味でWebサイトを作っている、職業がデザイナーなど)'))
* D:趣味・仕事でバリバリ書く\n
  (('note:(趣味でOSSを開発している、職業がエンジニアなど)'))

質問(3)

((‘tag:center’)) リーダブルコード(本)を…

* A:読んだ
* B:読んでいる
* C:まだ読んでいない

内容

* 自己紹介
* リーダブルコードとは
* 実例で考えよう
* 実際の改善にチャレンジ!
* まとめ
* 質疑応答

自己紹介(1)

* リーダブルコードの\n
  「解説」の著者\n
  (('note:http://www.clear-code.com/blog/2012/6/11.html'))

自己紹介(2)

* クリアコードの代表取締役
  * 「クリア」な(意図が明確な)\n
    「コード」を大事にする\n
    ソフトウェア開発会社

自己紹介(3)

# image
# src = images/github-kou-with-annotation.svg
# relative_height = 100

スライドプロパティー

: enable-title-on-image

false

リーダブルコードとは(1)

# blockquote
# title = はじめに p. x

本書の目的は、君のコードをよくすることだ

リーダブルコードとは(2)

# blockquote
# title = はじめに p. x

その中心となるのは、コードは理解しやすくなければいけないという考えだ

リーダブルコードとは(3)

# blockquote
# title = 1.2 読みやすさの基本定理 p. 3

「コードを理解する」というのは、変更を加えたりバグを見つけたりできるという意味

リーダブルコード

読む人が…

* 変更できるコード
* バグを見つけられるコード

((‘tag:center’))((‘tag:x-large’)) ↓n 読む人視点!

何をしているコード?

# coderay cpp

Node* node = list->head;
if (node == NULL) return;

while (node->next != NULL) {
    Print(node->data);
    node = node->next;
}
if (node != NULL) Print(node->data);

((‘tag:center’))((‘note:「優れた」コードって何? p. 2より’))

何をしているコード?

# coderay cpp

for (Node* node = list->head;
     node != NULL;
     node = node->next)
    Print(node->data);

((‘tag:center’))((‘note:「優れた」コードって何? p. 2より’))

どちらがリーダブル?

# coderay cpp

// どちらがリーダブルコード?どうして?
// リーダブルコード:変更できる・バグを見つけられるコード
// A                           // B
Node* node = list->head;     | for (Node* node = list->head;
if (node == NULL) return;    |      node != NULL;
while (node->next != NULL) { |      node = node->next)
    Print(node->data);       |     Print(node->data);
    node = node->next;       |
}                            |
if (node != NULL)            |
    Print(node->data);       |

((‘tag:center’))((‘note:「優れた」コードって何? p. 2より’))

実例で考えよう

((‘tag:center’)) 7章n 「制御フローを読みやすくする」n より

((‘ ’))

7.1 例:式の並び順(1)

# coderay c

/* A */
if (length >= 10)
/* B */
if (10 <= length)

((‘tag:center’)) どちらがリーダブル?

7.1 例:式の並び順(2)

# coderay c

/* A */
while (bytes_received < bytes_expected)
/* B */
while (bytes_expected > bytes_received)

((‘tag:center’)) どちらがリーダブル?

7.1 式の並び順の指針

* 左側
  * 「調査対象」の式。変化する。
  * (({length})), (({bytes_received}))
* 右側
  * 「比較対象」の式。変化しにくい。
  * (({10})), (({bytes_expected}))

7.1 指針に沿った並び順

# coderay c

/*  ↓調査対象。変化する。 */
if (length >= 10)
/*           ↑比較対象。変化しない。 */
/*     ↓調査対象 */
while (bytes_received < bytes_expected)
/*                     ↑比較対象 */

7.1 指針の理由

((‘tag:center’)) 自然言語の並び順に近い

# coderay c

/* もし長さが10以上なら */
if (length >= 10)

/* もし10が長さ以下なら */
if (10 <= length)

番外:別の指針

* 左側
  * 小さい値
* 右側
  * 大きい値

左から右にいくほどn 大きくなることは自然n ((‘note:(例:数直線)’))

番外:指針に沿った並び順

使う比較演算子は「(({<}))」と「(({<=}))」

# coderay c

if (10 <= length)

while (bytes_received < bytes_expected)

7.1:まとめ

* 条件式内の並び順を工夫すると\n
  リーダブル度があがる
* 指針:
  * 左側:変化する式
  * 右側:変化しにくい式

7.2 例:処理の順番(1)

# coderay c

/*     どちらがリーダブル?    */
/* A */         | /* B */
if (a == b) {   | if (a != b) {
  /* ケース1 */ |   /* ケース2 */
} else {        | } else {
  /* ケース2 */ |   /* ケース1 */
}               | }

7.2 例:処理の順番(1)

# coderay c

/*    A:条件が肯定形だから    */
/* A */         | /* B */
if (a == b) {   | if (a != b) {
  /* ケース1 */ |   /* ケース2 */
} else {        | } else {
  /* ケース2 */ |   /* ケース1 */
}               | }

7.2 例:処理の順番(2)

# coderay c

/*     どちらがリーダブル?    */
/* (ケース1の方が処理が長い) */
/* A */         | /* B */
if (a == b) {   | if (a != b) {
  /* ケース1 */ |   /* ケース2 */
  /*   ...   */ |  } else {
  /*   ...   */ |   /* ケース1 */
} else {        |   /*   ...   */
  /* ケース2 */ |   /*   ...   */
}               | }

7.2 例:処理の順番(2)

# coderay c

/*   B:単純な処理の条件が先   */
/*    if/elseを一望しやすい    */
/* A */         | /* B */
if (a == b) {   | if (a != b) {
  /* ケース1 */ |   /* ケース2 */
  /*   ...   */ |  } else {
  /*   ...   */ |   /* ケース1 */
} else {        |   /*   ...   */
  /* ケース2 */ |   /*   ...   */
}               | }

7.2 指針

* 否定形は肯定形にして書く
  * if (!debug)よりif (debug)
* 単純な処理の条件を先に書く
  * ifとelseを一望できて読みやすい
* 関心を引く条件を先に書く
* 目立つ条件を先に書く

7.2 指針:注意

* 同時に満たせないことがある
* 自分で優劣を判断すること
* 優劣は明確になることが多い

7.2 判断してみよう

# coderay cpp

if (!url.HasQueryParameter("expand_all")) {
  response.Render(items);
  // ...
} else {
  for (int i = 0; i < items.size(); i++) {
    items[i].Expand();
  }
  // ...
}

7.2 !を外して順番を逆に

# coderay cpp

// 関心を引く条件「expand_all」を優先
// (肯定形になったのはおまけ)
if (url.HasQueryParameter("expand_all")) {
  for (int i = 0; i < items.size(); i++) {
    items[i].Expand();
  }
  // ...
} else {
  response.Render(items);
  // ...
}

7.2 まとめ

* 処理する条件の順番次第で\n
  リーダブル度があがる
* 指針:
  * 否定形は肯定形にして書く
  * 単純な条件を先に書く
  * 関心がある・目立つ条件を先に書く

7.5 関数から早く返す

((‘tag:center’)) 「ガード節」

# coderay java

public boolean Contains(String str, String substr) {
  if (str == null || substr == null) // ガード節
    return false;
  if (substr.equals(""))             // ガード節
    return true;
  // 大事な処理だけに興味があるなら↑までは無視可能。
  // 大事な処理は↓から。
  // ...
}

7.5 ガード節なし

# coderay java

public boolean Contains(String str, String substr) {
  if (str == null || substr == null) {
    return false; // 異常値
  } else {
    if (substr.equals("")) {
      return true; // 特別値
    } else {
      // 大事な処理はここ。ネストが深い。
      // ...
    }
  }
}

7.5 ガード節の効果

* 以降の処理が単純になる
  * 異常値・特別値を考慮しなくてよい
  * →考える事が減る
* ネストが浅くなる
  * →コードの見通しがよくなる

((‘tag:center’)) リーダブルコードにつながる!

番外:ifとreturnの使い方

((‘tag:center’)) ifとreturnの使い方n ククログ(2012-03-28)n ((‘note:www.clear-code.com/blog/2012/3/28.html’))

((‘ ’))

番外:パス

((‘tag:center’)) 同じ処理でも流れ(パス)は違う

# coderay ruby

if 条件    | return if !条件
  サブ処理 |
end        | サブ処理

番外:パスとリーダブル

* パス
  * 処理を実行するときの流れ
  * コードを読むときの流れでもある
* 読みやすい流れ
  * →リーダブル!

番外:例(1)

# coderay ruby

def add_comment(post, user, text)
  if post.hidden_from?(user)
    report_access_error
  else
    comment = Comment.create(text)
    post.add(comment)
  end
end

番外:例(1)コメント付き

# coderay ruby

# コメントを追加するメソッドだな!
def add_comment(post, user, text)
  if post.hidden_from?(user) # ユーザーに見えない投稿なら…
    report_access_error
    # エラー処理か。ifの後にもなにか処理はあるのかな?
  else
    comment = Comment.create(text)
    post.add(comment)
    # ユーザーに見えるときだけコメント追加か。
    # こうやってコメントを追加するんだな。
  end # ifの後にはなにも処理はないんだな。
end

番外:例(1)return付き

# coderay ruby

# コメントを追加するメソッドだな!
def add_comment(post, user, text)
  if post.hidden_from?(user) # ユーザーに見えない投稿なら…
    report_access_error # エラー処理をして、
    return # それで終了か。
  end

  # エラー処理が終わったからここからは通常の処理だな。
  comment = Comment.create(text)
  post.add(comment) # こうやってコメントを追加するんだな!
end

番外:例(1)まとめ

* 異常系と正常系の扱いを変える
* 異常系はすぐにreturn
  * エラー処理だけというのをアピール
* 正常系はネストしない
  * 正常系の方が重要なコード
  * 重要なコードは見やすい場所に

番外:例(2)

# coderay ruby

def prepare_database(path)
  if not File.exist?(path)
    return Database.create(path)
  end

  Database.open(path)
end

番外:例(2)コメント付き

# coderay ruby

# データベースを準備するんだな
def prepare_database(path)
  if not File.exist?(path) # なかったら…
    # 作ってすぐに終了か。あれ、作るのも普通の「準備」なような…
    return Database.create(path)
  end

  # あったら開くのか。これも「準備」だよなぁ。
  # なにか特別な意図があるんだろうか…
  Database.open(path)
end

番外:例(2)returnなし

# coderay ruby

# データベースを準備するんだな
def prepare_database(path)
  if File.exist?(path) # あったら…
    Database.open(path) # 開いて
  else                   # なかったら…
    Database.create(path) # 作るのか
  end
end

番外:例(2)まとめ

* 正常系同士の扱いを変えない
  * 変えると意図があると勘違いする
  * 「Aの処理は何か特別なのかも…」
* なんでもガード節にしない
  * 特別なケースや異常系のときだけ

他の例

* 三項演算子
* ネストを浅くする
* …
* (詳細は本を買ってください)

実際の改善にチャレンジ!

# coderay javascript

// 読みやすい制御フローにして投稿・よい投稿に「いいね!」して応援

function positiveIntegerSum(n) { // n以下の正の整数の合計を返す
  if (0 >= n) {            // ヒント:
    return -1; // エラー   // 1. 条件式の並び順
  } else {                 //    * 左に変化するもの
    var total = 0;         //    * 右に変化しにくいもの
    for (; n > 0; n--) {   // 2. 処理する条件の順番
      total += n;          //    * 肯定形で書く
    }                      //    * 単純な条件を先に書く
    return total;          //    * 関心がある条件を先に書く
  }                        //    * 目立つ条件を先に書く
}                          // 3. ガード節

まとめ(1)

* リーダブルコードとは
  * 変更できるコード
  * バグを見つけられるコード
  * ↑は((*読む人視点*))

まとめ(2)

* 「読みやすい制御フロー」を\n
  考えた
  * 条件式内の並び順
    * →左に変化するもの・右に変化しにくいもの
  * 処理する条件の順番
    * →肯定形で書く・単純な条件を先に書くなど
  * ガード節
    * →特別なケースはすぐにreturn

まとめ(3)

* 実際の改善にチャレンジした
  * 「((*読む人*))が理解しやすいか?」を\n
    とことん考えたはず

# blockquote
# title = 7章 制御フローを読みやすくする p. 89

行数を短くするよりも、他の人が理解するのにかかる時間を短くする。

これから(1)

* これからも((*読む人*))のことを\n
  考えてコードを書こう
* ((*読む人*))のことを考えるには?
  * 読む経験をたくさん積む
  * たくさんコードを読もう

これから(2)

* たくさんコードを読むコツ
  * コードから学ぶ気持ちで読む
  * ×悪いこと探し
  * ○いいこと探し
* 読むコード
  * オススメは自分が使っているOSS
  * ↑動作を知っているから読みやすい

悪いコード

* 見つけやすい
  * 異質
  * リーダブルじゃない

よいコード

* 見つけにくい
  * リーダブルだから
  * すーっと理解できてひっかからない
* これからのチャレンジ
  * 意識して見つけよう!

これから(3)

((‘tag:center’))((‘tag:large’)) 「解説」を読むn ((‘note:www.clear-code.com/blog/2012/6/11.html’))

* 本文:((*個人*))で\n
  リーダブルコードを書く方法
* 解説:((*チーム*))で\n
  リーダブルコードを書く方法