プリンシプル オブ プログラミング 3年目までに身につけたい 一生役立つ101の原理原則 - 秀和システム あなたの学びをサポート
本を読む目的
いいプログラミングをするにはどうすればいいかに対しての指針が書かれていそうなので読みます。
読書メモ
# 第1章 前提
## 1.1
- ソフトウェアの本質には、その困難性を示す4つの性質がある
- 複雑性: ソフトウェアは大きくて複雑
- 同調性: ソフトウェアは実世界に同調し続けなくてはならない
- 可変性: ソフトウェアは変化し続けなければならない
- 不可視性: ソフトウェアは概念の集積であり、目には見えない
- 物事の2つの側面
- 本質: それがなければ対象物とは言えなくなる性質
- 偶有: 副次的・付随的な性質 (なくても対象物と言える性質)
- 開発現場で必要なスキルと言われるものが当てはまる (e.g. ビルド環境・プログラミング言語)
- 本質的な部分の活動に注力するために、偶有的な部分は大きな役割を果たす(本質的な部分と対照的に、容易に改善できるため)
## 1.2
- 設計とは、「基本設計、詳細設計、プログラミング、テスト、デバッグ」までのすべて
- そのアウトプットが、設計書である「コード」
- 製造はコードを元にして行うリリースビルドになる
- ロゼッタストーン
- 保守担当者に対する簡潔な手引書
- 開発環境を理解するための情報
- ソフトウェアのアーキテクチャを理解するための情報
## 1.3
- プログラミングにおける1つ1つの判断は、「そのコードが変更される」という前提で行う -> 変更に強いコードを書く
# 第2章 前提
## 2.1 KISS
- Keep It Simple, Stupid.
- コードを書く時の最優先の価値
- 単純性
- 簡潔性
## 2.2 DRY
- ソフトウェア開発に関わるすべての活動に適用できる (コードだけじゃない)
- 繰り返し作業の自動化
- e.g. CI
- 重複がやむを得ない場合もある
- e.g. インピーダンス・ミスマッチ (プログラミングと、それが使用するサービスの溝を埋める情報部分)
- 類義語: OFOP (One Fact in One Place)
- 対義語: WET (Write Every Time)
## 2.3 YAGNI
- You Aren't Going to Need It.
- 汎用性よりも単純性を考える
## 2.4 PIE
- Program Intently and Expressively.
- コードを書くときは、意図を明確に表現するように書く
- コードだけがソフトウェアの動作を「正確に」「完全に」知るための手がかり
- 要件定義書: どのようなものが欲しいか
- 基本設計書: どのようなソフトウェアで要件を実現するか
- 詳細設計書: どのような構造でソフトウェアを作るのか
- コードは「書かれる」ことよりも「読まれる」ことの方がずっと多い
- 読む効率を重視すべき
- コードはどこまで行っても、「What」と「How」しか表現できない
- Whyはコメントを使用する必要がある
- cf https://twitter.com/t_wada/status/904916106153828352
## 2.5 SLAP
- Single Level of Abstraction Principle.
- 関数の抽象レベルにしたがって分割していき、同じ関数に属するコードの抽象レベルをすべて統一する
- 要約性と閲覧性を同時に満たす
- cf コードと書籍のアナロジー
## OCP
- Open-Closed Principle
- コードが以下の2つの属性を同時に満たすように設計すること
- 拡張に対して開いている (コードの振る舞いを拡張できる)
- 修正に対して閉じている (コードの振る舞いを拡張しても、その他のコードは全く影響を受けない)
- 変化に晒されながらも、長期に安定したソフトウェアを設計するために必要な属性
- インターフェースを使った設計を行う (クライアントとサーバーの間に入る緩衝材)
- すべてに適応すればいいものではない (変更が発生しなければ、ただコードを複雑にしてしまうだけである)
- 対応として、変更が発生するのを待つ戦略 (2回変更が発生した時に初めて対処する)
## 2.7 名前重要
- リアルタイムでないコミュニケーションを円滑にするために、名前には最大限の配慮が必要
- 名前は、コードを読む人のユーザーインターフェース
- 名前をつけるときは、「使う側」「読む側」の視点に立って命名する
- 名前は効果と目的を説明する (not 手段)
- テストコードを書くことで使う側の視点に立って考えられる
- 名前可逆性
- 名前は、その元となった内容の説明文を復元できなければならない
# 第3章 思想
## 3.1 プログラミングセオリー
- プログラミングの最大の関心ごとは、「最高のコード」を作り上げること
- 最高のコード = 「拡張方法が多く存在し、余分な要素が存在せず、読みやすく、理解しやすい」コード
- セオリーで示される価値が、個別技術の「適用理由」になる
- 最高のコードを実現するための3つのセオリー
- コミュニケーション
- シンプル
- 柔軟性
## 3.2 コミュニケーション
- ドキュメントの本質は「コミュニケーションツール」
- コードが持つ側面
- コンパイラ・インタプリタへの入力
- 人に見せるドキュメント (コミュニケーションとしての価値を置くようにする)
## 3.3 シンプル
- コードがシンプル = コードから「余分な複雑性」が取り除かれた状態
## 3.4 柔軟性
- 即効果のあるコード以外は、我慢して書かないようにする
## 3.5 結果の局所化
- 変更の影響が、局所に留まるようにコードを構成する
- e.g. モジュール化
- 関係性の高いコードを密集させ、関係性の低いコード同士が依存しないようなコードにする
- -> 関係性の高いコードを集約してモジュール化する
## 3.6 繰り返しの最小化
- 重複を極力排除する
- e.g. 関数
- 繰り返しコードは「結果の局所化」原則を侵害して、変更コストを増大させる
- コードをたくさんの小さい部分に分割する
- 大きな塊を小さくすると、共通項が導きやすくなる
- コード内で色分けをする (「完全に同じ」、「とりあえず似ている」、「全く異なる」)
## 3.7 ロジックとデータの一体化
- ロジックとロジックが操作するデータを互いに近くに置くようにする
- 近く = 同じ関数 or 同じモジュール
- どのロジックとどのデータを近づけた方がいいかは最初からベストな解はわからない
- -> 最初は仮配置して、後から妥当な場所に移し替えていくのが効率的
## 3.8 対称性
- 同じ考えなら、コードのどの場所で現れても、同じように表現される
- グループ内のレベル合わせ
- 読む側にとって、一部を読むだけで他の部分を類推できるメリットがある
- 対称性の追求の副次効果として、重複を除去するための準備作業にもなる
## 3.9 宣言型の表現
- コードの意図を伝えるときは、できるだけ「命令型」よりも「宣言型」で表現すること
- 命令型: 問題の解法(データ構造とアルゴリズム)を記述する
- 宣言型: 問題の定義(解くべき問題の性質・その際に満たすべき制約)を記述する
- プログラミングのパラダイムが宣言型の言語を使用すると、自然と宣言型になる
## 3.10 変更頻度
- 同じタイミングで変更する要素は同じ場所に置き、異なるタイミングで変更される要素は別の場所に分けておく
- 「対称性」原則を時間に対して適用したもの
- 変更理由が1つなら、関連性が高いコードが集合していることになる(高い凝集性を満たしている)
- ロジックにもデータにも適用する
- 単一責任の原則(SRP)を満たすためにも必要
- モジュールを変更する理由は、1つより多く存在してはならない (1モジュールは1責任、ある役割に対して「ピュア」であることが望ましい)
## 3.11 アーキテクチャ根底技法
- モジュールの設計方針として10個ある (よいソフトウェア・アーキテクチャ構築のための基本原理)
- 抽象
- カプセル化
- 情報隠蔽
- パッケージ化
- 関心の分離
- 充足性、完全性、プリミティブ性
- ポリシーと実装の分離
- インターフェースと実装の分離
- 参照の一点性
- 分割統治
## 3.12 抽象
- 概念的に明確な線引きを行うこと
- 線引きに従って、あるモジュールを、それ以外のモジュールから明確に区別する
- 抽象の2つの観点
- 捨象
- 一般化
- 捨象
- 枝葉を取り除いて、対象物の本質をむき出しにする (真なる問題に集中する)
- 複雑なものに取り組むときに使用する
- 一般化
- 複数のものを共通の特徴によってグルーピングし、「同じ」と見なすこと (1つの事象における学びを他の場面でも適用できる)
- 異なる複数の対象に取り組むときに使用する
## 3.13 カプセル化
- 関連のあるデータとロジックをグルーピングして、1つのモジュールを定義する
- モジュールの膜で包み込みので、カプセル化
- メリット
- 関連のない要素が混じらないため、コードが見やすくなる
- 変更時の影響が、モジュール内に閉じることになる
- 影響度が明確なので、コードの変更が容易になる
- それぞれが独立した部品になるので、再利用性が高まる
- 小さい単位に分割されるため、複雑な問題に対処できる
## 3.14 情報隠蔽
- モジュールは、クライアントからは必要最小限度に公開された関数でしか操作できない
- モジュール内部のデータや、モジュール内部でのみ使用している関数は、外部からアクセスできないようにする
- カプセル化との違い
- カプセル化: 関係のある要素(データと関数)を1箇所に集めて、モジュール化すること
- 情報隠蔽: モジュールの内部状態や内部関数を隠蔽(外部からアクセスを遮断)すること
- ただし、両方の意味を含んだ、広義のカプセル化も使われる
## 3.15 パッケージ化
- パッケージは、ソフトウェアの論理構造(機能や責務)を、意味のある単位で格納するための物理コンテナの役割
- 機能を大量に作る -> 機能をまとめてモジュールを作る -> モジュールをまとめてパッケージを作る
- ソフトウェアのビルド方法を示すマップの位置付け
## 3.16 関心の分離
- 関心とは、ソフトウェアの機能や目的のこと
- 関心の分離とは、それぞれの関心に関連するコードを集めて、独立したモジュールにし、他のコードから分離すること
- アスペクト指向プログラミング(AOP)
- 関心の分離を目的とする技術で、特に「横断的関心事」(各関心ごとに横串で刺さるような関心ごと)をうまく分離する
## 3.17 充足性、完全性、プリミティブ性
- モジュールの担っている抽象の表現は、「充足」し「完全」で「プリミティブ」であるべき
- 充足性: モジュールが表現しようとしている抽象が、それを伝えるために十分であるか
- e.g. コレクションを表現しているときに、remove()が提供されているが、add()がない場合は不十分である
- 完全性: モジュールが表現しようとしている抽象が、すべての特徴を備えているか
- e.g. コレクションを表現しているときに、要素数を取得するsizeが提供されていなければ完全ではない
- プリミティブ性: モジュールが表現しようとしている抽象が、すべて純粋であるか
- e.g. コレクションを表現しているときに、要素を1個追加するadd()が提供できていれば、要素を10個追加するadd10()は必要ない
- モジュールが表現しようとしている抽象は、その意図が使う人に伝わり、使う人にとって有用でなければならない
## 3.18 ポリシーと実装の分離
- 1つのモジュールは、「ポリシー」 or 「実装」を扱う (両方を扱ってはいけない)
- 両方が混ざっていると、本来なら変更不要なタイミングで変更が必要になるなど、再利用ができなくなる
- ポリシーモジュール: 特定のソフトウェアの前提に依存する
- e.g. ビジネスロジック、その他のモジュールに対する引数の選択を行う部分
- 特定のソフトウェアに変更が生じると、ポリシーモジュールも変更を強いられる
- 実装モジュール: 特定のソフトウェアの前提に依存しない (純粋なモジュール)
- e.g. 独立したロジック部分
- 他のソフトウェアでも再利用できる
## 3.19 インターフェースと実装の分離
- モジュールは、「インターフェース」パートと「実装」パートの2つの分離した部分で構成する
- 分離できていることで、クライアントは実装の詳細を知る必要がなくなる
- クライアントへの影響を考えることなく、実装の修正を行うことが可能になる
- インターフェースパート
- モジュールが持つ機能を定義し、モジュールの使用方法を定める部分
- クライアントからアクセス可能な関数のシグネチャで構成される
- 実装パート
- モジュールが持つ機能を実現しているコード(モジュール内部のロジックとデータ)部分
- クライアントからアクセスできない
## 3.20 参照の一点性
- モジュール要素で、宣言され定義されるのは1回に限る
- 変数の値の変遷を追いかけないで済む、見通しの良いコードにできる
- 単一代入を使う
- 参照透過性とは、関数が2つの特性を持つこと
- 呼び出しの結果が、引数のみに依存する: 引数に同じ値を与えると常に同じ戻り値を返す
- 呼び出しが、他の機能の動作に影響を与えない: 関数が副作用を持っていない
## 3.22 アーキテクチャ非機能要件
- アーキテクチャ設計時に、非機能要件を考慮に入れる(後回しでは間に合わない)
- 機能のテストは「何をするか(What)」に注目する
- 非機能テストは「どのように動作するか(How)」に注目する
- 非機能要件を満たすために、合格基準を儲ける
- 目標がなければ、要件を満たすことはできない
- セキュリティの検証では、インシデント発生時の追跡可能性、監査能力の評価が求められる
## 3.23 変更容易性
- アーキテクチャの設計を考えるための側面
- 保守性: 障害が発生したコードの修正しやすさ
- 拡張性: 新しい機能の追加、不要な機能の除去のしやすさ
- 再構築: モジュール間の関係の再組織化を行うこと
- 移植性: 様々なプラットフォーム(OSや言語など)に適合させる際のやりやすさ
- ソフトウェアのどの部分が変更に対して高い柔軟性を持つべきか、逆に変更の生じない部分はどこなのかを見極める
- 柔軟性とシンプルのバランスを考慮する
## 3.24 相互運用性
- ソフトウェアはシステムの一部であり、独立して存在するものではない (他のシステムと頻繁に相互作用する)
## 3.25 効率性
- ソフトウェアが実行に伴うリソース使用において、適切な性能を引き出す能力
- 関節化と効率性のバランスを考慮する
- 関節化: モジュール間の直接の結合を避けるため、介在する「媒介モジュール」を導入すること
## 3.26 信頼性
- ソフトウェアが、例外的な場面(予期しない方法や不正な方法で使用)においても、機能を維持する能力
- フォールトトレランス、 ロバストネスという2つの側面がある