A Philosophy of Software Design ch13 Comments Should Describe Things That Aren't Obvious From The Code

APoSDソフトウェア設計勉強メモ

出典: 


Comments Should Describe Things That Aren’t Obvious From The Code

  • コメントを書く理由

    • プログラムコードには、開発者が念頭においていた重要な情報全てを込めることはできない
    • コメントで残しておき、将来簡単にコードを理解・変更できるようにする
  • コメントには(隣の)コードから明白でないことを書け

    • コードよりも低水準の詳細
    • なぜそのコードが必要なのか
    • 事前条件

      • 「必ずbの前にaを呼べ」
  • コードをしらみつぶしに読めば推測できるが、苦痛で間違いも起きやすい
  • コメントを書く最も重要な理由は、抽象化

    • コードは詳細すぎて、読んでも抽象は見えづらい
    • コメントはより高水準の目線を提供する

      • 例: 「このメソッドの実行後は、ネットワークのトラフィックはmaxBandwidth bpsになる」
    • コードから推測できるにせよ、モジュールの利用者にそれを強制したくはない

      • 時間がかかる
      • 多くのことを考えなければならない
    • 開発者は、外部公開された宣言以外のコードを読むことなく、モジュールの利用法(抽象化)を理解できるべき

      • これを達成する唯一の方法は、宣言にコメントを添えること
  • 本章のテーマ

    • コメントには何が記述されるべきか
    • 良いコメントを書く方法
  • 良いコメントは、コードとは異なる詳細度である

    • コードよりも詳細
    • コードよりも抽象的

Pick conventions

  • コメントの規約を決める

    • 何を書くか

      • 後述
    • フォーマット

      • 使用している言語でドキュメント自動生成用のものがあればそれを

        • java: Javadoc
        • C/C++: Doxygen
        • Go: godoc
      • なければ何かを真似る

          • 他の言語のもの
          • 他のプロジェクトのもの
        • 他開発者が理解を示し、従ってくれやすい
  • フォーマットを決める理由

    • 一貫性

      • 読みやすく理解しやすい
    • 書きやすくもなる

      • 何をどうコメントすればよいかの枠組み

        • 【補】PHPDocの@paramとか@returnとか
  • コメントの分類

    • インタフェース

      • モジュール直上のコメント

        • クラス
        • データ構造
        • 関数
        • メソッド
      • モジュールのインタフェース

        • クラス

          • 全体的な抽象化
        • 関数やメソッド

          • 全体的な振る舞い
          • 引数
          • 戻り値
          • (あれば)副作用
          • (あれば)例外
          • 事前条件
    • メンバのデータ構造

      • インスタンスメンバ変数
      • クラスメンバ変数
    • 実装

      • メソッドや関数内のコードに添える
    • モジュール横断

      • モジュール間の依存についてのコメント
  • はじめの2つが最重要
  • 実装コメントは必要ないこと多し
  • モジュール横断的なコメントは最もまれだが…

    • 書くうえで厄介
    • 必要な場合はとても重要

Don’t repeat the code

  • コードのオウム返しのコメントはなんの付加価値もない

    • 「コメントは無価値」と考える開発者もいる理由がこれ
  • 説明しようとしているものとは異なる単語で書け
/**
 * The horizontal padding of each line in the text.
 */
private static final int textHorizontalPading = 4;
  • 問題点

    • そもそもパディングって何?
    • パディングはどこにつくの?
    • パディングの単位は?
/**
 * The amount of blank space to leave on  the left and
 * right sides of each line of text, in pixels.
 */
private static final int textHorizontalPading = 4;
  • 改善した点

    • パディングなるものの説明
    • パディングがつく場所の明示
    • パディングの単位の明示

Lower-level comments add precision

  • コメントは、コードと異なる詳細度の知識でコードを補強する

    • コードよりも低水準で詳細なコメントは、精度を加える
    • コードよりも高水準で抽象的なコメントは、直感性を提供する
  • コードと同じ詳細度のコメントは、コードのオウム返しになりがち
  • 本節では「コードよりも低水準のコメント」について述べる
  • コードよりも低水準のコメントが有用なのは:

    • メンバ変数
    • メソッド引数
    • メソッド戻り値
  • 変数宣言中の名前と型は精度が不十分であることが多い

    • 【補】PHPでは配列の要素型を表現できなかったりする
  • コメントが補いうる情報

    • 変数の単位
    • 境界を含むか含まないか

      • 【補】ある程度は名前の規約で表せる

        • endは含まない
        • lastは含む
    • nullの意味
    • リソースの解放は誰の責任か
    • 不変条件

      • 例: 「コレクションの要素数は最低でも1」
  • コードをしらみつぶしに読めば理解できるが、時間がかかるし間違いも起きやすい
// Contains all line-widths inside the document and
// number of appearances
private TreeMap<Integer, Integer> lineWidth
  • 問題点

    • key/value どちらが行の長さでどちらが度数か分からない
    • 行の長さの単位がわからない

      • 画素数
      • 文字数
