Slide 1

Slide 1 text

良いコードの書き方 エキサイト株式会社 新卒研修 2022.4.19

Slide 2

Slide 2 text

良いコードとは 本日の議題は良いコードの書き方ですが, 「良いコード」とは何でしょう? 2

Slide 3

Slide 3 text

良いコードとは 本日の議題は良いコードの書き方ですが, 「良いコード」とは何でしょう? このスライドでは - 読みやすいこと - 拡張しやすいこと - 安全なこと の3つとしてみます。 3

Slide 4

Slide 4 text

内容 2部構成となっています。 1. Principle プログラミングにおける普遍的な原則や思想, 視点を説明します。 2. Cording プリンシプルを実行するための手法や手段を説明します。 4

Slide 5

Slide 5 text

Principle 原則, 思想, 視点 5

Slide 6

Slide 6 text

なぜプリンシプル? プリンシプルとは - プログラミングの指針となる原則や思想のこと - 歴史の審査を受けたプログラミングのためのエッセンスでもある プリンシプルは指針なので手段ではなく方向性を示します。 プログラミング技術の大半はプリンシプルを意識したものです。 後半に紹介する手段やテクニックはプリンシプルを満たすためのものです。 プリンシプルを意識すれば手段を間違えても悪いコードにはなりません。 6

Slide 7

Slide 7 text

原則 Keep It Simple! Stupid. (KISS) コードを書くとき最優先の価値を「単純性」「簡潔性」に置きます。 - 新しく覚えた技術を使いたい - 将来の必要に備えたい(YAGNI) - 勝手に用件を加えてしまう といったことを避けましょう。 機能が同じであれば, 余計なコードを含まないコードの方が優れています。 7

Slide 8

Slide 8 text

原則 Don’t Repeat Yourself. (DRY) 同じ知識(コード)を重複して書いてはいけません。 重複はコード量を増やし, 理解を妨げ, 変更の難易度をあげます。 知識(コード)を抽象化しましょう。 単にコードをそのまま説明しているコメントも重複です。 オブジェクト指向や設計, デザインパターンといった技術はコードの重複の排除を目的の 1つにしています。 8

Slide 9

Slide 9 text

原則 You Aren’t Going to Need It. (YAGNI) コードは「多分必要になるだろう」で書いてはいけません。 色々な事態に備えてコードを盛り込んでおいても使われないことがほとんどです。逆に 時間が経つとなぜ使われないコードがあるのかわからなくなります。 汎用性よりも単純性を考え, コードは「今」必要なものに留めましょう。 仮に要件が増えて拡張することになっても, 単純なコードを変更する方が, 汎用的で複雑 なコードを変更するよりも簡単です。 9

Slide 10

Slide 10 text

原則 Open Closed Principle (OCP) コードの振る舞いを拡張してもその他のコード(クラスやモジュール)には全く影響を受け ないようにしましょう。 ソフトウェアの寿命は想定よりも長くなる傾向にあります。柔らかい設計を意識しましょ う。 そのためにはポリモーフィズムが重要です。 オブジェクト指向と手続き型の違いはここへの対処と言い切ってる書籍もあるくらいで す。(Robert C. Martin著, 角 征典・高木 正弘訳, Clean Archetecture 達人に学ぶソフトウェアの構造と設計, 株式会社ドワンゴ, 2018) 10

Slide 11

Slide 11 text

思想 テスト容易性 コードはテストをしやすいように書きましょう。 ポイントはモジュール間の依存関係の排除です。 テストのしやすさを意識することで - 他クラスへの影響が減り - 責務がわかりやすく - 呼び出し側が使いやすい コードになります。 11

Slide 12

Slide 12 text

思想 線形原理 処理の流れは直線にこだわりましょう。 ある機能はいくつかの機能の重ね合わせによって実現されるべきです。 スイッチでコードを制御したり, 状態の数を無闇に増やしたりするとコードがわかりにくく なります。 条件分岐を避けましょう。 12

Slide 13

Slide 13 text

