A Philosophy of Software Design の論点メモと感想

本記事は書籍「A Philosophy of Software Design」の論点をまとめたメモです。内容の詳細や結論までは含みません。論点について興味ある方は是非書籍をお読みください。英語ですが170ページほどで比較的読みやすい文体で書かれています。

A Philosophy of Software Design

A Philosophy of Software Design

感想

スタンフォード大学でソフトウェア設計を教える筆者が、授業の経験を基に執筆した本である。複雑さをいかに抑えるかという一貫した観点からソフトウェア設計のあるべき姿を議論しており、丁寧に提示される例によってそれぞれの主張に納得・共感できる。いかにしてソフトウェアの複雑さに立ち向かうかについて議論する書籍はこれまでに何冊か読んできたが、複雑さ自体についてじっくりと考え理解を深めるために本書は有用であった。主張自体はリーダブルコードなどと重複するような常識的なものも多いが、なぜその設計が良い/悪いのかを複雑さの観点から見つめなおすことは有意義だった。本書で提示されるRed Flag(危険信号)と設計原則のうち重要なものは巻末の一覧表にまとめられており、コードレビューの観点として有用だろう。他の書籍の主張とぶつかるように見える箇所もあるが、それぞれの言わんとしていることをよく考えることが重要である。

第1章 はじめに(結局、「複雑さ」が問題だ)

ソフトウェアを人が開発する以上、人の限られた理解力で無理なく理解できる必要があり、したがってソフトウェアの複雑さをいかにして抑えるかがソフトウェア設計において最も重要である。2つ方法がある。

  • 複雑さを取り除きシンプルで明確なものにする
  • 複雑さをカプセル化することで1度に理解しなければならない複雑さを限定する

第2章 複雑さの性質

複雑さとは、ソフトウェアシステムを理解しづらくまた変更しづらくする構造のことである。複雑さは以下の形で現れる。

  • 変更の増幅 (Change amplification):少しの変更のはずが大量の変更が必要になってしまうこと
  • 認知能力への負荷 (Cognitive load):変更するために開発者が一度に多くの情報を理解しなければならないこと
  • 何を知らないのか分からない (Unknown unknown):変更対象が分からなかったり、変更に際して何を考慮しなければならないのかがわからないこと

第3章 動くだけでは不十分

目先の変更量を最小にする設計ではなく、持続可能な設計をすべきだ。

第4章 「深い」モジュール

レイヤのイメージで、モジュールが提供する機能を四角形で表し他のレイヤから見えるインターフェースを上辺とする。

  • 深いモジュール:簡単・シンプルなインターフェースで多くの機能を提供する縦長の四角
  • 浅いモジュール:提供する機能のわりにインターフェースが難しい・複雑な横長の四角

例えば、盲目的にクラスを小さくして浅いモジュールが大量にある状態になると、クラス単体で見て理解しやすくともシステム全体では複雑になる。

第5章 情報隠蔽と漏洩

あるモジュールを使う際にそれとは別のモジュールについて知らなければならないとしたら、それはモジュールとして適切に情報隠蔽できていない。深いモジュールにするためには適切に情報隠蔽できている必要がある。

第6章 汎用モジュールは深い

ある機能を設計する際に汎用モジュールとして設計できれば、それは深いモジュールにつながる。特化モジュールは情報漏洩しがちである。

第7章 レイヤが違えば抽象度も違う

あらゆるコードはシステムの複雑さを増加させる。モジュールのインターフェースはその提供する機能を抽象化することでシステム全体の複雑さをできるだけ増加させないことが望ましい。

第8章 複雑さを減らす

モジュールにとって、実装のシンプルさよりもインターフェースがシンプルであることが重要である。

第9章 一緒がいいのか別がいいのか

ある機能の2つの部分があるとして、それらを1か所に実装すべきか分けるべきかというよくある問題について、複雑さの観点で考える。

