書籍 プリンシプルオブプログラミング 第3章 上 まとめ
3章 プログラミングセオリー 〜 アーキテクチャ根底技法
思想 ~プログラミングのイデオロギー~
プログラミングセオリー
最高のコード:拡張しやすい、余分な要素が存在しない、読みやすい、理解しやすい
価値観を技術の選択基準に
プログラミングにおける問題解決は、その状況に応じて適切な技術を選択する必要がある。 下記の価値観は、個別技術を適用する際の理由になる。
- コミュニケーション
- シンプル
- 柔軟性
フォース
フォース:技術を適用する際に考慮する観点
- 要件:解決策が満たす必要のある
- 制約:課題に含まれている
- 特性:解決策に望まれる
これらを考慮した上で、解決策を選択していく。
意識すること
言語・ツール・技術・問題領域などといった、利用するモノを理解した上で作業に取り掛かるべき
-「何のために?」という本質を理解しておくことで、初歩的なミスや品質的な問題を減らすことが出来る
価値をプログラミングに繋げる原則
3.2 コミュニケーション
コードは記述している時間よりも読んでいる時間の方が圧倒的に多い。 読みやすいコードを記述することで、コードを読む時間を減らせるため、プログラマ同士のコミュニケーションをより円滑にできる。
意識すること
コードを書く際に「このコードを誰かが見たらどう感じるだろう?」と考えながら記述する
3.4 シンプル
コードの複雑性を排除する
コード内の「余分な複雑性」には一切の価値はなく、ただの禍根でしかないため、極力排除すべきである
コードの本質性は独立させる
コードの本質的なところと余分なところを混ぜ込まないようにする
コミュニケーションとシンプルはどっちが優先?
シンプルとコミュニケーションの両立が難しくなった際は、多少冗長でも「コミュニケーション」を優先させる
3.4 柔軟性
コードの変更は必ず行われるので、変更を考慮した「適切に柔軟な設計」を心掛ける
- 余計な柔軟は複雑化を招くので注意
3.5 結果の局所化
変更の影響を抑える
コードを変更した際の影響を最小限に抑える - 関係ないところに飛び火しない設計
発見、修正が容易になる
結果を局所化することで一部分の検証のみで済ませることができ、余計なコストを減らせる。
3.6 繰り返しの最小化
コードを分割して管理
重複を防ぐためには、コードを小さく分割することが重要。
- 共通の部分を探し出して関数化
3.7 ロジックとデータの一体化
データと操作は近くに
「ロジック(操作)」と「ロジックで扱うデータ」はなるべく近くに置くこと。
- コードを読む量を減らせる
3.8 対称性
コードに一貫性を持たせる
同質なコードは、どこで実行されても同じように表現されるよう設計する。
- 追加メソッドがあれば削除メソッドもある
- モジュール内のデータの生存期間を統一
- 抽象化レベルの統一
3.9 宣言型の表現
宣言型を取り入れる
宣言型で無い言語でも、アノテーションやDSLなどの宣言型の表現手法を取り入れること。
- 命令形特有のフローが無くなり、読みやすくなる
3.10 変更頻度
変更理由でグループ化
同じタイミングや同じ理由で変更されるコード同士をグループ化する。
- 変更によって起こりゆる影響範囲を狭められる
アーキテクチャ根底技法
3.12 抽象
抽象化を行うことで、対象物を以下のように取り扱うことが出来る。
- 無駄な要素を排除し、本質を捉える
- 必要な要素のみを残すので、理解しやすくなる
- 汎用化することで他の場所でも取扱やすくできる
3.13 カプセル化
関連の深いデータや機能を集約してモジュール化することで、関係性を強め無駄な要素が混じるのを防ぐ。 また、以下のようなメリットを得られる。
- 必要な要素だけになるため、コードが見やすくなる
- コードの変更が容易になる
- 変更時の影響をモジュール内で抑える
- 関数化と同様、機能が独立するため再利用しやすくなる
- 小さい単位に分割されるため、複雑な問題への対処ができる(小回りが効く)
3.14 情報隠蔽
必要ないものは見せない
モジュール内のデータや機能を外部からアクセスできないようにする。
- 無駄な関係を作らないことで、モジュールをシンプルにでき、変更などによる影響を最小限に留めることができる。
3.15 パッケージ化
モジュール同士をグループ化
モジュール自体をまとめる(パッケージ化)ことで、機能をモジュール化したときのようなメリットを得られる。
ボトムアップで設計
トップダウンだと正確に決めることが困難なため、ボトムで必要なパッケージ化を行う (上だと詳細なことを把握できない)
3.16 関心の分離
関心ごとにコードを分離
大きな機能や目的(関心)ごとに機能を他の機能から分離することで、関心ごとに独立して操作を行える。
3.17 充足性、完全性、プリミティブ性
モジュールが担っている抽象の表現において、下記の要素全てが成立していなければならない。
充足性
- 機能が十分存在しているか
- 一貫性を持っているか
完全性
- 全ての特徴を完全に備えているか
- 必要なものを表現できない状況であってはならない
プリミティブ性
- 必要なものだけで純粋であるか
- 無駄なものが無い状況
3.18 ポリシーと実装の分離
「ポリシー」と「実装」は混ぜない
ポリシーモジュール
- ソフトウェアの前提に依存する、ビジネスロジックやその他のモジュールに対する引数の選択を行う部分
- 特定のソフトウェアに特化している
- ソフトウェア側で変更が生じると、モジュール側でも変更を強いられる
実装モジュール
- ソフトウェアの前提に依存しない、独立したロジック
- 特定のソフトウェアに依存しない「純粋」なモジュール
- 他のソフトウェアでも再利用可能
それぞれを把握、意識して設計し、別モジュールに分けてコーディングをする。 モジュールの分離が不可能な場合は、モジュール内に明記しておく。
3.19 インタフェースと実装の分離
構成はインタフェースと実装から
インタフェースパート
- モジュールが持つ機能を定義し、使用方法を定める部分
実装パート
- モジュールが持つ機能を実現しているコードの部分
- 内部で使用するロジックとデータが含まれる
インタフェースと実装を分離させること
- 使用者が実装部を理解していなくても、インタフェースを利用できる (シンプル)
インタフェースを用いて設計
- 実装ではなく、インターフェースを意識して設計する
- モジュール間での呼び出しも、実装を直接叩くのではなくインタフェースを経由して呼び出す
- 実装側も、インタフェースからの呼び出しのみに対応する
3.20 参照の一点性
定義は一回だけ
モジュールの要素の定義は、最初の一回のみにする(後から変更しない)
- モジュールから得られる結果が、いつどこで取っても変わらないようにする
- コロコロ変わる変数を広域で利用しない
単一代入
基本的に変数への再代入を行わない。 なるべく定数を利用したり、Javaでいう「final」を定義して再代入を制限すること。
3.21 分割統治
大きな問題を小さく割る
解決が難しい大きな問題は、小さな問題に分割して各個撃破していく
- 問題を小さくすると、一つ一つの問題が解決が容易になるため
問題が発生してから分割するのではなく、あらかじめ制御が容易になるくらいの規模に分割しておく。