A Philosophy of Software Design : 読書メモ
書評ではありません。
読書メモです。
書籍情報
- タイトル: A Philosophy of Software Design
- 著者: John Ousterhout
読書メモ内容
- llm で情報抽出するのに必要なメモを残す
- 珍しく英語の本を読んだ。 この本は結構楽に読める。
メモ
1 章
- 複雑性とは何か?
- waterfall は機能しなかった。patch を当てるから複雑性が増えていく。 設計は、ソフトウェアのライフサイクル全てにspan している
- この本の目的は、complexity(複雑性) を最小化する方法を得る
2 章
- 複雑性とは
- システムの理解と modify を難しくするもの
- 症状として出てくるのは
- 変更点の増大 change amplification
- cognitive load
- nuknown unknown
発生原因は
- 依存
- web boackground color の例 page 感の依存は不明瞭で複雑
- 不明瞭
- 情報が足りない 変数名とか
複雑性はコードの安全な変更を大変にする
3 章
デザインが悪い時は staragegic に tactical mindset を求められることが多いけどね
tactical programming
- 早く終わるなら多少の complexty の増加は仕方ない
- 真のヒーローは、tactical tornado よりも仕事が遅くなる。
strategic programming
- 将来の変更・拡張を受け入れるデザイン
- 時間, reactive が必要
4 章
依存とは
- 引数の型
- 実行順とか interface
- 利用者が知るべき唯一のこと
- 利用中のモジュールの interface implementation & 他の interface だけ知れば利用できるべき
interface
- formal: language level で保証される
- informal コメントで説明するしかないこと。 xx の前に oo をしてください
- 複雑どはたいてい formal < informal
shallow module
- interface と implementation がほぼ等しいならば、 interface 学習ぶんの複雑性を追加することになる
5 章
- information hiding
- interface をシンプルにする
- easier to evolve system : tcp を変更したら tcp のモジュールだけ変えればいい
- information leakage
- interface leakage
- back-door leakage : 書式とか
クラスは肥大化する
- 含めるものをベテを含むから
- シンプルな interface を提供するから
6章
特化するよりも一般化のほうがsimple earu
some what - 今の問題を解くのに十分に易しく可能な限り一般的なインターフェース texteditor の例 backspace, delete -> [left, right) と一般化したほうが楽。
一般化の基準
- いくつも引数を追加せずに method をまとめられるか?
- 追加コードを書かずに使えるか? ... texteditorの例だと、1文字のinsert, delete は simple であるが、 loop しないと複数文字に対応できない。
上の層ほど具体的で、下層ほど抽象的になる。 ドライバなどは逆で、下が具体的で上が抽象的になる。
7 章
- pass through method を利用しているときは、設計の抽象度のレイヤがうまく機能していない可能性がある
- decorator も、レイヤーとして正しいかを疑う。 ... python のデコレータではなくて、あるオブジェクトに機能を追加するようなデコレータ デコレータを作成するよりも別のオブジェクトとして用意するべきかも
- 同じインターフェースが複数あるなら、抽象化がうまく機能していないかも。 ... テキスト と テキストラインで同じように挿入処理がある。
- pass throught variable ... react でよくあるように感じる。 多分慣れていないせい。
- この主張は納得し難いですね。 pass throught variable の方がまだマシに見える。
8 章
- 複雑性を上 (呼び出し先) から下 (呼び出し元) に移動するのが良い
- この考え方は - 下に移動するさせる複雑性が、移動させる先の機能と結びついていること - アプリケーションの他の部分の複雑性も単純にできるとき - インターフェースが簡潔にできるときに に行うと良い
例 http などの通信の時のリトライの間隔は呼び出し先ではなくて呼び出し元で適切な亜地を計算できるはず。
- インターフェースが簡潔になる
- プログラムの変更に対して強くなる。 適切な値が、モジュールの中で算出されているならば、どこに移動しても使えるということ。
9 章
10 章
エラーハンドリングは難しい
- エラーハンドリング中に別のエラーが発生する可能性がある
- どこで発生したかを解くてしようとすると冗長コードが増えるÏ
- エラーケースはテストが難しく、滅多に実行されない > “code that hasn’t been executed doesn’t work”. Ousterhout, John K. . A Philosophy of Software Design, 2nd Edition (English Edition) (pp. 79-80). (Function). Kindle Edition.
exception も interfac. exception を処理せずに caller に渡すということは、何もメリットがないのにinterfaceの複雑化をしている可能性がある。
- exception が発生しない関数の定義を考える。 ... define error out of existence
- 変数を削除する -> 変数が存在しないことを保証する こうすると存在しない変数の削除と言うエラーを処理する必要がなくなる。 .... 他の例として windows と unix のファイル削除の挙動の例
- 部分文字列取得 index 外の取得は error -> index があるところまで取得 (python はこうだったかな?)
- マスクする
- nfs はエラーせずにただ繰り返す
- exception aggregation
- error handler をせずに up level にながすことで、handler を減らすことができる。
- exception masking と背反する考えだが、最終的にexception handler を減らせる方が複雑性を減らせる。 laibrary のような複数箇所で用いられる場合は mask の方がよく、http request の gateway のようなところでは top level が良い。
- 本当か? masking も top level まで持っていければ、どちらにせよhandler は1箇所で良いように感じるよ。
- just crash
- どうしようもないものならばclashするのも手. out of memory など
例外情報は取得可能にしておく必要がある。 mask であろうと、 define error out of existenc でも
11 章
システムのデザインは2回行うこと。
- 悪いと思っている案であろうと、副案を出すことで、何が悪いのか?を明確にすることができる。
- どんなに賢かろうと、最初の試行で成功しない難しい問題はある
12 章
コメントは必要なものである という主張に対して考えられる反論とその反論。
- いいコードにはコメントは必要ない。
- 反論: いいコードでもコメントは必要。 特に interface の説明など。
- 反論: モジュールの利用者にコードを読むことを強要するのは、抽象化の観点でよくない。
- コメントを書く時間がない。
- 反論: 冷静に考えるとコメントを記述するのにかかる時間は開発の10%に満たない程度であろう。そのコストはすぐに回収できる
- コメントは古くなる。
- 反論: コメントが古くなる程のコードの構造変更は時間がかかる。適切なレビューにより解決できる。
コメントを書く利点は、cognitive load を減らすことと、unknown unknown を減らすことにある。
In his book Clean Code, Robert Martin takes a more negative view of comments: ... comments are, at best, a necessary evil. If our programming languages were expressive enough, or if we had the talent to subtly wield those languages to express our intent, we would not need comments very much — perhaps not at all.
ここは意外でした。
適切なコメントは必要なものだとされているので。
もしかしたら、docstring はコメントとは別に考えている可能性もあるので、前後の文脈がわからないと判断出来ませんが。
13 章
コメントで何を記述するべきか? モジュール利用者がコードを読まなくて良くなることが利点。
コメントというよりドキュメンテーションに関する記述に感じました。
慣習があるならそれに従うと、コメントを読みやすく、書きやすくなる。
コメントするべきは、インターフェース,データ構造, 実装時のコメント, 依存するモジュール ... 依存するモジュールが存在するのは設計として良くないと思ったら、すぐ下に、
Cross-module comments are the most rare of all and they are problematic to write,
とありました。
コメントが、コード(全体ではなくて、コメント横のもの)を初めて読んだ人が書けるものであれば不要。
コメントが、説明する関数やクラスと全く同じ単語で書かれているならば情報が増えないので不要。 変えるべき。
低レベルについての説明。
より詳細な説明。none と何か? 前提条件は? など
また、変数は動詞的ではなくて、名詞的に考えると良い。 (... つまり変数がどう作られるか?ではなくて、何を意味するか?という感じ?)
高レベルの説明 コード全体の意図を説明する。
コード・実装は、具体的な詳細なので、抽象はコメントに記述する。 インターフェースコメントは抽象。 実装コメントは、抽象の実現のための内部動作を記述する。 インターフェースコメントに具体的な内部動作(実装コメント)を記述してはいけない。
実装コメントは、how ではなく、what, why を説明する。 変数はどのように操作されるか?ではなくて、変数が何を意味するか?を説明する。
複数のモジュールが関わるコメントはどこに配置するか?サーバーの状態など ... design Note に配置するのが次善策
14 章
命名について
- 複数の異なる事柄を説明し得る曖昧な名称は避ける
- 命名が難しいなら設計が悪い可能性もある
- 一貫した名称を用いる。たとえば、block という名称で、ある場所ではファイルブロック、ある場所では、記憶媒体上の物理的なブロックを表すのは良くない
- 情報を追加しない単語を追加するべきではない。たとえばファイルクラスで、ファイルブロックという要素を持つべきではない。ブロックで十分。 ただし、ブロックという単語が、ファイルブロック以外の意味も持つならば、ファイルブロックとするのもあり。
- 記述しやすいではなくて、読んで意図が理解しやすい名称が良い。
- 寿命がないが変数は長い名称
15 章
コメントは作業の最初に記述する 説明的な長いコメントが必要であれば、デザインに失敗している可能性が高い
16 章
- コードを修正するとき、最小の変更で機能を実装しようとして場当たり的な実装をしてはならない
- コメントをコードの近くに配置しておけば、コードの修正とともにコメントも修正される ことが期待できる
- 修正意図はコミットメッセージではなくてコードにコメントする
- IDEの補助があるので、コメントはコードの近くにあった方が有用 同じコメントを複数箇所に散らばせる必要はないしそうするべきではない
- 抽象度の高いコメントはメンテナンスが容易
17 章
コードの一貫性は、コードの理解を容易にし、ミスを減らすのに有効である。
ではどのように一貫性を保ち、保たせるか?
そもそも一貫性は、コードスタイル、命名、インターフェース、デザインパターンなど
一貫性を保たせるには?
- ドキュメント二の子明日。 これは目につくところでなければならない
- チームの慣習に慣れるまでコードレビュー
一貫性を保つには
- 自分が記述しようとするコードの周りを見て、慣習に沿わせること。
18 章
コードを明瞭に保つ。
明瞭とは、コードを初めて読んだ人が苦労せずに理解できること。 コードを書いた人がコードを理解しやすいことではない。
- 空白を使う。 python の docstring などはいい例。
- event-driven programing は難しい。
- generic container ... つまり、named tuple などを使った方が理解しやすいということですね。
- 一般的なしきたりに従う。 極端にはエントリーポイントの名前を main 以外にすると分かりにくくなる。
19 章
デザインに影響を与えそうな諸々
- Object Oriented Programming には、実装とインターフェースの2種類の継承がある。インターフェースの継承は良いが、実装の継承は、コードの重複を減らす代わりに依存関係が増える。 -> 最近ではDIを検討する。
- agile は、デザインは継続的に行うべきであるという考えと合致するが、 tactical programmingにならないように注意が必要
- unittest は、デザインの検討時の堅牢性を保持してくれる
- テスト駆動開発は、そのテストを通すためだけの実装になり、一般性を持ったデザインを考えることができない可能性がある
20 章
パフォーマンスの問題に対する対処
- IOやネットは遅い、hash talbe より ordered map の方が遅いなど一般的な知識で本質的によい設計を選ぶこと
- 本当に必要になるまでは簡潔な方法を選ぶが、どうしてもパフォーマンスのために、簡潔さを諦める必要がある場合は
- まず、勘に頼らずにどこが遅いのか?を測定してはっきりさせる。
- 条件分岐やコールがないコードは早い。
- キャッシュ、アルゴリズムの変更など根本的な変更が理想的 (fundamental change)
- もっとも高頻度で実行されるケースを切り出して、高頻度のケースがうまく回るようにデータの持ち方などを設計する. この special caseを可能な限りシンプルにする
21 章
重要な点と重要でない点を分けること
- レバレッジの効果を見ると何が重要かがわかる。 インターフェースを変更して、二つのものを片付けた、6章の text editor 例
- 重要な点がわからなければ仮定を立てて進めてみて、仮定が合っていても間違っていても、理由を確認して将来の判断に役立つようにする
- 重要なことに気がつかないミスは、再実装などにより、 unknown unknown になる可能性がある
- なんでも重要にしてしまうミスは、shallow class などにつながる
- 重要なことは最小化する。重要でないことはinterface に露出しない
- 重要なことはドキュメントや変数名など目に入る場所に配置する
- 重要なことにフォーカスするのはソフトウェアデザイン以外の文脈でも大事
22 章
まとめ
良いデザインは慣れるまでは余計に時間がかかるように感じるが、習得すればそんなに大きな時間はかからない。
良いデザインを採用すればデバッグ作業は減り、仕事の中のデザインに割く割合を大きくできる。
単語
- exacerbate: 病気などを悪化させる
- destribute: 分散処理
- awkward: へた ぎこちない
- irrelevant: 関係のない
- adequately: 十分に
- obscurity: 不明瞭
- advocate: 主張する
- intuition: 直感
- preserve: 保つ
- clues: 手がかり, ヒント
- contemplate: 熟考する
- consequences: 結果
- concurrent: 同時に起こる
- relevant: 関連のある
- inevitaly: 必然的に
- deduce: 推測する
- mediocre: 平凡な
- convey: 伝える
- sentinel: 見張り, 監視
- violate: 違反する
- erroneous: 誤った, 間違った
- obscure: 不明瞭な
- investment: 投資
- clutter: 散らかっている状態
- assumptions: 仮定
- predicates: 述語
- pull off: 成し遂げる
- decomposition: 分解
- rationalize: 合理化する
- conceive: 考え出す
- recoup: 回収する
- invalidate: 無効にする
- stale: 古くなった
- tedious: 退屈な
- conventions: 慣習
- unintentionally: 意図せずに
- violate: 違反する
- conspicuous: 顕著な
- adage: 格言
- urge: 強く促す
- obscurity: 不明瞭なこと
- error-prone: エラーが起こりやすい
- likelihood: 可能性
- judicious: 賢明な
- compensate: 補う
- obscurity: 不明瞭なこと
- worthwhile: 価値のある
- constraints: 制約
- prominence: 顕著さ
- deciding: 決定すること
- irrelevant: 関係のない
- impedes: 邪魔する
- downside: 不利な点
- getting in the way of: 邪魔になる
- honing: 磨くこと
- brittle: 壊れやすい