プリンシプルオブプログラミング 3年目までに身につけたい 一生役立つ101の原理原則 のメモ。めちゃくちゃ広く浅く書いてあって誤解しかねないところとかもあるんだけどまあ抑えるところは抑えてる気がするので悪い本じゃなかった気がする。ただ本質的な理解を導くような本じゃなくて、こういうのあるよ、こういうのあるよってひたすら言っていく感じ。出典とかも幅広いので入り口としてはまあ悪くないのかなあと思った。
3章まとめ
プログラミングのセオリー
良いプログラムは下記の3つを備えている。
読みやすさ
シンプルさ(必要とされる機能以上のことをしない)
柔軟性(修正・変更・拡張しやすい)
これを実現するのにやるべきことは
変更した結果がなるべく他に影響を及ぼさないようにする(局所化)
繰り返しや重複を減らす
ロジックとデータを一体化する(クラス化・モデル化)
対称性を持たせる
宣言的に書く?
関心の近いコードを近くに集める
アーキテクチャ
3.11 アーキテクチャ根底技法
よく利用される手法
抽象
カプセル化
情報隠蔽
パッケージ化
関心の分離
充足性・完全性・プリミティブ性
ポリシーと実装の分離
インターフェースと実装の分離
参照の一点性
分割統治
3.12 抽象(abstraction)
ある問題を解決するためにプログラムで表現しようとするときには、問題に登場するオブジェクトを、抽象化したり一般化したりすることが重要。
オブジェクトの抽象化は、オブジェクトの性質のうち、問題と関わりのない性質を無視すること。余計な情報がなくなれば、問題を取扱いやすくなる。抽象化は、性質を捨てる動作であることから「捨象」とも言う。
オブジェクトの一般化は、複数のオブジェクトの共通点を見つけること。すると、一つのオブジェクトの知識を使いまわすことができるので、似た問題を解決する負担を減らすことができる。
3.13 カプセル化(Encapsulation)
カプセル化とは、関係のあるデータとロジックだけを集めてモジュールにまとめること。関係のない事柄はモジュールの外に追い出す。影響範囲がモジュールの中に閉じるようにする。これによって、コードが
わかりやすくなる
変更しやすくなる
再利用しやすくなる
問題を分解できる
関係のない事柄がモジュールに混ざっていると、上記の利点が失われてしまう。
3.14 情報隠蔽(information hiding)
モジュールは、内部に持っているデータやロジックに直接アクセスするような機能は提供せず、必要な部分だけにアクセスするように機能提供するほうがよい。
モジュールにできることを減らせば、余計な処理を差し込まれたり、不正な使い方をされたりすることがなくなる。また、モジュールの利用者は、実装がどうなっているかに注意を払うことなく、気軽に利用できる。
3.15 パッケージ化(packaging)
大量のモジュールが作られると、複雑になる。そこで、モジュールを意味のある単位に分類して、グループ化したものをパッケージと呼ぶ。パッケージはソフトウェアの論理構造を物理構造に反映したもの。パッケージは最初から作るのではなく、モジュールが増えてきてから作るのが基本。
3.16 関心の分離(separation of concerns)
関心(concern)という言葉はふわふわしていてよくわからないが、ここではソフトウェアの機能や目的のことを指すものとする。コードを関心によって分離するのはごく自然なこと。わかりやすい例としては MVC パターンがある。MVC はビジネスロジック・ユーザへの表示・入力の3種類に関心事を分割している。アスペクト指向プログラミングも、関心の分離を目的とした手法らしい。
3.17 充足性・完全性・プリミティブ性(sufficiency, completeness, primitiveness)
モジュールがもつべき性質を表す言葉
充足性: モジュールが要件を満たす
完全性: ???(あらゆる入力に対して動作するということかな?)
プリミティブ性: モジュールが提供する機能が、プリミティブである。
プリミティブの例:要素を追加する add はプリミティブ。要素を10個追加する add10 はプリミティブでない。絶対に add10 が不要かというと、そうとも言えないが基本的にはいらないはず。
3.18 ポリシーと実装の分離(separation of policy and implementation)
モジュールはポリシーモジュールと実装モジュールがある。
ポリシー: ビジネスロジックに依存する
実装: ビジネスロジックに依存しない
実装モジュールは再利用可能なので、それ単体でライブラリとかにできるかもしれない。
3.19 インターフェースと実装の分離(separation of interface and implementation)
モジュールのインターフェースと実装は分離するべき。
インターフェース: モジュールの機能と使い方を定義するもの
実装: モジュールの機能の内部実装
モジュールを利用するとき、モジュールのインターフェースだけ理解すればよく、内部実装のコードを知らずとも利用できるほうがよい。そのほうが使いやすいし、モジュールに修正があったときに巻き込まれずに済む。
3.20 参照の一点性(single point of reference)
変数の定義はなるべく一回だけにする。変数を使う場合は immutable な変数を使う。関数はなるべく下記の性質=参照透過性をもたせる。状態変化を気にしなくてよいのでコードが読みやすくなる。
関数の実行結果が引数によってのみ定まる(純粋関数)
関数が副作用を持たない
3.21 分割統治(devide and conquer)
大きな問題は、分割して解決する。
3.22 アーキテクチャ非機能要件(non-functional requirement for architecture)
非機能要件とは下記のものをさす。軽視されやすいので注意が必要。できるならテストもしたほうが良い。あとで一個ずつ説明する。
変更容易性
相互運用性
効率性
信頼性
テスト容易性
セキュリティ非機能要件とは、下記の物をさす。
気密性(confidentially): 第三者からのアクセスを防止する性質
完全性(integrity): 情報が欠損したり、改竄されたりしない性質
可用性(availability): 認可を受けた人がいつでも情報を利用できる性質
セキュリティ非機能要件の検証には、脆弱性を探して攻撃を試みるペネトレーションテストが使われる。専門的な技術・知識がないと難しい。脆弱性検証用のツールを使えば、テストの品質が上がるかもしれない。アウトソーシングしてもよい。f
セキュリティ対策のためにパスワードを長くしたり、操作を複雑にしたりするのはユーザの利便性を損なう。バランスに注意。
3.23 変更容易性(changeability)
ソフトウェアが変更しやすいこと。通常、ソフトウェアは変更・拡張され続けるものなので、変更しやすいアーキテクチャには価値がある。アーキテクチャは下記の性質をもつようにするほうがよい。
保守性: バグの修正がしやすい。(モジュールが独立している)
拡張性: 機能を追加しやすい。(モジュールの結合度が小さい)
再構築: モジュールの再編成がしやすい。
移植: 他のプラットフォームに乗り換えやすい。
柔軟性を持たせる場所とそうでもない場所を区別したほうが良い。ソフトウェアが経年劣化していくという考え方(ソフトウェアエージング)がある。変更に耐えられずアーキテクチャが維持できなくなるのが主な理由。がんばって再構築していこう。
3.24 相互運用性(interoperability)
他のソフトウェアと連携できること。ファイルを通じて連携するか、特定のプロトコルで通信するかは問わない。このような外部ソフトウェアへ接続するときは何かしら規格を使うのがよい。
3.25 効率性(efficiency)
計算機資源を効率的に利用できること。メモリやCPUの使用量をなるべく少なく、そして計算の実行時間が短いほうが効率が高い。
3.26 信頼性(reliaiblity)
ソフトウェアに障害が起きたり、不正利用されても機能を維持できること。2つに分解できる。
障害耐久性(fault tolerance): 障害発生時でも、正常に動き続けること。(2重化など)
堅牢性(robustness): 不正な操作を受けても、正常に動き続けること。
信頼性を高くするのもコストなので、妥協するかどうか判断が必要。たとえば医療用ソフトでは高い信頼性が必要だが、家庭用の家計簿ソフトではそれほど信頼性は必要ない。
3.27 テスト容易性(testability)
テストを効果的・効率的にできること。
モジュールの依存関係が高いとテストが難しい。
3.28 再利用性(reusability)
ソフトウェア(or その一部)を再利用できること。再利用する部分は独立してビルドできるようなパッケージにすると良い。再利用可能なコードを書くのは、そうでないコードを書くよりも3倍難しいと言われている。そして少なくとも3種類の利用ケースでテストしなければならないと言われている。
3.29 7つの設計原理(seven design principles)
コードの価値基準に関する経験則。
単純原理
同型原理
対称原理
階層原理
透明原理
明証原理
安全原理
線形原理
3.30 単純原理(simplicity principle)
コードは、シンプルであるほうがよい。
複雑なところにバグが生じやすい。素人のようなコードであってもそれが好ましいこともある。
3.31 同型原理(isomorphism principle)
コードは、同じことは同じように扱う。
たとえば、同じ重さなのに、2つの単位系を取り扱っているコードはバグを含みやすい。
3.32 対称原理(symmetry principle)
コードは、対称性を持つべき。
予測しやすい。set/get, start/stop など。
3.33 階層原理(hierarchy principle)
コードは、階層を持つべき。
理解しやすい。主従関係など。
3.34 線形原理(linearity principle)
コードは、一直線に実行されるべき。
理解しやすい。なるべく、分岐したり状態が変化したりしないようにする。
3.35 明証原理(clearity principle)
コードは、明らかに正しいものであるべき。
理解しやすい。
3.36 安全原理(safty principle)
コードは、安全に実行できるべき。
壊れにくい。どの分岐にも当てはまらないケースや、NULLが与えられるケースも、カバーしておくのが良い。
3.37 UNIX 思想(unix culture)
設計に関する17の経験則
モジュール化
明確性
組立部品
分離
単純性
倹約
透明性
安定性
表現性
驚き最小
沈黙
修復
経済性
生成
最適化
拡張性
3.38 モジュール化
なるべく独立したものにするべき。モジュールの相互依存関係をなるべく少なくする。
3.39 明確性
可読性を大事にするべき。 何度も読み直して意味がわかるプログラムは保守コストが高い。
3.40 組立部品
プログラムが部品として働くようにする。 入出力が決まっていれば、他のコマンドと自由に組み合わせられる。 UNIX的にはテキスト入力を受けてテキスト出力するプログラムをフィルターというらしい。
3.41 分離
汎用機能と専用機能を分けよう。 UNIX的には汎用機能をメカニズム、専用機能をポリシーというらしい。 レンダリングエンジンとかはメカニズムとして作られている。
3.42 単純性
シンプルなものにするべき。
3.43 倹約
行数を大きくしない。
3.44 透明性
ソフトウェアのインターフェースを見て機能を想像できるようにする=透明性がある。 ソフトウェアを動かしているときに内部状態が見えるようにする=開示性がある。
3.45 安定性
予定しない状況でも動く。バグを少なく。場合分けはなるべく少なく。網羅的なテスト。
3.46 表現性
ロジックよりもデータ。ロジックが複雑であるよりも、データ構造が複雑な方がわかりやすい?
3.47 驚き最小
インターフェース(外見)に対して、内部で奇抜なことをしたり、巧妙なことをしたりするのはよくない。 また UNIX のコンフィグとかを真似して、習慣に従うほうが驚きは少ない。
3.48 沈黙
表示する情報は最小限にする。冗長モード以外で、デバッグメッセージをユーザに出さない。
3.49 修復
エラーを目立たせる。エラーが起きたまま動くと被害が拡大してしまう。 誤った入力を受け入れてもよいが、出力は厳格であるべき。
3.50 経済性
開発機の性能や、インターネット環境、サブモニターの有無など、お金をかけてプログラミングの効率化できることはやるべき。 プログラマの効率を落とすほうが損失になる。
3.51 生成
同じコードを繰り返し書く場合は、コードを生成するツールを用意する・利用する。
3.52 最適化
メモリ・CPUの使用量を減らしたりする最適化(optimization)は、必ずしも毎回必要なものではなく、最初から考慮しなくてもよい。
3.53 多様性
多様性を受け容れるべき。
3.54 拡張性
拡張できるような柔軟性を持たせるべき。
3.55 UNIX 哲学
3.56 Small is beautiful
3.57 Make each program do one thing well
3.58 Build a prototype as soon as possible
3.59 Choose portability over efficiency
3.60 Store numerical data in flat ASCII files
3.61 Use software leverage to your advantage
ここでの leverage というのは再利用することを指しているっぽい 既存ライブラリ使っていこうねということかもしれない
3.62 Use shell scripts to increase leverage and portability
既存ライブラリ使うのにはシェルスクリプト活用しようねという感じ。 シェルスクリプトは、可読性とか機能性はいまいちだけど移植性があるので。 ソフトウェアを組み合わせるための言語をグルー(接着剤)言語と呼ぶらしい。
3.63 Avoid captive user interface
ユーザーと対話しないほうが汎用性があるのでなるべくそうしよう。
3.64 Make every program a filter
(テキストを入出力する)フィルターとして機能するプログラムを作ろう。
4章 視点
4.1 凝集度(cohesion)
モジュールの純粋さを表す数値。高いほどよい。
暗合的強度 ... 純粋でない。無関係な機能が集まっている。
論理的強度 ...
時間的強度 ... 時間的に同時に行われる機能が集まっている。
手順的強度 ... 手順的に同時に行われる機能が集まっている。
連絡的強度 ... 手順的に同時に行い、かつデータも共有している機能が集まっている。
情報的強度 ... 一つのデータ構造に対する操作機能が集まっている。
機能的強度 ... 一つの機能を実現するための機能が集まっている。
4.2 結合度(coupling)
モジュールの関係の細さを表すもの。高いほどよい。
内容結合 ... コードを共有している(アセンブリ言語などにみられる)
共通結合 ... グローバル変数などでデータを共有している
外部結合 ... パブリックメンバ変数でデータを共有している
制御結合 ... 引数でデータを共有しており、それが内部実装に言及している
スタンプ結合 ... 引数でデータを共有しており、それが使用しないデータ構造まで含んでいる
データ結合 ... 引数でデータを共有しており、それはスカラ値である
4.3 直行性(orthogonality)
2つのモジュールが独立していて分離していること。良い。 最も成功したモジュールの例としてはネットワークプロトコルがある。 これらはレイヤーを持っていて、それぞれのレイヤーは必要な関係しかもってない。
4.4 可逆性(reversibilty)
ある操作を実行した時、もとに戻せること。
4.5 コードの臭い(bad smell in code)
食品が劣化していくと悪臭を放つように、コードも品質が劣化していくとある種の徴候を示す。 コードの重複・関数が長過ぎる・モジュールが大きすぎる・名前が不適切…等 そういう性質が見られるときコードが臭うなどと言ったりする。
4.6 技術的負債(technical debt)
スケジュールの都合で品質の低いコードをリリースしたとする。 これらをリファクタリングせずにプログラムを成長させようとすると品質の低いコードが 更に他のコードの品質に影響を与えて、その繰り返しでプログラムがたち行かなくなってしまう。 このことを借金が利息によって膨らむことに例えて技術的負債と呼んだりする。
チームの文化にも関係がある。 品質低いコードがあちこちに見られるようだと気にしなくなって全体の品質が落ちる。
5章 習慣
5.1 Three great virtues of a programmer
怠慢: 楽をするための手間は惜しまない
短気: 効率が悪いことに怒って改善する
傲慢: 良いコードを書くという誇りをもつ
5.2 Boy Scout Rule
自分のいた場所は、そこを出ていく時、来た時よりもきれいにしなければならない
5.3 Proverb of performance tuning(パフォーマンスチューニングの箴言)
エキスパートであっても、速さよりもまずは読みやすさを重視するべき。 最適化(optimization)は、確実にコードを複雑にしてしまう。本当に必要なとき以外は最適化するべきでない。
5.4 Egoless programming
自分自身も間違いを犯すということを理解し、受け入れます。
書いたコードは、自分自身ではありません。
どれほど極めたと思っていても、上には上がいます。
相談なしに、コードを書き直しません。
自分よりもスキルが劣る人にも、尊敬と敬意と忍耐を持って接します。
世界で唯一変わらないことは、変わるということだけです。
本当の権威は、地位ではなく、知識から生じます。
信じるもののために戦います。ただし、負けは潔く受け入れます。
部屋に籠りきりはいけません。
「人に優しく、コードに厳しく」して、人ではなくコードを批評します。
5.5 One by one
階段を登るように一つずつ問題解決していきましょう。
5.6 There's more than one way to do it(TMTOWTDI)
perl のスローガン。やり方はひとつじゃない。 ツールを提供する上でいくつかのやり方を用意するという方針。
※ Ruby では Diversity is Good を掲げているが最優先はしてない
6章 手法
6.1 Tracer ammunition(曳光弾)
しっかり検討してコードを試験的に作ること。 プロトタイプとの違いは、コードの核となる部分は捨てないで利用するという点。 プロトタイプの後に曳光弾を作るようなイメージ。
6.2 Design by Contract(DbC)
関数と、関数の呼び出し元が契約を結んでいるという考え方がある。 これを契約による設計(DbC)とよぶ。
※詳細よくわからなかった。
6.3 Defensive programming(防御的プログラミング)
不正な入力が来ることを想定する。 バリケード戦略:安全でない領域(dirty room)と安全領域(clean room)をわける。
※エラー処理とアサーションの違いわからん
正当性重視:エラーが起きたら停止する(不正な結果を返さない)
堅牢性重視:エラーが起きても停止しない(不正な結果でも別の値で代用したりする)
6.4 Dogfooding (eating your own dog food)
自分で作ったソフトウェアは自分で使ってみよう。
6.5 Rubber ducking
ゴムのアヒルに順を追って説明することで問題の自己解決を促す。
6.6 コンテキスト
コードの見出しとか名前空間とかはコンテキストを伝えるので重要。 あとに続くものの理解を助けてくれるので、先行オーガナイザーと呼んだりもする。 コンテキストスイッチは減らそう。
7章 法則
7.1 ブルックスの法則
遅れているプロジェクトにメンバーを追加しても遅れを取り戻すことはできない。
7.2 コンウェイの法則
ソフトウェアの構造は組織の構造を反映する。
ある問題を解決しようとする時、 チームが3つに別れてるなら3つのモジュールに分解されるだろうし、 チームが4つに別れてるなら4つのモジュールに分解されるだろう。
先にアーキテクチャを考えてから組織を作ると、そうならずに済む。
7.3 割れ窓理論(Broken Window Theory)
割れた窓があると、他の窓が割れても気にしなくなる。そうして建物は荒廃していく。 それとおなじことがコードにも起こる。
7.4 エントロピー増加の法則
放っておくと自然と無秩序になっていく。 場当たり的な修正はしないようにしましょう。
7.5 80:10:10 の法則
ソフトウェアは80%のことは簡単にできる。10%は相当な努力を要する。10%はできない。 万能のツールは作れない。
7.6 ジョシュアツリーの法則
人は、名前のないものを認識することができない。
図書館で図鑑を見てジョシュアツリーという樹を知った。見たことがないと思った。 しかし帰り道でジョシュアツリーを見つけてしまった。これまでずっと気が付かなかったということだ。
ユビキタス言語を定めて問題を整理しよう。
7.7 セカンドシステム症候群
2番目のバージョンは余計な機能を盛り込みすぎて品質が悪くなる事が多い。 セカンドシステム以降も同じことが起こりやすいので注意。 機能が増えすぎて品質の下がったソフトウェアをフィーチャークリープという。
※ microsoft office とかね
7.8 車輪の再発明
既にそれを実現するライブラリがあるのにコードを書いてしまうこと。 情報共有していくことが大事。
7.9 ヤクの毛刈り(Yak Shaving)
次から次へと出てくる問題に立ち向かっているうちに、何が目的だったかわからなくなってしまうこと。