勉強日記

チラ裏

A Philosophy of Software Design ch5. Information Hiding (and Leakage)

www.goodreads.com


Information Hiding (and Leakage)

  • ch4.で述べたこと: モジュールは深くあるべき
  • 本章: どう作るの

Information hiding

  • 知識の隠蔽
    • 深いモジュールを作る上で最重要テクニック
    • in 1972 David Parnas
  • 実装のhowが隠蔽される
    • データ構造やアルゴリズム
      • B木にデータを格納する方法、効率的なアクセス方法
      • ファイルの論理ブロックとディスクの物理ブロックとの対応づけ
      • TCPの実装
      • マルチコアプロセッサのスケジューリング
      • JSONドキュメントのパース
    • 情報の水準はさまざま
      • 低水準
        • 例: ページサイズ
      • 高水準
        • 例: 「ほとんどのファイルのサイズが小さい」という仮定
  • 2つの点で複雑性を減らす
    • インタフェースが単純になる
      • 重要でない詳細が削ぎ落とされ、開発者の「認知の負荷」が下がる
        • 例: B木
          • 木の平衡の保ち方などを知る必要がない
    • システムの拡張が容易になる
      • 隠蔽された知識は他のモジュールとの依存関係をもたない
      • ので、独立して変更を加えることができる
  • モジュールを設計する際は、なんの知識を閉じ込めるかよく考えよ
    • 隠蔽すればするほど、インタフェースは単純になる
    • つまり、モジュールは「深く」なる
    • 【補】利用者にとって重要な知識は隠蔽してはいけない(ch.6)
  • 知識の隠蔽はゼロ百ではない、部分的でも価値がある
    • クラスの知識が全クライアントコードに暴露されているよりは、一部のクラスにのみ公開されているほうがマシ
      • 知識を必要とするクラス向けのメソッドを用意する
        • 【補】Repository向けのsetterとかはこれ?

Information leakage

  • 隠蔽できてないやつ
  • 設計(how)の変更があちこちに跳ねる
    • ある知識が複数のモジュールに反映されている
      • 重複
      • ひとかたまりであるべきものが分散している(Back-door leakage)
        • 例: データフォーマットのread/writeが別々のモジュールに分かれている
        • ひと目でわからないので厄介
    • 知識がインタフェースに漏れている
  • 知識の漏出はソフトウェア設計上、最重要危険信号
    • 知識の漏出に敏感であることは最重要スキル
  • 知識の漏出に出くわしたら自問自答せよ:
    • 「これらのクラスを再構成して、この知識が1つのクラスにしか影響を与えないようにするには?」
    • クラス群が小さく、漏出した知識と密に紐付いているなら、1つのクラスにまとめることを検討する
    • あるいは、知識を別クラスに切り出して委譲する
      • 切り出した新しいクラスのインタフェースに知識が漏出していたら意味がないことに注意する
        • Back-door leakageがインタフェース越しの(普通の)漏出になったたけ

Temporal decomposition

  • ソフトウェア構造がタスク発生順に対応している
  • 時間分割してしまっている例
    • クラス群
      1. HogeReader
      2. HogeModifier
      3. HogeWriter
    • Hogeフォーマットについての知識がReaderとWriterに散らかっている
      • Back-door leakage
    • まとめよう
  • タスクの発生順ではなく、各タスクが必要とする知識に着目する
    • 【補】この場合、Hogeフォーマットの読み書き

Example: HTTP server

  • HTTPとはなんぞや(略)
  • Request/Responseの話

Example: Too many classes

  • やりがちなミス: 時間分割
    1. HTTPリクエストを読み、文字列を返すクラス
      • 【補】Receiverとする
    2. 文字列をパースするクラス
      • 【補】Parserとする
  • パースの知識のBack-door leakage
    • ReceiverとParser両クラスで文字列パースの知識が必要になる
      • HTTPリクエストというものは、ある程度パースしないと読めないもの
      • 例えば、Content-Lengthヘッダを読まないと、リクエストボディの長さがわからない
    • 呼び出しコードに複雑性を強いる
      • Receiver, Parserのメソッドを特定の順番で呼ばせる
        • 【補】認知の負荷
  • 知識の隠蔽は、クラスを少し大きくすることで改善することが多い
    • 関連機能がまとまる
    • インタフェース単純化
      • 【補】クラス減る、メソッド減る
      • 深いモジュールにつながる
  • かといってアプリケーション全体を1クラスにしていいわけもない
  • いつクラスを分割すべきかについてはch9にて論ずる

Example: HTTP parameter handling

  • クエリパラメータをパースする部分は誰もがカプセル化するところ
    • &=区切りで連想配列にパース
    • +を半角スペースに
  • やりがち: こんな浅いメソッドを生やして、内部表現を暴露してしまう
public Map<String, String> getParams() {
    return this.params;
}
  • こうあるべき
public String getParameter(String name) {...}
public int getIntParameter(String name) {...}
  • 「内部表現がMap<String, String>である」という知識を隠蔽した
  • getIntParameterはさらに下記メカニズムを隠蔽
    • 文字列から整数への変換
    • 変換失敗時の例外送出

Example: defaults in HTTP responses

  • やりがちな間違い: デフォルト値を設定すべきものに設定しない
      • HTTPレスポンスのHTTPバージョン
        • リクエストのものと一致するべき
      • DATEヘッダ
    • モジュール利用者が設定するのは、知識の漏出にあたる
  • 「デフォルト値」は、知識の部分的な隠蔽の例
    • 普遍的なケースが可能な限り単純になるように設計する、という原則にしたがっている
    • 利用者は、普遍的なケースにおいて、デフォルトが設定された項目の存在について知らない
    • レアケースでは
      • デフォルト値をオーバライドする
      • デフォルトのふるまいを変えるための特別なメソッドを起動する
        • 【補】UnixのファイルIOのlseek()的なやつ
  • クラスはよしなに「正しいことをする」べき
    • デフォルト値はこの一種
    • JavaのファイルI/Oの姿勢はこれに反する
      • ファイルI/Oにおけるバッファリングは誰もが欲する機能
      • わざわざ指定しなくても有効になってほしい
      • 利用者は存在すら知らないのが望ましい

Red Flag: Overexposure

  • 普遍的な機能を利用するために、めったに利用されない機能についても学ばなければならない
  • 認知の負荷の増大

Information hiding within a class

  • 知識の隠蔽は、なにもシステムに対するクラスだけの話ではない
    • クラスの中に知識をカプセル化し、システムの残りの部分から隠蔽する
  • クラスに対するメソッド
    • privateメソッドの中に知識をカプセル化し、クラスの残りの部分から隠蔽する
  • インスタンス変数の利用箇所を減らせ
    • 依存を排除し、複雑性を減らす

Taking it too far

  • 隠蔽していいのは、「モジュール外で必要とされない知識」
  • モジュール外で必要とされる知識は隠蔽してはいけない
  • 例: モジュールのパフォーマンスチューニング用コンフィグ
  • モジュール外で必要な情報は最小化せよ
    • 設定値を自動調整できるなら、そのほうが設定項目を公開するよりも良い
    • どの知識がモジュール外で必要か認識し、それを確実に公開することが重要

Conclusion

  • 知識の隠蔽と深いモジュールとは深く関連している
  • 知識を隠蔽するほど…
    • モジュールが提供する機能は増える
    • インタフェースは小さくなる
    • つまり、モジュールは深くなる
  • 逆もまた然り

英語

  • pernicious
    • 有害な、悪質な