視点 凝集度 凝集度とはモジュールに含まれている機能の純粋さを表す尺度です。 同じ機能に関する手続きは同じモジュールに含まれている方が好まれます。 なぜならば, 変更箇所の特定が容易であり, 変更の影響をモジュール内に限定できるた めです。 手順が近いもの, 共通する参照データは同じモジュールに含めましょう。 13

Slide 14

Slide 14 text

視点 結合度 目指すべきモジュールの状態は, 他モジュールの改修によって影響を与えられることな く, また他モジュールにロジックを知られていない状況です。 これによって, 変更の影響を大きくしない効果が生まれます。 以下のことに気をつけましょう。 - public宣言したデータの参照 - 使う側がロジックを知っているような実装 - 不必要なデータを含むパラメータ 14

Slide 15

Slide 15 text

視点 冪等性 何度同じ処理をしても同じ状態を保つべきということです。 例えばコードは次のようなことを満たすべきでしょう。 - 途中で落ちたプログラムを再実行しても正常終了の場合と同等の結果になる - 何度APIへ同じリクエストをしても同じリソース状態となる - 同じバッチを何度実行してもDBやS3は同一の状態に保たれる これによって安全性が上がります。安全なコードは気軽に実行できます。 気軽に実行できるとリファクタリングがしやすくなり, コードは綺麗に保てます。 15

Slide 16

Slide 16 text

まとめ 今回紹介したプリンシプルを簡単に意訳すると - シンプルにする - 重複させない - 将来の予測をしない - 他へ追加変更の影響を及ぼさないようインターフェースを使う - 処理を分岐させない - テストしやすいように書く - クラスは機能単位でまとめて各機能は他機能を参照しない - どのタイミングで何度実行しても同じ状態となるようにする 16

Slide 17

Slide 17 text

もっと知りたい方は 3年目まで(入社2年間)で身につけたい原理原則を テーマとした本です。 今回の内容はここから独断と偏見で採用していま す。(歴は近いので的外れではないはず) おすすめの本なのでぜひ。 17

Slide 18

Slide 18 text

Cording 手法, 手段, テクニック 18

Slide 19

Slide 19 text

命名 なぜ命名? コードは変数名, 関数名の連続です。 コードは書いてる時間よりも読んでる時間が長いです。(1:9くらい) 命名がよくなれば可読性が上がり, コーディングは格段に楽になります。 19

Slide 20

Slide 20 text

命名 意識すべきこと 変数, 関数, クラス名は次の大命題に応える必要があります。 - なぜそれをするのか - 何をするのか - どのように使用するのか もし名前に解説が必要なら, その名前は意図が明確とは言えません。 20

Slide 21

Slide 21 text

命名 品詞について 21 コーディング規約に近いですが - クラス名, 名前空間は名詞 - 関数名は名詞+動詞 とすると自然な命名になります。

Slide 22

Slide 22 text

命名 ニュアンス ニュアンスや意味に気を遣いましょ う。 「取得する」だけでも, get, find, factory, popなど複数の言い回しが あります。 DataやInfoなど意味のない語彙は避 けましょう。 22

Slide 23

Slide 23 text

命名 疑問系 疑問系の変数や関数はbool値を 返す文化があります。 文化に乗っ取ることで, よりわか りやすい命名が可能です。 23

Slide 24

Slide 24 text

命名 長い名前 推奨はされませんが, あやふやな短い名前よりも優れています。 名前が長くなってしまう時は以下を見直してみましょう。 - 処理を名前にしていないか?-> 抽象的な名前をつけましょう。 - コンテキストは適切か?->名前空間やクラス名を見直しましょう。 - 多くのことを関数が行っていないか?-> 関数を分割してみましょう。 24

Slide 25

Slide 25 text

命名 ユビキタス言語 プロジェクト独自の概念には積極的に名前をつけて 使用します。 日本語/英語の両方用意すると便利で良いです。 前項の長い名前やニュアンスの的確さ, 表記揺れに 強くなります。 25

Slide 26