1か所に実装すべきケース

  • 情報(前提知識)が共有されている場合
  • インターフェースをシンプルにできる場合
  • 重複コードを排除できる場合

分けて実装すべきケース

  • 汎用コードと特化コードに分けられる場合

「1メソッドは20行まで」ルールなどによりメソッドを細かく分割するのは複雑さを増加させる。

第10章 エラーを排除する

例外ハンドリングはかなり複雑な問題である。必要な例外ハンドリングを減らすための方策を紹介する。

  • 例外を発生させないインターフェース設計
  • 例外のマスキング(内部で処理してインターフェースに出さない)
  • 例外ハンドリングの集約*1
  • ハンドリングせずクラッシュさせる

第11章 2度設計する

最初に思いついた設計で突き進むのではなく別の設計も検討し、それぞれのメリット・デメリットを検討する習慣をつけるのが良い。

第12章 コメントを書かない4つの言い訳

  • コード自体がドキュメントになっているのが良いコードだ
  • コメントを書く時間がない
  • コメントは古くなり間違いを生む
  • 今まで見た全てのコメントは価値がなかった

これらの4つの言い訳はコメントを書かない理由とはならないことを説明する。コメントはコードに現れない開発者の思考を残すために書くべきであり、それにより認知能力への負荷何を知らないのかわからない問題を改善できる。

第13章 コメントではコードから明白ではないことを記述する

コメントは以下のように分類できる。それぞれについて含むべき情報とそこに書くべきではない情報を説明する。

  • インターフェースについて(クラス・メソッドなどの直前に書かれる)
  • データメンバについて(メンバ変数の直前に書かれる)
  • 実装について(メソッド内に書かれる)
  • モジュール間の依存関係について
  • 低レベルのコメント
  • 高レベルのコメント

第14章 名付け

最初に思いついた名前よりも良い名前がないか考える時間を割く価値がある。名前は以下の性質を満たすべきだ。

  • 正確性:読み手が名前から何を指しているのかすぐに正しく理解できる
  • 一貫性:複数の箇所で変数が同じものを指す場合は同じ変数名にし、逆に同じ名前で別のものを指すことがないようにする

第15章 コメントファースト

初めにインターフェースコメントを書いてから実装する手法を紹介する。以下の利点がある。

  • より良いコメントを残すことができる
  • 適切に抽象化できているかどうかが分かり、深いモジュールの設計に役立つ
  • コメントを書くのを楽しめる

第16章 既存コードの変更

ここまでの議論は既存コードを変更する際にも当てはめられる。現実の制約から難しい場合もあるだろうが、理想的ではなくとも少しでも複雑さを減らす方策を検討すべきだ。

既存コードにおいてはコメント(ドキュメント)のメンテナンスが疎かになる可能性がある。そうならないための方策を紹介する。

第17章 一貫性

同類の機能は一貫した設計・実装で、そうでない機能は別と分かるようにすることで複雑さを抑えることができる。例えば以下について一貫性が保たれるよう留意する。そのための方策も紹介する。

  • 名前
  • コーディングスタイル
  • インターフェースの複数の実装
  • デザインパターン
  • 不変条件

第18章 コードは明白であるべき

明白であるかどうかは読み手視点であるので、コードレビューで議論されるのが良い。

これまでの章で議論した以外の、コードを明白にするテクニックをいくつか紹介する。

第19章 ソフトウェアトレンド

以下の各テーマが複雑さの観点でどう影響するのか議論する。

特に、アジャイル開発でインクリメンタルに機能開発すると目先の機能開発を優先してしまいがちなことに警鐘を鳴らしている。

第20章 パフォーマンスのための設計

多くの場合、シンプルな設計はパフォーマンスにも優れている。パフォーマンスが問題になる場合その原因は複雑さにあることが多く、クリティカルパスをシンプルにすることで改善できないか検討するのが良い。

*1:本には書かれていないが、C#では例外自体をラッピングして集約するのも有効