// Holds statistics about line lengths of the form <length, count>
// where length is the number of characters in a line 
// (including the newline), and count is the number of lines with
// exactly that many characters. If there are no line with
// a particular length, then there is no entry for that length.
private TreeMap<Integer, Integer> numLinesWithLength;
  • 改善した点

    • 文字列長 => 度数 なる度数表であることがわかる
    • 「width」ではなく「length」という語を採用したことで、単位が「文字数」であることが伝わりやすくなった
    • エントリがないことの意味の説明が追加された

Higher-level comments enhance intuition

  • 本節では「コードよりも高水準のコメント」について述べる
  • 直感的なわかりやすさを提供する
  • (長いわりに大したこと言ってないので省略)
  • 高水準なコメントは、低水準なコメントよりも書くのが難しい

    • 自問せよ:

      • このコードは何をしようとしているか?
      • コードのすべてを説明するもっとも単純なものは?
      • このコードで最も重要なことは?
  • whatのほか、whyを書く

    • なぜそのコードが実行されるのか
    • どういう条件下でそれが実行されるのか書くと有用

      • 異常系でのみ実行される場合とくに

Interface documentation

  • コードは詳細すぎて、インタフェースに含めるべきでない情報を含んでしまう
  • よい抽象化のためにはコメント必須
  • インタフェースコメントと実装コメントとを分離する

    • インタフェースコメントが実装についても述べているならば、そのクラスもしくはメソッドは浅いということ
  • 書くべき内容

    • 呼び出し元から見た振る舞い

      • 冒頭1-2文程度
    • 引数と戻り値

      • これは低水準のコメントであること
      • 引数の制約条件についても記述する

        • 引数間の依存関係等
    • 副作用
    • 例外
    • 事前条件

      • 例: 2分探索に渡すコレクションはソート済でなければならない
    • 制限

      • マルチスレッドで使えない旨など
      • 利用者にとって、このモジュールを利用するか否かの判断材料になる
  • 書くべきでない内容

    • モジュールを利用するにあたり知る必要のない実装の詳細
    • private定数の設定

      • モジュール実装者向けコメントであって、モジュール利用者向けではない
    • 内部で握りつぶす例外フロー

      • 【補】define errors out of existence にも通ずる話
  • (同じことをずっと書いてる)

Implementation comments: what and why, not how

  • コードが何をしているかの理解を助ける

    • どのようにしているか、ではなく
    • 何をしているかわかれば、どのようにしているかも追いやすくなる
  • 1つのことだけをする短いメソッドでは不要

    • インタフェースコメントにすでに書いてあるはず
  • 行っていることが複数フェーズに別れるような場合に書く

    • 【補】テストスイートで 準備、アクション、アサート、後始末各フェーズにコメントを書く
  • 長いメソッドでは、重要な局所変数にコメントを書くと有用なことがある

    • たいてい良い名前をつければ不要ではある
    • その変数が何を表すかを書く

      • どう処理されるか、ではなく

Cross-module design decisions

  • 理想的には、重要な設計上の意思決定は1クラスの中にカプセル化されているべき
  • 実際のシステムでは、設計上の意思決定が複数のクラスにまたがって影響することは避けられない

    • 例: ネットワークプロトコルの送信側クラスと受信側クラス
    • 良いドキュメンテーションをすることが肝心

      • 複雑で捉えづらいことが多い
      • したがって、バグの原因となりやすい
  • どこに書くか問題

    • 開発者が自然に見てくれそうな場所は?
  • 良い場所があればそこに

    • 例: enum

      • 「このenumを拡張するときは、下記の箇所に変更を加えてください: … (利用側モジュールが並ぶ)」
    • 【所感】双方向依存では?

      • designNoteに単方向依存のほうが良さそうに見える
  • 良い場所がなければ…

    • 著者も模索中の方法: designNoteファイルに集約する

      • コードコメントでは、designNoteファイルを参照する
      • 欠点: コードから遠く離れてしまう

Conclusion

  • コメントの目的は、システムの構造とふるまいを読み手に明瞭に伝えること

    • 必要な情報を素早く得られる
    • 変更が正しいことの確信を得られる
  • コードから明白にわかる情報もあるが、多くはコードから簡単には推測できない
  • 隣のコードから明白にわかることをコメントに書かない
  • 「明白」というのは読み手の視点

    • コメントを書くときには、読み手の立場に立って、必要とする重要な事実は何か自問せよ
    • 不明瞭な点をレビューで指摘されても、反論することなかれ
    • 何が混乱のもとかを理解し、コメントかコードを改善して明瞭にできないか検討せよ

Answers to questions on page 113

  • 実装の詳細は省く
  • 利用者が知っている必要のある仕様は書く
  • パフォーマンスに関することは必要かも

    • 【補】そのモジュールを利用するかどうかの判断材料となる

英語

  • distine

    • スケジュールする
  • contemplate

    • 熟考する
  • emanate

    • 発する

      • 例外送出の文脈にて