Slide 26 text

コメント なぜコメント? コードは実際に動いているからこそ仕様書としても機能します。 コメントは動かないので変更から取り残されることが頻発します。 余計なコメントは画面領域を奪い可読性を下げます。 適切なコメントへの姿勢を学びましょう。 26

Slide 27

Slide 27 text

コメント 種類 ブロックコメント /** */で括られたコメント クラスや関数の挙動を示すDocコメントとして使われることが多い インラインコメント //で始まるコメント 関数や処理の説明として使われることが多い 27

Slide 28

Slide 28 text

コメント 常に失敗 適切なコメントの使用方法とは, コードでうまく表現することに失敗したときに, それを補う のに使うことです。 コメントとは常に失敗なのです。 28

Slide 29

Slide 29 text

コメント Docコメントの無くし方 29 /** * idのユーザーを DBから取得する * * @access public * @params int $id 検索用のユニークな ID * @return User $user ユーザーオブジェクト **/ public function get($id) { } public function findById(UserId $user_id) : User { }

Slide 30

Slide 30 text

コメント インラインコメントの無くし方 30 // 名前は20文字以内であるべき if (length($name) <= 20) { return false; } // 生年月日の月は 1~12のうちのいずれかであるべき if ($month >= 1 && $month < 12) { return false; } if (isInvalidNameLenght($name)) { return false; } if (isInvalidMonth($month)) { return false; }

Slide 31

Slide 31 text

コメント 許されるコメント 以下のようなコメントは許されます。 - 意図を示す - なぜそう書かなければならなかったのかを示す - 警告する というのは英語圏の書籍の主張です。 基本的には賛成ですが, 英語のコードを読むのは基本的に辛いです。 コメントすることで理解の負担や時間を減らせるのであればヨシです。 31

Slide 32

Slide 32 text

条件分岐 なぜ条件分岐? プリンシプルでみた通り, 条件分岐は線形原理に反します。 条件分岐は区分や種別が要件に入っている場合に起こります。 区分や種別が入っても条件分岐を使わないことはインターフェースを使うことで可能にな ります。 32

Slide 33

Slide 33 text

条件分岐 条件の関数化 if ($user.type === 'child') { } if ($user.isChild()) { } 33 まずは条件を関数化してみましょう。 例として大人と子供という区分を作って関数化してみます。 これはuser.typeをprivateにすることで防げるという見方もできます。 カプセル化を緩めるとこのように外部にロジックが漏れる危険性があります。 ※とは言え, getterを全く持たないことも不可能です。

Slide 34

Slide 34 text

条件分岐 早期リターン else句はコードを複雑にします。早期リターンを使って無くしましょう。 大人と子供の料金を返す関数を例としてみます。 private function fee(User $user) : int { if ($user.isChild()) { return 100; } else { return 200; } } private function fee(User $user) : int { if ($user.isChild()) { return 100; } return 200; } 34

Slide 35

Slide 35 text

条件分岐 ポリモーフィズム interfaceを使ってif文を無くします。 これで線形原理が実現できました。 interface User { public function fee() : int; } class Child implements User { public function fee() : int { return 100; } } class Adult implements User { public function fee() : int { return 200; } } 35 private function fee(User $user) : int { if ($user.isChild()) { return 100; } return 200; } $user.fee();

Slide 36

Slide 36 text

条件分岐 ポリモーフィズム ポリモーフィズムを使えば新しい区分の追加もクラスの追加のみです。 ちなみに, 生成はFactoryクラスで分岐させるようにします。 これはOCPも満たしています。 36 class Baby implements User { public function fee(): int { return 0; } } public static function factory(string $type) : User { $types = [ 'baby' => new Baby(), 'child' => new Child(), 'adult' => new Adult(), ]; return $types[$type]; } 生成してもらう

Slide 37

Slide 37 text

変数 スコープを絞る スコープとはアクセスを受け付ける範囲のことです。 アクセス範囲が大きいと, - どこで書き変わったかわからない - どこで参照されているかわからず変更できない といった事態になります。 基本的にprivateにしてクラス内, 関数内, 宣言した場所から数行の利用に留めましょう。 37

Slide 38

Slide 38 text

変数 再代入をしない 基本的に変数の書き換えはデバッグを難しくします。 書き換えの場合は新しい変数を作成しましょう。 破壊的変更も受け付けてはいけません。(Immutable, mutable) これは変数の観点からみた冪等性とも言えます。 38

Slide 39

Slide 39 text

関数 小さくする 関数に関してはこれが全てです。 そのためには - 関数内では1つのことを行う - switch文をなるべく避ける - 引数は0もしくは1, 最高でも3つまで - フラグ引数を避ける 39

Slide 40

Slide 40 text

関数 1つのことを行う 関数が1つのことを行っているかは「1つの関数に1つの抽象レベル」になっているかでわ かります。 コメントでセクション分けするのではなくコメントを英訳した関数を作り呼び出しましょう。 40

Slide 41

Slide 41 text

クラス 小さくする 関数の小ささの指標は物理的な数でした。対してクラスの場合は責務の数です。 クラスは変更の原因となるものが1つでなければなりません。 これを単一責任の原則と呼びます。 クラス名が曖昧なほどその責務が大きくなりがちです。 41

Slide 42

Slide 42 text

境界 サードパーティーへの依存に対抗するためにインターフェースを使用します。 境界を設けることで, 次のメリットがあります。 - インターフェースの実装部を変えるだけでサードパーティーを交換できる - テストがしやすくなる 42

Slide 43

Slide 43 text

オブジェクト指向 多くの技術がプリンシプルを達成することを目的に作られたと書きましたが, オブジェクト 指向もその1つです。 オブジェクト指向をコンセプト通りに使うことで, 多くのプリンシプルを達成する手助けに なります。 特にポリモーフィズムとカプセル化は非常に強力なためぜひ活用しましょう。 43

Slide 44

Slide 44 text

オブジェクト指向 ポリモーフィズム ポリモーフィズムとは条件分岐部分でみたように, 様々な区分を同じ型で扱え, 同一型で の関数の存在を保証したものです。 インターフェースを使うことで実現できます。 これはOCPと線形原理を可能にすることを説明しました。 もう一つ, ポリモーフィズムはテスト容易性にも深く関わっています。 これを説明するには依存とDIを説明しないといけないのですが, それはClean Architectureの講義でやると思うのでこのスライドではパスします。 44

Slide 45

Slide 45 text

オブジェクト指向 カプセル化 カプセル化とはデータとロジックを1クラス内に隠蔽することです。 このようにすることである概念を1つのクラスで表すことができます。 ある概念に対するデータとロジックが1つのクラスにまとまる。つまり, これは凝集性をあ げて, 結合度を下げることに大きくつながります。 publicなインスタンス変数をなるべく避けましょう。 45

Slide 46

Slide 46 text

オブジェクト指向 継承は? もう1つの要素である継承は少し危険な機能です。 多くの依存を生み出したり, 神クラスと呼ばれるなんでもクラスを作る原因になったりしま す。 必要な場合は1度にとどめるよう心がけましょう。 46

Slide 47

Slide 47 text

書式化 諸説ありすぎるので言及しません。 基本的にはLinterやFormmatterなどによって自動的に整形します。 47

Slide 48

Slide 48 text

参考文献 [1] Dustin Boswell Trevor Foucher著 角 征典訳, リーダブルコード より良いコードを書くためのシンプルで実践的なテクニック , O’REILLY, 2012 [2] Robert C. Martin著 花井 志生訳, Clean Code アジャイルソフトウェア達人の技 , ASCII DWANGO, 2017 [3] 上田 勲, The Principles Of Programming 3年目までに身につけたい一生役立つ 101原理原則, 秀和システム, 2016 [4] 増田 亨, 現場で役立つシステム設計の原則 変更を楽で安全にするオブジェクト指向の原則技法 , 技術評論社, 2017 48